FIx pour Babele 2.8.X
Validation JSON / validate (push) Successful in 17s
Release Creation / build (release) Successful in 58s

This commit is contained in:
2026-04-27 23:58:55 +02:00
parent df1e0b9952
commit 0d20d76db0
53 changed files with 9322 additions and 9028 deletions
+22 -22
View File
@@ -1,6 +1,6 @@
/************************************************************************************/
import { WH4FRPatchConfig } from "./config-patch.js";
import { TranslatedCompendium } from "../../babele/script/translated-compendium.js";
//import { TranslatedCompendium } from "../../babele/script/translated-compendium.js";
/************************************************************************************/
const _patch_eis = () => {
@@ -175,23 +175,23 @@ const patch_trade_gazeteer = () => {
// Translate river cargoTypes to French (DotR module registers English values)
if (game.wfrp4e.trade?.tradeData?.river?.cargoTypes) {
Object.assign(game.wfrp4e.trade.tradeData.river.cargoTypes, {
"grain": game.i18n.localize("TRADE.Grain"),
"armaments": game.i18n.localize("TRADE.Armaments"),
"luxuries": game.i18n.localize("TRADE.Luxuries"),
"metal": game.i18n.localize("TRADE.Metal"),
"timber": game.i18n.localize("TRADE.Timber"),
"wine": game.i18n.localize("TRADE.Wine"),
"brandy": game.i18n.localize("TRADE.Brandy"),
"wool": game.i18n.localize("TRADE.Wool"),
"grain": game.i18n.localize("TRADE.Grain"),
"armaments": game.i18n.localize("TRADE.Armaments"),
"luxuries": game.i18n.localize("TRADE.Luxuries"),
"metal": game.i18n.localize("TRADE.Metal"),
"timber": game.i18n.localize("TRADE.Timber"),
"wine": game.i18n.localize("TRADE.Wine"),
"brandy": game.i18n.localize("TRADE.Brandy"),
"wool": game.i18n.localize("TRADE.Wool"),
});
}
// Translate maritime cargoTypes to French (SOC module)
if (game.wfrp4e.trade?.tradeData?.maritime?.cargoTypes) {
const maritimeKeys = {
"citrusfruit": "TRADE.Citrusfruit",
"olives": "TRADE.Olives",
"saltfish": "TRADE.Saltfish",
"stone": "TRADE.Stone",
"olives": "TRADE.Olives",
"saltfish": "TRADE.Saltfish",
"stone": "TRADE.Stone",
};
for (let [key, locKey] of Object.entries(maritimeKeys)) {
if (game.wfrp4e.trade.tradeData.maritime.cargoTypes[key]) {
@@ -199,7 +199,7 @@ const patch_trade_gazeteer = () => {
}
}
// Translate shared keys that may also appear in maritime
Object.assign(game.wfrp4e.trade.tradeData.maritime.cargoTypes,
Object.assign(game.wfrp4e.trade.tradeData.maritime.cargoTypes,
Object.fromEntries(
Object.entries(game.wfrp4e.trade.tradeData.maritime.cargoTypes)
.filter(([k]) => game.wfrp4e.trade.tradeData.river?.cargoTypes?.[k])
@@ -273,12 +273,12 @@ const __check_fix_wrong_modules = (chatFlag, patchFinished) => {
});
}
} else if (game.user.isGM && patchFinished) {
ChatMessage.create({
content: "<div>Les modules WFRP4E ont été <strong>patchés avec succés</strong>. Vous pouvez y aller et que <strong>Shallya vous garde !</strong><div><div>Changements v9.3.5 : <ul><li>Ajout de la commande /voyage !</li><li>Améliorations de la commande /auberge</li><li>Les joueurs doivent désormais pouvoir créer leur persos</li><li>Très grosses mise à jour des scripts d'Effets et de leur traduction</li></ul></div>",
user: game.user.id,
whisper: ChatMessage.getWhisperRecipients("GM")
});
}
ChatMessage.create({
content: "<div>Les modules WFRP4E ont été <strong>patchés avec succés</strong>. Vous pouvez y aller et que <strong>Shallya vous garde !</strong><div><div>Changements v9.3.5 : <ul><li>Ajout de la commande /voyage !</li><li>Améliorations de la commande /auberge</li><li>Les joueurs doivent désormais pouvoir créer leur persos</li><li>Très grosses mise à jour des scripts d'Effets et de leur traduction</li></ul></div>",
user: game.user.id,
whisper: ChatMessage.getWhisperRecipients("GM")
});
}
}
/************************************************************************************/
@@ -361,7 +361,7 @@ const __add_actors_translation = () => {
}
}
//console.log(metadata, translations)
game.babele.packs.set(metadata.metadata.id, new TranslatedCompendium(metadata.metadata, translations))
//game.babele.packs.set(metadata.metadata.id, new TranslatedCompendium(metadata.metadata, translations))
}
}
}
@@ -417,7 +417,7 @@ Hooks.on('ready', () => {
const SpellModel = CONFIG.Item.dataModels?.["spell"];
if (SpellModel?.prototype?.computeSpellDamage) {
const _origComputeSpellDamage = SpellModel.prototype.computeSpellDamage;
SpellModel.prototype.computeSpellDamage = function(formula, options) {
SpellModel.prototype.computeSpellDamage = function (formula, options) {
if (typeof formula === "string") {
const actor = options?.actor || this.parent?.actor;
if (actor?.system?.characteristics) {
@@ -439,7 +439,7 @@ Hooks.on('ready', () => {
// After i18nInit, config.symptoms values are French strings (e.g. "Fièvre"), so
// findKey("Fever", config.symptoms) fails. We normalize via game.i18n.localize first.
const _origPostSymptom = game.wfrp4e.utility.postSymptom.bind(game.wfrp4e.utility);
game.wfrp4e.utility.postSymptom = async function(symptom) {
game.wfrp4e.utility.postSymptom = async function (symptom) {
const baseName = symptom.split("(")[0].trim();
const symkey = warhammer.utility.findKey(baseName, game.wfrp4e.config.symptoms);
if (!symkey) {
+27 -27
View File
@@ -247,9 +247,9 @@ Hooks.once('init', () => {
dir: 'compendium'
})
game.babele.registerConverters({
"career_skills": (skills_list) => {
// Pre-define functions for cross-converter calls (Babele 2.8+ wraps converters
// as objects, so game.babele.converters.X() no longer works as a plain call)
const career_skills_fn = (skills_list) => {
let validCompendiums = game.wfrp4e.tags.getPacksWithTag("skill")
//DEBUG: console.log( "Thru here ...", compendium, skills_list);
if (skills_list) {
@@ -292,10 +292,27 @@ Hooks.once('init', () => {
}
}
return skills_list
},
};
const career_careergroup_fn = (value) => {
if (value == 'Slayer') return "Tueur";
if (value == 'Druidic Priest') return "Druide";
let validCompendiums = game.wfrp4e.tags.getPacksWithTag("career");
for (let compData of validCompendiums) {
let newName = game.babele.translate(compData.metadata.id, { name: value }).name;
if (!newName) newName = value;
return newName;
}
ui.notifications.error("Impossible de trouver la carrière " + value + ". Elle n'est probablement pas traduite.", { permanent: true });
return value;
};
game.babele.registerConverters({
"career_skills": career_skills_fn,
"role_skills": (skills) => {
let skills_list = skills.split(',');
let skillsFR = game.babele.converters.career_skills(skills_list);
let skillsFR = career_skills_fn(skills_list);
return skillsFR.join(', ')
},
"process_effects": (effectsData, translations, data, tc, tc_translations) => {
@@ -358,13 +375,14 @@ Hooks.once('init', () => {
let career = data.description.match(/{(.*)}/)
//console.log(">>>>>", career)
if (career?.[1]) {
let careerFR = game.babele.converters.career_careergroup(career[1])
let careerFR = career_careergroup_fn(career[1])
data.description = data.description.replace(career[1], careerFR)
}
}
}
if (results?.[0].documentUuid) {
return game.babele.converters.tableResults(results)
// Babele 2.8+ handles documentUuid resolution via its own mapping system
return results
}
return results
},
@@ -642,25 +660,7 @@ Hooks.once('init', () => {
}
},
// Search back in careers the translated name of the groupe (as it is the name of the level career itself)
"career_careergroup": (value) => {
// Manage exception - Dirty hack
if (value == 'Slayer') {
return "Tueur";
}
if (value == 'Druidic Priest') {
return "Druide";
}
//console.log("Carre groupe : ", value )
// Per default
let validCompendiums = game.wfrp4e.tags.getPacksWithTag("career")
for (let compData of validCompendiums) {
let newName = game.babele.translate(compData.metadata.id, { name: value }).name
if (!newName) newName = value
return newName
}
ui.notifications.error("Impossible de trouver la carrière " + value + ". Elle n'est probablement pas traduite.", { permanent: true })
return value
},
"career_careergroup": career_careergroup_fn,
"mutations_modifier": (value) => { // This is really UGLYYYY i know, but i started like this and discovered afterward that many strings were not easy to automate... Sorry :)
//console.log("Parsing mutation :", value);
@@ -699,7 +699,7 @@ Hooks.once('init', () => {
if (!translations) return;
for (let i = 0; i < effects.length; i++) {
let effect = effects[i];
effect.label = translations['label' + i];
effect.name = translations['label' + i];
}
return effects
},
+51
View File
@@ -399,6 +399,15 @@ export default class InnRoller {
message += `</div>`;
}
message += `</div>`;
// Bouton pour enregistrer dans le journal "Menus"
message += `<div class="inn-journal-action" style="margin-top:8px;text-align:right">`;
message += `<button type="button" class="inn-journal-btn" data-action="saveMenuToJournal"`;
message += ` data-menu-name="${menuName}">`;
message += `<i class="fas fa-book"></i> Enregistrer dans le journal "Menus"`;
message += `</button>`;
message += `</div>`;
message += `</div>`;
await ChatMessage.create({
@@ -407,4 +416,46 @@ export default class InnRoller {
whisper: ChatMessage.getWhisperRecipients("GM")
});
}
/**
* Enregistre le contenu d'un menu dans le journal FoundryVTT "Menus"
* @param {String} menuName - Nom du menu (ex: "Menu Commun")
* @param {String} content - HTML complet du message de tchat
*/
static async saveMenuToJournal(menuName, content) {
if (!game.user.isGM) {
ui.notifications.warn("Seul le MJ peut enregistrer dans le journal.");
return;
}
const journalName = "Menus";
let journal = game.journal.find(j => j.name === journalName);
if (!journal) {
journal = await JournalEntry.create({ name: journalName });
}
// Supprimer le bouton du contenu avant de l'enregistrer
const parser = new DOMParser();
const doc = parser.parseFromString(content, 'text/html');
doc.querySelectorAll('.inn-journal-action').forEach(el => el.remove());
const cleanContent = doc.body.innerHTML;
const now = new Date();
const dateStr = now.toLocaleDateString('fr-FR');
const pageName = `${menuName} (${dateStr})`;
await journal.createEmbeddedDocuments("JournalEntryPage", [{
name: pageName,
type: "text",
text: {
content: cleanContent,
format: CONST.JOURNAL_ENTRY_PAGE_FORMATS.HTML
}
}]);
// Ouvrir le journal sur la nouvelle page
const page = journal.pages.find(p => p.name === pageName);
journal.sheet.render(true, { pageId: page?.id });
ui.notifications.info(`Menu "${menuName}" enregistré dans le journal.`);
}
}
+17 -1
View File
@@ -18,7 +18,15 @@ export function initInn() {
// Enregistrer la commande dans le système WFRP4e si disponible
if (game.wfrp4e?.commands) {
console.log("Inn: Enregistrement de la commande /auberge");
// Patch warhammer-lib 3.0.2 bug: parseArgs crashes when no args provided
// (match.groups.args is undefined when user types command without arguments)
if (game.wfrp4e.commands.parseArgs && !game.wfrp4e.commands._parseArgsPatched) {
const _origParseArgs = game.wfrp4e.commands.parseArgs.bind(game.wfrp4e.commands);
game.wfrp4e.commands.parseArgs = function(command, text) {
return _origParseArgs(command, text ?? "");
};
game.wfrp4e.commands._parseArgsPatched = true;
}
game.wfrp4e.commands.add({
auberge: {
description: "Jets sur les tables d'auberge (FR)",
@@ -89,5 +97,13 @@ export function initInn() {
ui.notifications.warn("Seul le MJ peut utiliser les tables d'auberge.");
}
});
// Bouton "Enregistrer dans le journal Menus"
html.find('button[data-action="saveMenuToJournal"]').click(async (event) => {
event.preventDefault();
const btn = event.currentTarget;
const menuName = btn.dataset.menuName;
await InnRoller.saveMenuToJournal(menuName, message.content);
});
});
}
+61
View File
@@ -148,6 +148,9 @@ export default class TravelDistanceV2 {
}
}
}
// Bouton pour enregistrer dans le journal
message += this.formatJournalButton(originalFrom, originalTo);
} else if (fromTown && fromTown == "help") {
console.log("TravelV2: Branche: Affichage de l'aide");
// Afficher l'aide
@@ -222,6 +225,64 @@ export default class TravelDistanceV2 {
TravelDistanceV2.displayTravelDistance(fromTown, toTown);
}
/**
* Retourne le HTML du bouton "Enregistrer dans le journal"
* @param {String} from - Ville de départ
* @param {String} to - Ville d'arrivée
* @returns {String} HTML du bouton
*/
static formatJournalButton(from, to) {
return `<div class="voyage-journal-action" style="margin-top:8px;text-align:right">
<button type="button" class="voyage-journal-btn" data-action="saveVoyageToJournal"
data-from="${from}" data-to="${to}">
<i class="fas fa-book"></i> Enregistrer dans le journal "Voyages"
</button>
</div>`;
}
/**
* Enregistre le contenu d'un voyage dans le journal FoundryVTT "Voyages"
* @param {String} from - Ville de départ
* @param {String} to - Ville d'arrivée
* @param {String} content - HTML complet du message de tchat
*/
static async saveVoyageToJournal(from, to, content) {
if (!game.user.isGM) {
ui.notifications.warn("Seul le MJ peut enregistrer dans le journal.");
return;
}
const journalName = "Voyages";
let journal = game.journal.find(j => j.name === journalName);
if (!journal) {
journal = await JournalEntry.create({ name: journalName });
}
// Supprimer le bouton du contenu avant de l'enregistrer
const parser = new DOMParser();
const doc = parser.parseFromString(content, 'text/html');
doc.querySelectorAll('.voyage-journal-action').forEach(el => el.remove());
const cleanContent = doc.body.innerHTML;
const now = new Date();
const dateStr = now.toLocaleDateString('fr-FR');
const pageName = `${from}${to} (${dateStr})`;
await journal.createEmbeddedDocuments("JournalEntryPage", [{
name: pageName,
type: "text",
text: {
content: cleanContent,
format: CONST.JOURNAL_ENTRY_PAGE_FORMATS.HTML
}
}]);
// Ouvrir le journal sur la nouvelle page
const page = journal.pages.find(p => p.name === pageName);
journal.sheet.render(true, { pageId: page?.id });
ui.notifications.info(`Voyage "${from}${to}" enregistré dans le journal.`);
}
/**
* Formate l'affichage d'une route directe
* @param {Object} travel - Données de la route
+18 -1
View File
@@ -20,7 +20,15 @@ export function initTravelV2() {
// Enregistrer la commande dans le système WFRP4e si disponible
if (game.wfrp4e?.commands) {
console.log("TravelV2: Enregistrement de la commande /voyage");
// Patch warhammer-lib 3.0.2 bug: parseArgs crashes when no args provided
// (already applied by inn-init if it ran first, but safe to apply again)
if (game.wfrp4e.commands.parseArgs && !game.wfrp4e.commands._parseArgsPatched) {
const _origParseArgs = game.wfrp4e.commands.parseArgs.bind(game.wfrp4e.commands);
game.wfrp4e.commands.parseArgs = function(command, text) {
return _origParseArgs(command, text ?? "");
};
game.wfrp4e.commands._parseArgsPatched = true;
}
game.wfrp4e.commands.add({
voyage: {
description: "Outil de calcul de distances de voyage (FR)",
@@ -61,6 +69,15 @@ export function initTravelV2() {
const target = event.currentTarget;
TravelDistanceV2.handleTravelClick(event, target);
});
// Bouton "Enregistrer dans le journal"
html.find('button[data-action="saveVoyageToJournal"]').click(async (event) => {
event.preventDefault();
const btn = event.currentTarget;
const from = btn.dataset.from;
const to = btn.dataset.to;
await TravelDistanceV2.saveVoyageToJournal(from, to, message.content);
});
});
console.log("TravelV2: Module de voyage initialisé");