code review: fix critical issues and improve code quality
- Fix constructor in rollDialog.mjs (spread operator for options) - Remove all console.log statements from production code - Add comprehensive JSDoc comments for all public APIs - Convert French comments to English for consistency - Use parseInt with radix parameter (10) throughout - Replace let with const where appropriate - Use Set for O(1) lookups in group-link.mjs methods - Use spread operators for array cloning - Optimize removeActorFromAllGroups with Set lookups - Improve registerHooks with better comments and Set usage - Simplify roll-message.hbs template logic - Fix duplicate VERMINE key in lang/fr.json - Add missing error translations - Add .eslintrc.js with FoundryVTT-compatible linting config Compatibility: FoundryVTT v11-v14 Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
+295
-228
@@ -1,109 +1,119 @@
|
||||
export class VermineUtils {
|
||||
/**
|
||||
* Méthode pour effectuer un jet de dés avec différentes options
|
||||
* @param {Object} options - Les options du jet de dés
|
||||
* @param {Actor} options.actor - L'acteur qui lance les dés
|
||||
* @param {number} options.NoD - Nombre de dés de base
|
||||
* @param {number} [options.Reroll=0] - Nombre de relances autorisées
|
||||
* @param {number} [options.difficulty=7] - Difficulté du jet
|
||||
* @param {number} [options.self_control=0] - Sang-froid utilisé
|
||||
* @param {string} [options.rollLabel="jet custom"] - Libellé du jet
|
||||
* @param {Object} [options.totems={}] - Totems utilisés {human: false, adapted: false}
|
||||
* @param {number} [options.max_effort=0] - Effort maximum
|
||||
* @param {string} [options.skillCategory=null] - Catégorie de compétence pour les bonus de domaine
|
||||
* @param {string} [options.keepTotem=null] - Totem à garder ('human' ou 'adapted')
|
||||
* @param {number} [options.skillLevel=null] - Niveau de la compétence pour les réussites automatiques
|
||||
* @param {boolean} [options.hasSpecialty=false] - Si une spécialité est utilisée
|
||||
* @returns {Roll} - Le résultat du jet de dés
|
||||
* Rolls dice with Vermine2047-specific rules.
|
||||
* @param {Object} options - Roll options
|
||||
* @param {Actor} options.actor - The actor rolling
|
||||
* @param {number} options.NoD - Base dice pool
|
||||
* @param {number} [options.Reroll=0] - Reroll count
|
||||
* @param {number} [options.difficulty=7] - Difficulty threshold
|
||||
* @param {number} [options.self_control=0] - Self control used
|
||||
* @param {string} [options.rollLabel="jet custom"] - Roll label
|
||||
* @param {Object} [options.totems={}] - Totems used {human: boolean, adapted: boolean}
|
||||
* @param {number} [options.max_effort=0] - Max effort
|
||||
* @param {string} [options.skillCategory=null] - Skill category for domain bonuses
|
||||
* @param {string} [options.keepTotem=null] - Totem to keep ('human' or 'adapted')
|
||||
* @param {number} [options.skillLevel=null] - Skill level for auto-successes
|
||||
* @param {boolean} [options.hasSpecialty=false] - Whether a specialty is used
|
||||
* @returns {Promise<Roll>} The roll result
|
||||
*/
|
||||
static async roll({ actor, NoD, Reroll = 0, difficulty = 7, self_control = 0, rollLabel = "jet custom", totems = { human: false, adapted: false }, max_effort = 0, skillCategory = null, keepTotem = null, skillLevel = null, hasSpecialty = false }) {
|
||||
// Déclaration des variables
|
||||
static async roll({
|
||||
actor,
|
||||
NoD,
|
||||
Reroll = 0,
|
||||
difficulty = 7,
|
||||
self_control = 0,
|
||||
rollLabel = "jet custom",
|
||||
totems = { human: false, adapted: false },
|
||||
max_effort = 0,
|
||||
skillCategory = null,
|
||||
keepTotem = null,
|
||||
skillLevel = null,
|
||||
hasSpecialty = false
|
||||
}) {
|
||||
// Validate inputs
|
||||
if (!actor) {
|
||||
throw new Error("Actor is required for rolling");
|
||||
}
|
||||
|
||||
// Sanitize user name for use in dice flavor
|
||||
const safeUserName = (game.user?.name ?? "user").replace(/[^a-zA-Z0-9_]/g, '_');
|
||||
|
||||
// Declare variables
|
||||
let formula = "";
|
||||
let modFormula = null;
|
||||
let totemBonus = { human: 0, adapted: 0 };
|
||||
|
||||
// Calculer les bonus/malus par domaine de totem
|
||||
|
||||
// Calculate domain bonuses for totems
|
||||
if (skillCategory) {
|
||||
totemBonus = this._calculateTotemDomainBonuses(skillCategory, actor);
|
||||
}
|
||||
|
||||
// Appliquer les réussites automatiques et seuils auto
|
||||
|
||||
// Apply auto-successes and auto-thresholds
|
||||
let autoSuccesses = 0;
|
||||
let adjustedDifficulty = difficulty;
|
||||
|
||||
|
||||
if (skillLevel !== null && skillLevel !== undefined) {
|
||||
// Calculer les réussites automatiques
|
||||
autoSuccesses = this._calculateAutoSuccesses(skillLevel, hasSpecialty);
|
||||
|
||||
// Appliquer le seuil automatique si nécessaire
|
||||
const autoThreshold = this._getAutoThreshold(skillLevel);
|
||||
if (autoThreshold !== null) {
|
||||
adjustedDifficulty = autoThreshold;
|
||||
}
|
||||
}
|
||||
|
||||
// Vérification des totems humains
|
||||
// Handle human totem
|
||||
if (totems.human) {
|
||||
NoD--;
|
||||
const humanDifficulty = skillLevel !== null ? Math.max(adjustedDifficulty, difficulty) : adjustedDifficulty;
|
||||
const humanFormula = "(1D10cs>=" + humanDifficulty + `[human_${game.user.name}]*2)`;
|
||||
|
||||
// Appliquer bonus/malus de domaine
|
||||
const humanFormula = `(1D10cs>=${humanDifficulty}[human_${safeUserName}]*2)`;
|
||||
|
||||
// Apply domain bonus/malus
|
||||
if (totemBonus.human !== 0) {
|
||||
// Si bonus, ajouter un dé supplémentaire, si malus, réduire le pool
|
||||
NoD += totemBonus.human;
|
||||
}
|
||||
|
||||
|
||||
modFormula = humanFormula;
|
||||
}
|
||||
|
||||
// Vérification des totems adaptés
|
||||
|
||||
// Handle adapted totem
|
||||
if (totems.adapted) {
|
||||
NoD--;
|
||||
const adaptedDifficulty = skillLevel !== null ? Math.max(adjustedDifficulty, difficulty) : adjustedDifficulty;
|
||||
const adaptedFormula = "(1D10cs>=" + adaptedDifficulty + `[adapted_${game.user.name}]*2)`;
|
||||
|
||||
// Appliquer bonus/malus de domaine
|
||||
const adaptedFormula = `(1D10cs>=${adaptedDifficulty}[adapted_${safeUserName}]*2)`;
|
||||
|
||||
// Apply domain bonus/malus
|
||||
if (totemBonus.adapted !== 0) {
|
||||
NoD += totemBonus.adapted;
|
||||
}
|
||||
|
||||
// Construction de la formule modifiée
|
||||
if (modFormula != null) {
|
||||
modFormula = modFormula + "+" + adaptedFormula;
|
||||
|
||||
// Build combined formula
|
||||
if (modFormula !== null) {
|
||||
modFormula = `${modFormula}+${adaptedFormula}`;
|
||||
} else {
|
||||
modFormula = adaptedFormula;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Gestion du choix de totem à garder (si les deux sont activés)
|
||||
// Handle keepTotem selection (if both totems are active)
|
||||
if (totems.human && totems.adapted && keepTotem) {
|
||||
// Si on veut garder un seul totem, ne pas doubler le bonus
|
||||
if (keepTotem === 'human' && totems.adapted) {
|
||||
// Retirer le totem adapté du calcul
|
||||
modFormula = "(1D10cs>=" + adjustedDifficulty + `[human_${game.user.name}]*2)`;
|
||||
NoD++; // On avait décrémenté pour adapted, on annule
|
||||
modFormula = `(1D10cs>=${adjustedDifficulty}[human_${safeUserName}]*2)`;
|
||||
NoD++; // Cancel the decrement for adapted
|
||||
} else if (keepTotem === 'adapted' && totems.human) {
|
||||
// Retirer le totem humain du calcul
|
||||
modFormula = "(1D10cs>=" + adjustedDifficulty + `[adapted_${game.user.name}]*2)`;
|
||||
NoD++; // On avait décrémenté pour human, on annule
|
||||
modFormula = `(1D10cs>=${adjustedDifficulty}[adapted_${safeUserName}]*2)`;
|
||||
NoD++; // Cancel the decrement for human
|
||||
}
|
||||
}
|
||||
|
||||
// Construction de la formule de base
|
||||
let baseFormula = '' + NoD + "d10";
|
||||
baseFormula += (adjustedDifficulty != undefined) ? "cs>=" + adjustedDifficulty : "cs>=7";
|
||||
baseFormula += `[regular_${game.user.name}]`
|
||||
// Build base formula
|
||||
const baseFormula = `${NoD}d10cs>=${adjustedDifficulty}[regular_${safeUserName}]`;
|
||||
|
||||
// Construction de la formule finale
|
||||
if (modFormula != null) {
|
||||
formula = baseFormula + "+" + modFormula;
|
||||
} else { formula = baseFormula }
|
||||
// Build final formula
|
||||
formula = modFormula !== null ? `${baseFormula}+${modFormula}` : baseFormula;
|
||||
|
||||
// Création du jet de dés
|
||||
let roll = new Roll(formula, actor.getRollData());
|
||||
// Create the roll
|
||||
const roll = new Roll(formula, actor.getRollData());
|
||||
|
||||
// Stocker les métadonnées du roll pour l'affichage
|
||||
// Store metadata for display
|
||||
roll.vermineData = {
|
||||
totemsUsed: { ...totems },
|
||||
keepTotem: keepTotem,
|
||||
@@ -118,71 +128,93 @@ export class VermineUtils {
|
||||
rerolls: Reroll,
|
||||
selfControl: self_control
|
||||
};
|
||||
|
||||
//effectuer le lancé
|
||||
|
||||
// Evaluate the roll
|
||||
await roll.evaluate();
|
||||
//afficher le lancer 3d
|
||||
|
||||
// Show 3D dice if available
|
||||
await VermineUtils.showDiceSoNice(roll);
|
||||
// afficher le résultat dans le chat
|
||||
VermineUtils.diplayChatRoll(roll, { actor, NoD, Reroll, difficulty, self_control, rollLabel, totems, max_effort, skillCategory, keepTotem, skillLevel, hasSpecialty });
|
||||
|
||||
// Display result in chat
|
||||
VermineUtils.diplayChatRoll(roll, {
|
||||
actor,
|
||||
NoD,
|
||||
Reroll,
|
||||
difficulty,
|
||||
self_control,
|
||||
rollLabel,
|
||||
totems,
|
||||
max_effort,
|
||||
skillCategory,
|
||||
keepTotem,
|
||||
skillLevel,
|
||||
hasSpecialty
|
||||
});
|
||||
|
||||
return roll;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcule les bonus/malus par domaine de totem
|
||||
* @param {string} skillCategory - Catégorie de la compétence
|
||||
* @param {Actor} actor - L'acteur
|
||||
* @returns {Object} - Bonus pour chaque totem {human: number, adapted: number}
|
||||
* Calculates domain bonuses/penalties for totems.
|
||||
* @param {string} skillCategory - The skill category
|
||||
* @param {Actor} actor - The actor
|
||||
* @returns {Object} Bonuses for each totem {human: number, adapted: number}
|
||||
*/
|
||||
static _calculateTotemDomainBonuses(skillCategory, actor) {
|
||||
const bonuses = { human: 0, adapted: 0 };
|
||||
|
||||
|
||||
// Validate inputs
|
||||
if (!CONFIG.VERMINE?.totemDomains || !actor?.system?.identity?.totem) {
|
||||
return bonuses;
|
||||
}
|
||||
|
||||
|
||||
const actorTotem = actor.system.identity.totem;
|
||||
const totemConfig = CONFIG.VERMINE.totemDomains[actorTotem];
|
||||
|
||||
if (!totemConfig || !totemConfig.domains) {
|
||||
// Check if actor's totem exists in configuration
|
||||
if (!CONFIG.VERMINE.totemDomains[actorTotem]) {
|
||||
return bonuses;
|
||||
}
|
||||
|
||||
// Vérifier si la catégorie de compétence est dans les domaines du totem
|
||||
|
||||
const totemConfig = CONFIG.VERMINE.totemDomains[actorTotem];
|
||||
|
||||
if (!totemConfig?.domains) {
|
||||
return bonuses;
|
||||
}
|
||||
|
||||
// Get actor's preferred skill category
|
||||
const preferredCategory = actor.system.skill_categories?.preferred;
|
||||
|
||||
// Bonus pour le totem de l'acteur
|
||||
|
||||
// Bonus for actor's totem if preferred category is in its domains
|
||||
if (preferredCategory && totemConfig.domains.includes(preferredCategory)) {
|
||||
// Le domaine de prédilection est dans les domaines du totem
|
||||
bonuses[actorTotem] = totemConfig.bonus || 1;
|
||||
}
|
||||
|
||||
// Malus pour le totem opposé
|
||||
|
||||
// Penalty for opposite totem if preferred category is in its domains
|
||||
const oppositeTotem = CONFIG.VERMINE.totem_opposites?.[actorTotem];
|
||||
if (oppositeTotem && preferredCategory) {
|
||||
if (oppositeTotem && CONFIG.VERMINE.totemDomains[oppositeTotem]) {
|
||||
const oppositeConfig = CONFIG.VERMINE.totemDomains[oppositeTotem];
|
||||
if (oppositeConfig?.domains?.includes(preferredCategory)) {
|
||||
if (preferredCategory && oppositeConfig?.domains?.includes(preferredCategory)) {
|
||||
bonuses[oppositeTotem] = -(oppositeConfig.bonus || 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return bonuses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcule les réussites automatiques basées sur la maîtrise de la compétence
|
||||
* @param {number} skillLevel - Niveau de la compétence (0-5)
|
||||
* @param {boolean} hasSpecialty - Si une spécialité est utilisée
|
||||
* @returns {number} - Nombre de réussites automatiques
|
||||
* Calculates automatic successes based on skill mastery level.
|
||||
* @param {number} skillLevel - Skill level (0-5)
|
||||
* @param {boolean} [hasSpecialty=false] - Whether a specialty is used
|
||||
* @returns {number} Number of automatic successes
|
||||
*/
|
||||
static _calculateAutoSuccesses(skillLevel, hasSpecialty = false) {
|
||||
// Selon les règles de Vermine2047, les réussites automatiques sont basées sur le niveau de maîtrise
|
||||
// Niveau 0 (Incompétent): 0 réussite automatique
|
||||
// Niveau 1 (Débutant): 0 réussite automatique
|
||||
// Niveau 2 (Compétent): 1 réussite automatique si spécialité utilisée
|
||||
// Niveau 3 (Expert): 1 réussite automatique
|
||||
// Niveau 4 (Maître): 1 réussite automatique + 1 si spécialité utilisée
|
||||
// Niveau 5 (Légende): 2 réussites automatiques
|
||||
// According to Vermine2047 rules, automatic successes are based on mastery level:
|
||||
// Level 0 (Incompetent): 0 automatic successes
|
||||
// Level 1 (Beginner): 0 automatic successes
|
||||
// Level 2 (Proficient): 1 automatic success if specialty is used
|
||||
// Level 3 (Expert): 1 automatic success
|
||||
// Level 4 (Master): 1 automatic success + 1 if specialty is used
|
||||
// Level 5 (Legend): 2 automatic successes
|
||||
|
||||
if (!skillLevel) return 0;
|
||||
|
||||
@@ -210,204 +242,239 @@ export class VermineUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Détermine le seuil automatique si la compétence n'est pas maîtrisée
|
||||
* @param {number} skillLevel - Niveau de la compétence
|
||||
* @returns {number|null} - Seuil automatique ou null si la compétence est maîtrisée
|
||||
* Determines the automatic threshold if the skill is not mastered.
|
||||
* @param {number} skillLevel - Skill level
|
||||
* @returns {number|null} Automatic threshold or null if skill is mastered
|
||||
*/
|
||||
static _getAutoThreshold(skillLevel) {
|
||||
// Si la compétence n'est pas maîtrisée (niveau 0 ou 1), utiliser un seuil par défaut
|
||||
// Niveau 0 (Incompétent): seuil = 9 (très difficile)
|
||||
// Niveau 1 (Débutant): seuil = 7 (difficile)
|
||||
// Niveau >= 2: null (utiliser le seuil normal)
|
||||
// If the skill is not mastered (level 0 or 1), use a default threshold
|
||||
// Level 0 (Incompetent): threshold = 9 (very hard)
|
||||
// Level 1 (Beginner): threshold = 7 (hard)
|
||||
// Level >= 2: null (use normal threshold)
|
||||
|
||||
if (skillLevel === 0) return 9; // Très difficile
|
||||
if (skillLevel === 1) return 7; // Difficile
|
||||
if (skillLevel === 0) return 9; // Very hard
|
||||
if (skillLevel === 1) return 7; // Hard
|
||||
|
||||
return null; // Utiliser le seuil normal
|
||||
}
|
||||
|
||||
/**
|
||||
* Méthode pour gérer les événements de relance de dés
|
||||
* @param {Object} message - Le message contenant l'événement de relance
|
||||
* @param {Object} ev - L'événement de relance
|
||||
* Handles reroll events on dice in chat messages.
|
||||
* @param {Object} message - The chat message containing the reroll event
|
||||
* @param {Object} ev - The reroll event
|
||||
* @returns {Promise<boolean>} Whether the reroll was successful
|
||||
*/
|
||||
static async onReroll(message, ev) {
|
||||
// Vérification de l'utilisateur
|
||||
if (game.user._id != message.user._id || !game.user.isGM) {
|
||||
ui.notifications.warn('vous ne pouvez pas relancer un dés sur ce jet')
|
||||
return false
|
||||
// Verify user permissions
|
||||
if (game.user?._id !== message.user._id && !game.user?.isGM) {
|
||||
ui.notifications.warn(game.i18n.localize('VERMINE.error_cannot_reroll'));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Récupération du nombre de relances autorisé
|
||||
let rerollCount = ev.currentTarget.closest('div.vermine-roll-message').querySelector('#allowed_reroll')?.innerText;
|
||||
// Vérification du nombre de relances restantes
|
||||
if (!rerollCount || parseInt(rerollCount) < 1) {
|
||||
console.log('no reroll')
|
||||
ui.notifications.warn("plus de relance possible");
|
||||
let rerollables = ev.currentTarget.closest('ul').querySelectorAll('.rerollable');
|
||||
rerollables.forEach(el => el.classList.remove('rerollable'));
|
||||
|
||||
// Mise à jour du nombre de relances restantes
|
||||
ev.currentTarget.closest('div.vermine-roll-message').querySelector('#allowed_reroll').innerText = rerollCount - 1;
|
||||
|
||||
let content = ev.currentTarget.closest('div.message-content').outerHTML;
|
||||
|
||||
await message.update({
|
||||
content: content
|
||||
})
|
||||
return false
|
||||
// Get reroll count
|
||||
const rollMessage = ev.currentTarget.closest('div.vermine-roll-message');
|
||||
if (!rollMessage) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let rerollCount = rollMessage.querySelector('#allowed_reroll')?.innerText;
|
||||
|
||||
// Check if rerolls are available
|
||||
if (!rerollCount || parseInt(rerollCount, 10) < 1) {
|
||||
ui.notifications.warn(game.i18n.localize('VERMINE.error_no_rerolls_left'));
|
||||
const rerollables = ev.currentTarget.closest('ul')?.querySelectorAll('.rerollable');
|
||||
if (rerollables) {
|
||||
rerollables.forEach(el => el.classList.remove('rerollable'));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
ev.currentTarget.classList.add('rerolled');
|
||||
|
||||
// Mise en place de la relance
|
||||
// Set reroll flag
|
||||
await message.setFlag("world", "reroll", true);
|
||||
|
||||
// Récupération de la difficulté et du type de dé
|
||||
let difficulty = ev.currentTarget.closest('ul').dataset.difficulty;
|
||||
// Get difficulty and dice type
|
||||
const ulElement = ev.currentTarget.closest('ul');
|
||||
const difficulty = ulElement?.dataset.difficulty ?? 7;
|
||||
let diceType = ev.currentTarget.dataset.diceType;
|
||||
|
||||
// Mise à jour du nombre de relances restantes
|
||||
ev.currentTarget.closest('div.vermine-roll-message').querySelector('#allowed_reroll').innerText = rerollCount - 1;
|
||||
// Sanitize user name
|
||||
const safeUserName = (game.user?.name ?? "user").replace(/[^a-zA-Z0-9_]/g, '_');
|
||||
|
||||
// Construction de la formule de relance
|
||||
// Build reroll formula
|
||||
let formula = `1d10cs>=${difficulty}`;
|
||||
console.log(diceType)
|
||||
switch (diceType.trim()) {
|
||||
|
||||
switch ((diceType ?? '').trim()) {
|
||||
case 'human':
|
||||
formula = `(1d10cs>=${difficulty}[human_${game.user.name}])*2`
|
||||
formula = `(1d10cs>=${difficulty}[human_${safeUserName}])*2`;
|
||||
break;
|
||||
case 'adapted':
|
||||
formula = `(1d10cs>=${difficulty}[adapted_${game.user.name}])*2`
|
||||
formula = `(1d10cs>=${difficulty}[adapted_${safeUserName}])*2`;
|
||||
break;
|
||||
default:
|
||||
formula += `[regular_${game.user.name}]`
|
||||
formula += `[regular_${safeUserName}]`;
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
// Création et évaluation du jet de dés de relance
|
||||
let reroll = await new Roll(formula);
|
||||
// Create and evaluate reroll
|
||||
const reroll = new Roll(formula);
|
||||
await reroll.evaluate();
|
||||
|
||||
//afficher les dés 3d
|
||||
// Show 3D dice if available
|
||||
await VermineUtils.showDiceSoNice(reroll);
|
||||
// mise à jour de l'affichage du dés
|
||||
console.log(reroll)
|
||||
let result = reroll.dice[0].results[0].result;
|
||||
ev.currentTarget.querySelector('span').innerText = result;
|
||||
//mise à jour du total
|
||||
let success = reroll.dice[0].results[0].success;
|
||||
if (success) {
|
||||
ev.currentTarget.classList.add('success')
|
||||
let total = parseInt(ev.currentTarget.closest('.vermine-roll-message').querySelector('#total').innerText) + reroll.total
|
||||
ev.currentTarget.closest('.vermine-roll-message').querySelector('#total').innerText = total
|
||||
}
|
||||
// Mise à jour de l'affichagedu message
|
||||
ev.currentTarget.classList.remove("rerollable")
|
||||
let content = ev.currentTarget.closest('div.message-content').outerHTML;
|
||||
console.log(reroll, message);
|
||||
|
||||
await message.update({
|
||||
content: content
|
||||
})
|
||||
// Update die display
|
||||
const result = reroll.dice[0]?.results[0]?.result ?? 0;
|
||||
const dieSpan = ev.currentTarget.querySelector('span');
|
||||
if (dieSpan) {
|
||||
dieSpan.innerText = result;
|
||||
}
|
||||
|
||||
// Update total if successful
|
||||
const success = reroll.dice[0]?.results[0]?.success;
|
||||
if (success) {
|
||||
ev.currentTarget.classList.add('success');
|
||||
const totalElement = rollMessage.querySelector('#total');
|
||||
if (totalElement) {
|
||||
const currentTotal = parseInt(totalElement.innerText, 10) || 0;
|
||||
totalElement.innerText = currentTotal + reroll.total;
|
||||
}
|
||||
}
|
||||
|
||||
// Update message content
|
||||
ev.currentTarget.classList.remove("rerollable");
|
||||
|
||||
const messageContent = ev.currentTarget.closest('div.message-content');
|
||||
if (messageContent) {
|
||||
const newRerollCount = parseInt(rerollCount, 10) - 1;
|
||||
rollMessage.querySelector('#allowed_reroll').innerText = newRerollCount;
|
||||
|
||||
await message.update({
|
||||
content: messageContent.outerHTML
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Méthode pour gérer les événements de chat
|
||||
* @param {HTMLElement} html - L'élément HTML contenant les événements de chat
|
||||
/**
|
||||
* Sets up event listeners for chat messages.
|
||||
* @param {HTMLElement} html - The HTML element containing chat events.
|
||||
*/
|
||||
static async chatListenners(html) {
|
||||
// Récupérer le nombre de relances autorisées
|
||||
let reroll = html.find('#allowed_reroll')[0]?.innerText;
|
||||
// Vérifier s'il n'y a pas de relances ou si le nombre est inférieur à 1
|
||||
if (!reroll || parseInt(reroll) < 1) {
|
||||
// Désactiver les relances pour chaque dé
|
||||
for (let die of html.find('.die')) {
|
||||
die.classList.remove("rerollable")
|
||||
};
|
||||
// Get reroll count
|
||||
const rerollCountElement = html.find('#allowed_reroll')[0];
|
||||
const rerollCount = rerollCountElement?.innerText;
|
||||
|
||||
// Enable/disable rerolls based on count
|
||||
if (!rerollCount || parseInt(rerollCount, 10) < 1) {
|
||||
// Disable rerolls for all dice
|
||||
html.find('.die').forEach(die => {
|
||||
die.classList.remove("rerollable");
|
||||
});
|
||||
} else {
|
||||
// Activer les relances pour chaque dé
|
||||
for (let die of html.find('.die')) {
|
||||
die.classList.add("rerollable")
|
||||
};
|
||||
// Enable rerolls for all dice
|
||||
html.find('.die').forEach(die => {
|
||||
die.classList.add("rerollable");
|
||||
});
|
||||
}
|
||||
|
||||
// Ajouter un événement de clic pour les dés pouvant être relancés
|
||||
// Add click event for rerollable dice
|
||||
html.find('.rerollable').click(async (ev) => {
|
||||
ev.preventDefault();
|
||||
// Récupérer l'ID du message
|
||||
let msgId = ev.currentTarget.closest("li.message").dataset.messageId;
|
||||
// Récupérer le message correspondant à l'ID
|
||||
let message = await game.messages.get(msgId);
|
||||
// Appeler la fonction onReroll de VermineUtils
|
||||
await VermineUtils.onReroll(message, ev);
|
||||
const msgId = ev.currentTarget.closest("li.message")?.dataset?.messageId;
|
||||
if (msgId) {
|
||||
const message = await game.messages.get(msgId);
|
||||
await VermineUtils.onReroll(message, ev);
|
||||
}
|
||||
});
|
||||
|
||||
// Mettre à jour l'étiquette en fonction de la valeur sélectionnée
|
||||
// Update granted reroll label
|
||||
html.find("#effort-reroll").change(ev => {
|
||||
let label = html.find("#granted-reroll")[0]
|
||||
label.innerText = ev.currentTarget.value
|
||||
const label = html.find("#granted-reroll")[0];
|
||||
if (label) {
|
||||
label.innerText = ev.currentTarget.value;
|
||||
}
|
||||
});
|
||||
|
||||
// Ajouter un événement de clic pour accorder une relance
|
||||
// Add click event for granting rerolls
|
||||
html.find("button.grant-reroll").click(async (ev) => {
|
||||
// Mettre à jour le nombre de relances autorisées
|
||||
html.find("#allowed_reroll")[0].innerText = html.find('#granted-reroll')[0].innerText
|
||||
let mesEl = ev.currentTarget.closest('[data-message-id]')
|
||||
let messageId = mesEl.dataset.messageId;
|
||||
// Quand relance accorder masquer la zone pour accorder les relances
|
||||
ev.currentTarget.closest('.reroll-from-effort').style.display = "none"
|
||||
let content = ev.currentTarget.closest(".vermine-roll-message").outerHTML;
|
||||
// Mettre à jour le contenu du message avec la relance accordée
|
||||
let message = await game.messages.get(messageId);
|
||||
await message.update({ content: content });
|
||||
const grantedRerollElement = html.find('#granted-reroll')[0];
|
||||
const allowedRerollElement = html.find("#allowed_reroll")[0];
|
||||
|
||||
if (grantedRerollElement && allowedRerollElement) {
|
||||
allowedRerollElement.innerText = grantedRerollElement.innerText;
|
||||
}
|
||||
|
||||
const mesEl = ev.currentTarget.closest('[data-message-id]');
|
||||
const messageId = mesEl?.dataset?.messageId;
|
||||
|
||||
if (messageId) {
|
||||
// Hide reroll grant area
|
||||
ev.currentTarget.closest('.reroll-from-effort').style.display = "none";
|
||||
|
||||
const rollMessage = ev.currentTarget.closest(".vermine-roll-message");
|
||||
if (rollMessage) {
|
||||
const content = rollMessage.outerHTML;
|
||||
const message = await game.messages.get(messageId);
|
||||
await message.update({ content: content });
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Méthode pour afficher les résultats des dés de manière graphique
|
||||
* @param {Roll} roll - Le jet de dés à afficher
|
||||
* @param {string} rollMode - Le mode d'affichage du jet de dés
|
||||
* Displays dice rolls in 3D if available.
|
||||
* @param {Roll} roll - The roll to display
|
||||
* @param {string} [rollMode] - The roll mode (uses game settings if not provided)
|
||||
* @returns {Promise<boolean>} Whether 3D dice were shown
|
||||
*/
|
||||
static async showDiceSoNice(roll, rollMode) {
|
||||
if (game.dice3d) {
|
||||
let whisper = null;
|
||||
let blind = false;
|
||||
rollMode = rollMode ?? game.settings.get("core", "rollMode");
|
||||
switch (rollMode) {
|
||||
case "blindroll": //GM only
|
||||
blind = true;
|
||||
case "gmroll": //GM + rolling player
|
||||
whisper = this.getUsers(user => user.isGM);
|
||||
break;
|
||||
case "roll": //everybody
|
||||
whisper = this.getUsers(user => user.active);
|
||||
break;
|
||||
case "selfroll":
|
||||
whisper = [game.user.id];
|
||||
break;
|
||||
}
|
||||
await game.dice3d.showForRoll(roll, game.user, true, whisper, blind);
|
||||
if (!game.dice3d) {
|
||||
return false;
|
||||
}
|
||||
else { return false }
|
||||
|
||||
rollMode = rollMode ?? game.settings.get("core", "rollMode");
|
||||
let whisper = null;
|
||||
let blind = false;
|
||||
|
||||
switch (rollMode) {
|
||||
case "blindroll": // GM only
|
||||
blind = true;
|
||||
// Falls through
|
||||
case "gmroll": // GM + rolling player
|
||||
whisper = this.getUsers(user => user.isGM);
|
||||
break;
|
||||
case "roll": // Everybody
|
||||
whisper = this.getUsers(user => user.active);
|
||||
break;
|
||||
case "selfroll":
|
||||
whisper = [game.user.id];
|
||||
break;
|
||||
}
|
||||
|
||||
await game.dice3d.showForRoll(roll, game.user, true, whisper, blind);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Méthode pour afficher un jet de dés dans le chat
|
||||
* @param {Roll} roll - Le jet de dés à afficher
|
||||
* @param {Object} param - Les paramètres du jet de dés
|
||||
* @returns {ChatMessage} - Le message affichant le jet de dés
|
||||
* Displays a dice roll in the chat.
|
||||
* @param {Roll} roll - The roll to display
|
||||
* @param {Object} param - Roll parameters
|
||||
* @returns {Promise<ChatMessage>} The created chat message
|
||||
*/
|
||||
static async diplayChatRoll(roll, param) {
|
||||
let content = await renderTemplate("systems/vermine2047/templates/roll-message.hbs", { roll, param })
|
||||
let chatData = {
|
||||
user: game.user._id,
|
||||
const content = await renderTemplate("systems/vermine2047/templates/roll-message.hbs", { roll, param });
|
||||
const chatData = {
|
||||
user: game.user?._id,
|
||||
speaker: ChatMessage.getSpeaker(),
|
||||
content: content,
|
||||
roll: roll
|
||||
};
|
||||
let msg = await ChatMessage.create(chatData);
|
||||
const msg = await ChatMessage.create(chatData);
|
||||
await msg.setFlag('world', 'roll', roll);
|
||||
return msg
|
||||
return msg;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user