411 lines
15 KiB
JavaScript
411 lines
15 KiB
JavaScript
/**
|
|
* InnRoller
|
|
* Classe de gestion des jets de tables d'auberge pour WFRP4e
|
|
* Module de traduction française
|
|
*/
|
|
|
|
export default class InnRoller {
|
|
static tableNames = {
|
|
'boissonsbase': 'BoissonsBase',
|
|
'boissonsfortes': 'BoissonsFortes',
|
|
'desserts': 'Desserts',
|
|
'platscommuns': 'PlatsCommuns',
|
|
'platsexcellents': 'PlatsExcellents',
|
|
'platsmaritimes': 'PlatsMaritimes',
|
|
'platsmediocres': 'PlatsMédiocres',
|
|
'platsqualite': 'PlatsQualité',
|
|
'platsrivieres': 'PlatsRivières'
|
|
};
|
|
|
|
static displayNames = {
|
|
'BoissonsBase': 'Boissons de Base',
|
|
'BoissonsFortes': 'Boissons Fortes',
|
|
'Desserts': 'Desserts',
|
|
'PlatsCommuns': 'Plats Communs',
|
|
'PlatsExcellents': 'Plats Excellents',
|
|
'PlatsMaritimes': 'Plats Maritimes',
|
|
'PlatsMédiocres': 'Plats Médiocres',
|
|
'PlatsQualité': 'Plats de Qualité',
|
|
'PlatsRivières': 'Plats de Rivières'
|
|
};
|
|
|
|
/**
|
|
* Obtient le nom d'affichage formaté pour une table
|
|
* @param {String} tableName
|
|
* @returns {String}
|
|
*/
|
|
static getDisplayName(tableName) {
|
|
return this.displayNames[tableName] || tableName;
|
|
}
|
|
|
|
/**
|
|
* Normalise le nom d'une table (enlève accents, espaces, met en minuscules)
|
|
* @param {String} name
|
|
* @returns {String}
|
|
*/
|
|
static normalizeTableName(name) {
|
|
return name
|
|
.toLowerCase()
|
|
.normalize("NFD")
|
|
.replace(/[\u0300-\u036f]/g, "")
|
|
.replace(/\s+/g, '');
|
|
}
|
|
|
|
/**
|
|
* Trouve la table correspondant au mot-clé
|
|
* @param {String} keyword
|
|
* @returns {String|null}
|
|
*/
|
|
static findTableByKeyword(keyword) {
|
|
if (!keyword) return null;
|
|
|
|
const normalized = this.normalizeTableName(keyword);
|
|
|
|
// Recherche exacte
|
|
if (this.tableNames[normalized]) {
|
|
return this.tableNames[normalized];
|
|
}
|
|
|
|
// Recherche partielle
|
|
for (let [key, value] of Object.entries(this.tableNames)) {
|
|
if (key.includes(normalized) || normalized.includes(key)) {
|
|
return value;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Lance un jet sur une table d'auberge
|
|
* @param {String} keyword Mot-clé pour identifier la table
|
|
*/
|
|
static async rollInnTable(keyword) {
|
|
console.log(`InnRoller: rollInnTable appelé avec keyword="${keyword}"`);
|
|
|
|
// Si pas de keyword, afficher l'aide
|
|
if (!keyword) {
|
|
this.displayHelp();
|
|
return;
|
|
}
|
|
|
|
// Rechercher la table
|
|
const tableName = this.findTableByKeyword(keyword);
|
|
|
|
if (!tableName) {
|
|
this.displayHelp();
|
|
ui.notifications.warn(`Table d'auberge introuvable pour le mot-clé: "${keyword}"`);
|
|
return;
|
|
}
|
|
|
|
console.log(`InnRoller: Table trouvée: ${tableName}`);
|
|
|
|
// Charger le compendium
|
|
const compendium = game.packs.get('wh4-fr-translation.plats-dauberges');
|
|
|
|
if (!compendium) {
|
|
ui.notifications.error("Compendium 'plats-dauberges' introuvable");
|
|
console.error("InnRoller: Compendium wh4-fr-translation.plats-dauberges non trouvé");
|
|
return;
|
|
}
|
|
|
|
// Récupérer les tables
|
|
const tables = await compendium.getDocuments();
|
|
|
|
// Trouver la table correspondante
|
|
const rollTable = tables.find(t => t.name === tableName);
|
|
|
|
if (!rollTable) {
|
|
ui.notifications.error(`Table "${tableName}" non trouvée dans le compendium`);
|
|
console.error(`InnRoller: Table ${tableName} non trouvée`);
|
|
return;
|
|
}
|
|
|
|
console.log(`InnRoller: Jet sur la table ${rollTable.name}`);
|
|
|
|
// Effectuer le jet sans affichage automatique
|
|
try {
|
|
const roll = await rollTable.draw({ displayChat: false });
|
|
console.log(`InnRoller: Jet effectué avec succès`, roll);
|
|
|
|
// Créer un message personnalisé
|
|
await this.displayRollResult(rollTable.name, roll);
|
|
} catch (error) {
|
|
console.error("InnRoller: Erreur lors du jet:", error);
|
|
ui.notifications.error("Erreur lors du jet sur la table d'auberge");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Affiche le résultat d'un jet de table avec un style personnalisé
|
|
* @param {String} tableName Nom de la table
|
|
* @param {Object} rollResult Résultat du jet
|
|
*/
|
|
static async displayRollResult(tableName, rollResult) {
|
|
// Déterminer l'icône en fonction du type de table
|
|
let icon = "fa-utensils";
|
|
let category = "Plat";
|
|
|
|
if (tableName.toLowerCase().includes('boisson')) {
|
|
icon = "fa-wine-glass";
|
|
category = "Boisson";
|
|
} else if (tableName.toLowerCase().includes('dessert')) {
|
|
icon = "fa-birthday-cake";
|
|
category = "Dessert";
|
|
}
|
|
|
|
// Extraire les informations du résultat
|
|
const resultText = rollResult.results[0]?.text || "Résultat inconnu";
|
|
const rollFormula = rollResult.roll?.formula || "1d100";
|
|
const rollTotal = rollResult.roll?.total || 0;
|
|
|
|
// Construire le message HTML simplifié
|
|
let message = `<div class="wfrp4e-inn-result">`;
|
|
message += `<div class="message-header">`;
|
|
message += `<i class="fas ${icon}"></i> `;
|
|
message += `<span class="flavor-text">${category}: ${tableName}</span>`;
|
|
message += `</div>`;
|
|
message += `<div class="inn-dish-name">${resultText}</div>`;
|
|
message += `<div class="inn-roll-info"><i class="fas fa-dice"></i> ${rollFormula} = ${rollTotal}</div>`;
|
|
message += `</div>`;
|
|
|
|
// Créer le message dans le chat
|
|
await ChatMessage.create({
|
|
content: message,
|
|
speaker: ChatMessage.getSpeaker(),
|
|
whisper: ChatMessage.getWhisperRecipients("GM")
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Affiche l'aide pour la commande /auberge avec liste cliquable
|
|
*/
|
|
static displayHelp() {
|
|
let message = `<div class="wfrp4e-inn-help">`;
|
|
message += `<h3><i class="fas fa-utensils"></i> Aide pour /auberge</h3>`;
|
|
message += `<p><strong>Usage:</strong> <code>/auberge [mot_clé]</code></p>`;
|
|
|
|
// Bouton Menu
|
|
message += `<div style="margin: 0.8em 0;">`;
|
|
message += `<a class="action-link inn-menu-quick-btn" data-action="clickAubergeMenu" data-quality="menu">`;
|
|
message += `<i class="fas fa-book-open"></i> Générer un menu complet`;
|
|
message += `</a>`;
|
|
message += `</div>`;
|
|
|
|
message += `<hr>`;
|
|
|
|
// Section avec liste cliquable
|
|
message += `<h4><i class="fas fa-list"></i> Tables disponibles</h4>`;
|
|
message += `<div class="wfrp4e-inn-table-grid">`;
|
|
|
|
const sortedTables = Object.values(this.tableNames).sort();
|
|
for (let tableName of sortedTables) {
|
|
const normalized = this.normalizeTableName(tableName);
|
|
const displayName = this.getDisplayName(tableName);
|
|
message += `<a class="action-link inn-table-btn" data-action="clickAuberge" data-table="${normalized}">`;
|
|
message += `<i class="fas fa-dice"></i> ${displayName}`;
|
|
message += `</a>`;
|
|
}
|
|
|
|
message += `</div>`;
|
|
message += `<hr>`;
|
|
message += `<p style="font-size: 0.9em; margin-top: 0.5em;"><em>Vous pouvez aussi taper <code>/auberge [mot_clé]</code> directement (ex: <code>/auberge base</code>)</em></p>`;
|
|
message += `</div>`;
|
|
|
|
ChatMessage.create({
|
|
content: message,
|
|
whisper: ChatMessage.getWhisperRecipients("GM")
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Liste toutes les tables disponibles
|
|
*/
|
|
static listTables() {
|
|
let message = `<div class="wfrp4e-inn-list">`;
|
|
message += `<h3><i class="fas fa-list"></i> Tables d'auberge disponibles</h3>`;
|
|
message += `<div class="wfrp4e-inn-table-grid">`;
|
|
|
|
const sortedTables = Object.values(this.tableNames).sort();
|
|
for (let tableName of sortedTables) {
|
|
const normalized = this.normalizeTableName(tableName);
|
|
const displayName = this.getDisplayName(tableName);
|
|
message += `<a class="action-link inn-table-btn" data-action="clickAuberge" data-table="${normalized}">`;
|
|
message += `<i class="fas fa-dice"></i> ${displayName}`;
|
|
message += `</a>`;
|
|
}
|
|
|
|
message += `</div>`;
|
|
message += `</div>`;
|
|
|
|
ChatMessage.create({
|
|
content: message,
|
|
whisper: ChatMessage.getWhisperRecipients("GM")
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Affiche le choix de qualité pour générer un menu complet
|
|
*/
|
|
static displayMenuChoice() {
|
|
let message = `<div class="wfrp4e-inn-menu-choice">`;
|
|
message += `<h3><i class="fas fa-book-open"></i> Menu de l'auberge</h3>`;
|
|
message += `<p>Choisissez la qualité du menu :</p>`;
|
|
message += `<div class="inn-menu-buttons">`;
|
|
|
|
message += `<a class="action-link inn-menu-btn" data-action="clickAubergeMenu" data-quality="mediocre">`;
|
|
message += `<i class="fas fa-drumstick-bite"></i> Menu Médiocre`;
|
|
message += `<br><span class="inn-menu-desc">Plat médiocre + Boisson de base</span>`;
|
|
message += `</a>`;
|
|
|
|
message += `<a class="action-link inn-menu-btn" data-action="clickAubergeMenu" data-quality="commun">`;
|
|
message += `<i class="fas fa-utensils"></i> Menu Commun`;
|
|
message += `<br><span class="inn-menu-desc">Plat commun + Boisson de base + Dessert</span>`;
|
|
message += `</a>`;
|
|
|
|
message += `<a class="action-link inn-menu-btn" data-action="clickAubergeMenu" data-quality="qualite">`;
|
|
message += `<i class="fas fa-crown"></i> Menu de Qualité`;
|
|
message += `<br><span class="inn-menu-desc">Plat de qualité + Boisson forte + Dessert</span>`;
|
|
message += `</a>`;
|
|
|
|
message += `<a class="action-link inn-menu-btn" data-action="clickAubergeMenu" data-quality="fluvial">`;
|
|
message += `<i class="fas fa-fish"></i> Menu Fluvial`;
|
|
message += `<br><span class="inn-menu-desc">Plat de rivière + Boisson de base + Dessert</span>`;
|
|
message += `</a>`;
|
|
|
|
message += `<a class="action-link inn-menu-btn" data-action="clickAubergeMenu" data-quality="maritime">`;
|
|
message += `<i class="fas fa-anchor"></i> Menu Maritime`;
|
|
message += `<br><span class="inn-menu-desc">Plat maritime + Boisson forte + Dessert</span>`;
|
|
message += `</a>`;
|
|
|
|
message += `<a class="action-link inn-menu-btn" data-action="clickAubergeMenu" data-quality="excellent">`;
|
|
message += `<i class="fas fa-gem"></i> Menu Excellent`;
|
|
message += `<br><span class="inn-menu-desc">Plat excellent + Boisson forte + Dessert</span>`;
|
|
message += `</a>`;
|
|
|
|
message += `</div>`;
|
|
message += `</div>`;
|
|
|
|
ChatMessage.create({
|
|
content: message,
|
|
whisper: ChatMessage.getWhisperRecipients("GM")
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Génère un menu complet selon la qualité choisie
|
|
* @param {String} quality - 'mediocre', 'commun', ou 'qualite'
|
|
*/
|
|
static async generateMenu(quality) {
|
|
console.log(`InnRoller: generateMenu appelé avec quality="${quality}"`);
|
|
|
|
let tables = [];
|
|
let menuName = "";
|
|
|
|
// Définir les tables à tirer selon la qualité
|
|
switch(quality) {
|
|
case 'mediocre':
|
|
menuName = "Menu Médiocre";
|
|
tables = ['PlatsMédiocres', 'BoissonsBase'];
|
|
break;
|
|
case 'commun':
|
|
menuName = "Menu Commun";
|
|
tables = ['PlatsCommuns', 'BoissonsBase', 'Desserts'];
|
|
break;
|
|
case 'qualite':
|
|
menuName = "Menu de Qualité";
|
|
tables = ['PlatsQualité', 'BoissonsFortes', 'Desserts'];
|
|
break;
|
|
case 'fluvial':
|
|
menuName = "Menu Fluvial";
|
|
tables = ['PlatsRivières', 'BoissonsBase', 'Desserts'];
|
|
break;
|
|
case 'maritime':
|
|
menuName = "Menu Maritime";
|
|
tables = ['PlatsMaritimes', 'BoissonsFortes', 'Desserts'];
|
|
break;
|
|
case 'excellent':
|
|
menuName = "Menu Excellent";
|
|
tables = ['PlatsExcellents', 'BoissonsFortes', 'Desserts'];
|
|
break;
|
|
default:
|
|
ui.notifications.error(`Qualité de menu inconnue: ${quality}`);
|
|
return;
|
|
}
|
|
|
|
// Charger le compendium
|
|
const compendium = game.packs.get('wh4-fr-translation.plats-dauberges');
|
|
if (!compendium) {
|
|
ui.notifications.error("Compendium 'plats-dauberges' introuvable");
|
|
return;
|
|
}
|
|
|
|
const allTables = await compendium.getDocuments();
|
|
let results = [];
|
|
|
|
// Effectuer les jets sur chaque table
|
|
for (let tableName of tables) {
|
|
const rollTable = allTables.find(t => t.name === tableName);
|
|
if (rollTable) {
|
|
try {
|
|
const roll = await rollTable.draw({ displayChat: false });
|
|
const resultText = roll.results[0]?.text || "Résultat inconnu";
|
|
results.push({
|
|
category: this.getCategoryName(tableName),
|
|
name: resultText,
|
|
tableName: tableName
|
|
});
|
|
} catch (error) {
|
|
console.error(`InnRoller: Erreur lors du jet sur ${tableName}:`, error);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Afficher le menu complet
|
|
this.displayMenuResult(menuName, results);
|
|
}
|
|
|
|
/**
|
|
* Obtient le nom de catégorie pour une table
|
|
* @param {String} tableName
|
|
* @returns {String}
|
|
*/
|
|
static getCategoryName(tableName) {
|
|
if (tableName.includes('Boisson')) return 'Boisson';
|
|
if (tableName.includes('Dessert')) return 'Dessert';
|
|
if (tableName.includes('Plat')) return 'Plat';
|
|
return 'Item';
|
|
}
|
|
|
|
/**
|
|
* Affiche le résultat d'un menu complet
|
|
* @param {String} menuName
|
|
* @param {Array} results
|
|
*/
|
|
static async displayMenuResult(menuName, results) {
|
|
let message = `<div class="wfrp4e-inn-menu-result">`;
|
|
message += `<div class="message-header">`;
|
|
message += `<i class="fas fa-book-open"></i> `;
|
|
message += `<span class="flavor-text">${menuName}</span>`;
|
|
message += `</div>`;
|
|
|
|
message += `<div class="inn-menu-items">`;
|
|
for (let result of results) {
|
|
message += `<div class="inn-menu-item">`;
|
|
let icon = result.category === 'Boisson' ? 'fa-wine-glass' :
|
|
result.category === 'Dessert' ? 'fa-birthday-cake' : 'fa-utensils';
|
|
message += `<i class="fas ${icon}"></i> `;
|
|
message += `<strong>${result.category}:</strong> ${result.name}`;
|
|
message += `</div>`;
|
|
}
|
|
message += `</div>`;
|
|
message += `</div>`;
|
|
|
|
await ChatMessage.create({
|
|
content: message,
|
|
speaker: ChatMessage.getSpeaker(),
|
|
whisper: ChatMessage.getWhisperRecipients("GM")
|
|
});
|
|
}
|
|
}
|