Merge pull request '## 13.0.37 - L'urgence d'Illisys' (#804) from feature/v13-corrections into v13

Reviewed-on: https, #804
This commit was merged in pull request #804.
This commit is contained in:
2026-05-05 23:54:19 +02:00
21 changed files with 153 additions and 63 deletions

View File

@@ -1,5 +1,14 @@
# 13.0
## 13.0.37 - L'urgence d'Illisys
- Corrections v14
- on peut utiliser les commandes foundry dans le tchat (par exemple, /roll 1d6)
- les dés customisés Dice-So-Nice (heure, rencontres, dé draconique) fonctionnent
- Le Haut-rêve est de nouveau proposé dans les options d'initiative
- Nouvelle règle optionnelle: choisir si seules les armes équipées sont proposées en combat
- L'appel à la chance utilise la nouvelle fenêtre de jet de dés
## 13.0.37 - Le bonheur des zyglutes d'Illisys
- Corrections v14

View File

@@ -47,11 +47,7 @@ import { RdDRollResult } from "./rdd-roll-result.js";
import { RdDInitiative } from "./initiative.mjs";
import RollDialog from "./roll/roll-dialog.mjs";
import { OptionsAvancees, ROLL_DIALOG_V2 } from "./settings/options-avancees.js";
import { ROLL_TYPE_JEU, ROLL_TYPE_MEDITATION, ROLL_TYPE_SORT } from "./roll/roll-constants.mjs";
import { PART_TACHE } from "./roll/roll-part-tache.mjs";
import { PART_COMP } from "./roll/roll-part-comp.mjs";
import { PART_OEUVRE } from "./roll/roll-part-oeuvre.mjs";
import { PART_CUISINE } from "./roll/roll-part-cuisine.mjs";
import { ROLL_TYPE_COMP, ROLL_TYPE_CUISINE, ROLL_TYPE_JEU, ROLL_TYPE_MEDITATION, ROLL_TYPE_OEUVRE, ROLL_TYPE_SORT, ROLL_TYPE_APPEL_CHANCE, ROLL_TYPE_TACHE } from "./roll/roll-constants.mjs";
import { RdDPossessionV2 } from "./rdd-possession-v2.mjs";
import { Apprecier, MORAL, SITUATION_MORAL } from "./moral/apprecier.mjs";
import { Distance } from "./combat/distance.mjs";
@@ -140,13 +136,14 @@ export class RdDActor extends RdDBaseActorSang {
.reduce(Misc.sum(), 0);
}
listActions({ isAttaque = false, isEquipe = false }) {
listActions() {
// Recupération des attaques
const actions = this.listActionsAttaque()
.filter(it => !isEquipe || it.arme.system.equipe)
const actions = ReglesOptionnelles.isUsing('armes-equipees')
? this.listActionsAttaque().filter(action => action.equipe)
: this.listActionsAttaque()
if (!isAttaque && this.system.attributs.hautrevant.value) {
actions.push({ label: "Draconic", action: 'haut-reve', initOnly: true })
if (this.system.attributs.hautrevant.value) {
actions.push({ label: "Draconic", action: 'haut-reve', initOnly: true, equipe: true })
}
return actions
}
@@ -1876,7 +1873,7 @@ export class RdDActor extends RdDBaseActorSang {
if (OptionsAvancees.isUsing(ROLL_DIALOG_V2)) {
const rollData = {
ids: { actorId: this.id },
type: { allowed: [PART_COMP], current: PART_COMP },
type: { allowed: [ROLL_TYPE_COMP], current: ROLL_TYPE_COMP },
selected: {
carac: { key: caracName },
comp: { key: compName, forced: options.forced },
@@ -1915,7 +1912,7 @@ export class RdDActor extends RdDBaseActorSang {
const rollData = {
ids: { actorId: this.id },
selected: { tache: { key: tache.id, forced: options.forced } },
type: { allowed: [PART_TACHE], current: PART_TACHE }
type: { allowed: [ROLL_TYPE_TACHE], current: ROLL_TYPE_TACHE }
}
return await RollDialog.create(rollData, options)
}
@@ -2128,18 +2125,22 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
async rollAppelChance(onSuccess = () => { }, onEchec = () => { }) {
await this.openRollDialog({
name: 'appelChance',
label: 'Appel à la chance',
template: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-carac.hbs',
rollData: { selectedCarac: this.getCaracByName('chance-actuelle'), surprise: '' },
callbacks: [{ action: r => this.$appelChanceResult(r, onSuccess, onEchec) }]
});
return await RollDialog.create(
{
ids: { actorId: this.id },
type: { allowed: [ROLL_TYPE_APPEL_CHANCE], current: ROLL_TYPE_APPEL_CHANCE },
},
{
onRollDone: (dialog, roll) => RollDialog.onRollDoneClose(dialog, roll),
callbacks: [
async roll => await this.$appelChanceResult(roll, onSuccess, onEchec)
]
})
}
/* -------------------------------------------- */
async $appelChanceResult(rollData, onSuccess, onEchec) {
await RdDRollResult.displayRollData(rollData, this, 'chat-resultat-appelchance.hbs')
if (rollData.rolled.isSuccess) {
await this.setFlag(SYSTEM_RDD, 'utilisationChance', true);
await this.chanceActuelleIncDec(-1);
@@ -2474,10 +2475,10 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
async equiperObjet(item) {
if (item?.isEquipable()) {
const isEquipe = !item.system.equipe;
await item.update({ "system.equipe": isEquipe })
const newEquipe = !item.system.equipe;
await item.update({ "system.equipe": newEquipe })
this.computeEncTotal()
if (isEquipe)
if (newEquipe)
await this.verifierForceMin(item)
}
}
@@ -3014,7 +3015,7 @@ export class RdDActor extends RdDBaseActorSang {
const rollData = {
ids: { actorId: this.id },
selected: { oeuvre: { key: oeuvre.id } },
type: { allowed: [PART_OEUVRE], current: PART_OEUVRE, },
type: { allowed: [ROLL_TYPE_OEUVRE], current: ROLL_TYPE_OEUVRE, },
}
return await RollDialog.create(rollData)
}
@@ -3116,7 +3117,7 @@ export class RdDActor extends RdDBaseActorSang {
if (OptionsAvancees.isUsing(ROLL_DIALOG_V2)) {
const rollData = {
ids: { actorId: this.id },
type: { allowed: [PART_CUISINE], current: PART_CUISINE },
type: { allowed: [ROLL_TYPE_CUISINE], current: ROLL_TYPE_CUISINE },
selected: {
cuisine: { key: recette.id }
}
@@ -3166,7 +3167,7 @@ export class RdDActor extends RdDBaseActorSang {
if (item.getUtilisationCuisine() == 'brut' && OptionsAvancees.isUsing(ROLL_DIALOG_V2)) {
const rollData = {
ids: { actorId: this.id },
type: { allowed: [PART_CUISINE], current: PART_CUISINE },
type: { allowed: [ROLL_TYPE_CUISINE], current: ROLL_TYPE_CUISINE },
selected: {
cuisine: { key: item.id }
}

View File

@@ -26,9 +26,8 @@ import { BASE_CORPS_A_CORPS, BASE_ESQUIVE, CATEGORIES_COMPETENCES_CREATURES } fr
import { RollDataAjustements } from "../rolldata-ajustements-v1.js";
import { MappingCreatureArme } from "../item/mapping-creature-arme.mjs";
import RollDialog from "../roll/roll-dialog.mjs";
import { DEFAULT_ROLL_TYPES, DIFF, ROLL_TYPE_ATTAQUE } from "../roll/roll-constants.mjs";
import { DEFAULT_ROLL_TYPES, DIFF, ROLL_TYPE_ATTAQUE, ROLL_TYPE_COMP } from "../roll/roll-constants.mjs";
import { OptionsAvancees, ROLL_DIALOG_V2 } from "../settings/options-avancees.js";
import { PART_COMP } from "../roll/roll-part-comp.mjs";
import { RdDInitiative } from "../initiative.mjs";
import { RdDItemCompetenceCreature } from "../item-competencecreature.js";
import { RdDPossessionV2 } from "../rdd-possession-v2.mjs";
@@ -145,7 +144,7 @@ export class RdDBaseActorReve extends RdDBaseActor {
}
}
listActions({ isAttaque = false, isEquipe = false }) {
listActions() {
return this.itemTypes[ITEM_TYPES.competencecreature]
.filter(it => it.isAttaque())
.map(it => it.attaqueCreature())
@@ -317,7 +316,7 @@ export class RdDBaseActorReve extends RdDBaseActor {
const competence = this.getCompetence(compName);
const rollData = {
ids: { actorId: this.id },
type: { allowed: DEFAULT_ROLL_TYPES, current: PART_COMP },
type: { allowed: DEFAULT_ROLL_TYPES, current: ROLL_TYPE_COMP },
selected: {
carac: { key: caracName },
comp: { key: competence.name },
@@ -384,7 +383,7 @@ export class RdDBaseActorReve extends RdDBaseActor {
ids: { actorId: this.id },
type: {
allowed: DEFAULT_ROLL_TYPES,
current: PART_COMP,
current: ROLL_TYPE_COMP,
},
selected: {
diff: { type: DIFF.DEFAUT }
@@ -417,8 +416,8 @@ export class RdDBaseActorReve extends RdDBaseActor {
const rollData = {
ids: { actorId: this.id },
type: {
allowed: [PART_COMP],
current: PART_COMP,
allowed: [ROLL_TYPE_COMP],
current: ROLL_TYPE_COMP,
resistance: resistance
},
selected: {
@@ -440,8 +439,8 @@ export class RdDBaseActorReve extends RdDBaseActor {
const rollData = {
ids: { actorId: this.id },
type: {
allowed: options.resistance ? [PART_COMP] : DEFAULT_ROLL_TYPES,
current: PART_COMP,
allowed: options.resistance ? [ROLL_TYPE_COMP] : DEFAULT_ROLL_TYPES,
current: options.resistance ? ROLL_TYPE_COMP : ROLL_TYPE_COMP,
resistance: options.resistance
},
selected: {

View File

@@ -914,11 +914,7 @@ export class RdDBaseActor extends Actor {
isActorCombat() { return false }
getCaracInit(competence) { return 0 }
listAttaques() {
return this.listActions({ isAttaque: true, isEquipe: false })
}
listActions({ isAttaque = false, isEquipe = false }) { return [] }
listActions() { return [] }
listActionsPossessions() {
return this.itemTypes[ITEM_TYPES.possession]

View File

@@ -4,9 +4,8 @@ import { SANS_COMPETENCE } from "../item/base-items.js"
import { Misc } from "../misc.js"
import { CARACS } from "../rdd-carac.js"
import { RdDUtility } from "../rdd-utility.js"
import { DIFF } from "../roll/roll-constants.mjs"
import { DIFF, ROLL_TYPE_COMP } from "../roll/roll-constants.mjs"
import RollDialog from "../roll/roll-dialog.mjs"
import { PART_COMP } from "../roll/roll-part-comp.mjs"
export const MORAL = {
MALHEUREUX: "malheureux",
@@ -134,7 +133,7 @@ export class Apprecier {
const competence = (this.appreciation.jetComp && this.appreciation.competence) ? this.appreciation.competence : ""
const rollData = {
ids: { actorId: this.actor.id },
type: { allowed: [PART_COMP], current: PART_COMP, appreciation: true },
type: { allowed: [ROLL_TYPE_COMP], current: ROLL_TYPE_COMP, appreciation: true },
selected: {
carac: { key: this.appreciation.carac, forced: true },
comp: { key: competence, forced: true },

View File

@@ -170,7 +170,7 @@ export class RdDCombatManager extends Combat {
}
static getFirstInitRollFormula(actor) {
const actions = actor.listActions({ isEquipe: true })
const actions = actor.listActions()
if (actions.length > 0) {
const action = actions[0]
const init = RdDCombatManager.getInitData(actor, action)
@@ -307,7 +307,7 @@ export class RdDCombatManager extends Combat {
const possessions = actor.listActionsPossessions()
const actions = possessions.length > 0
? possessions
: actor.listActions({ isEquipe: true })
: actor.listActions()
return Misc.indexed(actions)
}
@@ -644,7 +644,7 @@ export class RdDCombat {
opponentId: this.defender.id,
opponentTokenId: this.defenderTokenId,
},
type: { allowed: ['attaque'], current: 'attaque' },
type: { allowed: [ROLL_TYPE_ATTAQUE], current: ROLL_TYPE_ATTAQUE },
selected: {},
passeArme: foundry.utils.randomID(16),
}

View File

@@ -218,16 +218,16 @@ export class RdDCommands {
/* -------------------------------------------- */
/* Manage chat commands */
processChatCommand(commandLine, content = '', msg = {}) {
// Setup new message's visibility
ChatUtility.applyRollMode(msg)
msg.type = 0;
if (!this.commandsTable) {
this._registerCommands()
}
let command = commandLine[0].toLowerCase();
if (this._isCommandHandled(command)) {
// Setup new message's visibility
ChatUtility.applyRollMode(msg)
msg.type = 0;
let params = commandLine.slice(1);
this._processCommand(this.commandsTable, command, params, content, msg)
return true

View File

@@ -18,7 +18,7 @@ export class DeTMR extends foundry.dice.terms.Die {
static diceSoNiceData(system) {
return {
type: "dt",
type: "d8",
font: "HeuresDraconiques",
fontScale: 0.8,
labels: ['1', '2', '3', '4', '5', '6', 'd', '0'],
@@ -56,7 +56,7 @@ export class DeDraconique extends foundry.dice.terms.Die {
static diceSoNiceData(system) {
return {
type: "dr",
type: "d8",
font: "HeuresDraconiques",
fontScale: 0.8,
labels: ['1', '2', '3', '4', '5', '6', 'd', '0'],
@@ -96,7 +96,7 @@ export class DeHeure extends foundry.dice.terms.Die {
static diceSoNiceData(system) {
return {
type: "dh",
type: "d12",
font: "HeuresDraconiques",
fontScale: 1.2,
labels: ['v', 'i', 'f', 'o', 'd', 'e', 'l', 's', 'p', 'a', 'r', 'c'],

View File

@@ -377,7 +377,17 @@ export class SystemReveDeDragon {
/* -------------------------------------------- */
game.system.rdd.calendrier = new RdDCalendrier()
if (Misc.isFirstConnectedGM()) {
new Migrations().migrate()
try {
new Migrations().migrate()
}
catch (erreur) {
const message = `<p>Erreur lors de la migration du système Rêve de Dragon: ${erreur}</p><p>Plus de détails sont sans doute disponibles dans la console de Foundry/du navigateur.</p><p>Merci de nous signaler cetter erreur qu'on puisse ous aider et réparer le problème.</p>`
ui.notifications.error(message)
await ChatMessage.create({
user: game.user.id,
content: message
})
}
this.messageDeBienvenue()
import("https://www.uberwald.me/fvtt_appcount/count-class-ready.js").then(moduleCounter => {
console.log("ClassCounter loaded", moduleCounter)
@@ -399,7 +409,7 @@ export class SystemReveDeDragon {
if (!game.user.isGM && game.user.character == undefined) {
ui.notifications.info("Attention ! Vous n'êtes connecté à aucun personnage !")
ChatMessage.create({
content: "<b>ATTENTION</b> Le joueur " + game.user.name + " n'est connecté à aucun personnage !",
content: `<b>ATTENTION</b> Le joueur ${game.user.name} n'est connecté à aucun personnage !`,
user: game.user.id
})
}

View File

@@ -49,7 +49,7 @@ export class RdDTokenHud {
static async addExtensionHudCombatV2(html, combatant, actor, token) {
const isPossession = actor.listActionsPossessions().length > 0;
const actionsCombat = isPossession ? [] : actor.listAttaques()
const actionsCombat = isPossession ? [] : actor.listActions()
const ajustements = combatant?.initiative ?
[
{ label: 'Initiative +1', action: 'delta', value: 1 },

View File

@@ -200,6 +200,7 @@ export default class ChatRollResult {
const chatMessage = ChatUtility.getChatMessage(event)
const savedRoll = this.loadChatMessageRoll(chatMessage)
const actor = this.getActiveActor(savedRoll)
Misc.doIfOwner(actor, it => it.rollAppelChance(
() => this.onAppelChanceSuccess(savedRoll, chatMessage),
() => this.onAppelChanceEchec(savedRoll, chatMessage))

View File

@@ -9,6 +9,7 @@ export const ROLL_TYPE_MEDITATION = 'meditation'
export const ROLL_TYPE_OEUVRE = 'oeuvre'
export const ROLL_TYPE_SORT = 'sort'
export const ROLL_TYPE_TACHE = 'tache'
export const ROLL_TYPE_APPEL_CHANCE = 'appel-chance'
export const ATTAQUE_ROLL_TYPES = [ROLL_TYPE_ATTAQUE]
export const COMBAT_ROLL_TYPES = [ROLL_TYPE_ATTAQUE, ROLL_TYPE_DEFENSE]

View File

@@ -50,6 +50,7 @@ import { RollPartResistance } from "./roll-part-resistance.mjs";
import { RollTypePossession } from "./roll-type-possession.mjs";
import { RollPartPossession } from "./roll-part-possession.mjs";
import { RollPartApprecier } from "./roll-part-apprecier.mjs";
import { RollTypeAppelChance } from "./roll-type-appel-chance.mjs";
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api
@@ -65,6 +66,7 @@ export const ALL_ROLL_TYPES = [
new RollTypeCuisine(),
new RollTypeOeuvre(),
new RollTypeJeu(),
new RollTypeAppelChance(),
// new RollTypeResistance ??
// new RollTypeFixedCarac ??
]
@@ -290,7 +292,6 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2
return rollData.type?.allowed
? ROLL_PARTS.filter(p => RollDialog.$isIntersecting(rollData.type.allowed, p.rollTypes))
: ROLL_PARTS
}
/** pre-configure les paramètres des différentes parties de la fenêtre (par exemple, prépare les listes de caractéristiques/compétences */

View File

@@ -35,7 +35,8 @@ export class RollPartAttaque extends RollPartSelect {
loadRefs(rollData) {
const refs = this.getRefs(rollData)
const attaques = rollData.active.actor.listAttaques()
const attaques = rollData.active.actor.listActions()
.filter(it => !it.initOnly)
.sort(Misc.descending(it => it.comp?.system.niveau ?? -8))
refs.all = attaques.map(it => RollPartAttaque.$extractAttaque(it, rollData))
this.filterAttaquesInitiative(rollData)

View File

@@ -27,7 +27,7 @@ export class RollPartComp extends RollPartSelect {
const selected = this.getSelected(rollData)
const all = this.$getActorComps(rollData)
const selectedComp = selected.key
if (selected.forced && selectedComp) {
if (selected.forced && selectedComp!= undefined ) {
refs.all = all.filter(comp => Grammar.equalsInsensitive(comp.label, selectedComp))
if (refs.all.length == 0) {
if (selected.key && selected.key.length > 0) {

View File

@@ -10,6 +10,11 @@ export class RollPartConditions extends RollPart {
settingMin() { return RollPart.settingKey(this, 'min') }
settingMax() { return RollPart.settingKey(this, 'max') }
visible(rollData) {
const current = this.getCurrent(rollData)
return !current.hide
}
onReady(rollParts) {
game.settings.register(SYSTEM_RDD, this.settingMin(),
{
@@ -36,6 +41,7 @@ export class RollPartConditions extends RollPart {
restore(rollData) {
const current = this.getCurrent(rollData)
current.value = this.getSaved(rollData)?.value ?? current.value ?? 0
current.hide = this.getSaved(rollData)?.hide
}

View File

@@ -37,7 +37,7 @@ export class RollPartDiff extends RollPart {
}
const current = this.getCurrent(rollData)
/* TODO: affiner les cas où afficher ou non. devrait s'afficher pour les jets basiques (même si pas d'opposant sélectionné)*/
return Object.values(DIFF).includes(current.type)
return (DIFF.AUCUN !=current.type) && Object.values(DIFF).includes(current.type)
}
prepareContext(rollData) {

View File

@@ -0,0 +1,44 @@
import { CARACS } from "../rdd-carac.js"
import { DIFF, ROLL_TYPE_APPEL_CHANCE } from "./roll-constants.mjs"
import { RollType } from "./roll-type.mjs"
const SPECIAL_ROLL_TYPES_DETAILS = {
label: "fait appel à la chance",
icon: `systems/foundryvtt-reve-de-dragon/assets/ui/chance.svg`,
rollData: {
selected: {
carac: { key: CARACS.CHANCE_ACTUELLE, forced: true },
diff: { value: 0, type: '' },
comp: { key: '', forced: true },
conditions: { hide: true }
}
}
}
export class RollTypeAppelChance extends RollType {
get code() { return ROLL_TYPE_APPEL_CHANCE }
get name() { return `fait appel à la chance` }
get icon() { return `systems/foundryvtt-reve-de-dragon/assets/ui/chance.svg` }
get chatResultTemplate() { return `systems/foundryvtt-reve-de-dragon/templates/roll/result/chat-appel-chance.hbs` }
title(rollData) {
return this.name
}
setRollDataType(rollData) {
foundry.utils.mergeObject(rollData, {
selected: {
carac: { key: CARACS.CHANCE_ACTUELLE, forced: true },
diff: { value: 0, type: '' },
comp: { key: '', forced: true },
conditions: { hide: true }
}
})
super.setRollDataType(rollData)
}
onSelect(rollData) {
this.setDiffType(rollData, DIFF.AUCUN)
}
}

View File

@@ -14,7 +14,7 @@ export class RollType {
return { code: this.code, name: this.name, icon: this.icon, section: 'type', template: this.template, selected: this.isSelected(rollData) }
}
prepare(rollData){}
prepare(rollData) {}
isAllowed(rollData) { return rollData.type.allowed == undefined || rollData.type.allowed.includes(this.code) }
visible(rollData) { return true }
title(rollData) { return this.code }

View File

@@ -10,7 +10,7 @@ const listeReglesOptionnelles = [
{ group: 'Récupération', name: 'recuperation-reve', descr: "Récupérer le rêve pendant la nuit (les jets sont toujours faits pour les Rêves de Dragons)"},
{ group: 'Récupération', name: 'recuperation-moral', descr: "Le moral revient vers 0 durant Château Dormant"},
{ group: 'Règles de combat', name: 'armes-equipees', descr: "Proposer uniquement les armes équipées au combat", default: false},
{ group: 'Règles de combat', name: 'localisation-aleatoire', descr: "Proposer une localisation aléatoire des blessures" },
{ group: 'Règles de combat', name: 'recul', descr: "Appliquer le recul en cas de particulière en force ou de charge" },
{ group: 'Règles de combat', name: 'acrobatie-pour-recul', descr: "L'acrobatie aide à ne pas chuter en cas de recul" , default: false },

View File

@@ -0,0 +1,22 @@
<div class="roll-chat">
<div class="chat-img">
<img src="{{active.img}}" data-tooltip="{{active.name}}" />
<img src="{{current.comp.comp.img}}" data-tooltip="{{current.comp.label}}" />
</div>
<div class="chat-header">
{{active.name}} fait appel à la chance
</div>
<div class="chat-resume">
{{> 'partial-infojet'}}
</div>
<div class="chat-details">
{{#if rolled.isSuccess}}
<p>Un point de chance a été dépensé, le jet peut être retenté</p>
{{else}}
<p>Pas de dépense de chance</p>
{{/if}}
</div>
</div>