fix: migrate combat system from d6 to d10 to match official rules
- Change all dice rolls in fight.mjs from d6 to d10 - Implement success counting system (dice >= difficulty) as per Vermine2047 rules - Add d10 success class to dice that meet or exceed difficulty - Display success count in confrontation UI - Update chat message handler to count successes instead of summing dice - Add comprehensive JSDoc documentation to performTest method - Add missing French translations for fight tool terms This corrects a critical inconsistency where fight.mjs was using d6 while the official Vermine2047 rules and the rest of the system (roll.mjs) use d10 with success counting. Compatibility: FoundryVTT v11-v14 Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
@@ -130,6 +130,14 @@
|
|||||||
"error_no_rerolls_left": "Plus de relances possibles",
|
"error_no_rerolls_left": "Plus de relances possibles",
|
||||||
"error_select_ability": "Veuillez sélectionner une caractéristique",
|
"error_select_ability": "Veuillez sélectionner une caractéristique",
|
||||||
"error_not_enough_self_control": "Pas assez de points de sang-froid",
|
"error_not_enough_self_control": "Pas assez de points de sang-froid",
|
||||||
|
"FightTool": "Outil de combat",
|
||||||
|
"Roll4Fight": "Lancer pour le combat",
|
||||||
|
"Selected": "Sélectionné",
|
||||||
|
"Achievement": "Accomplissement",
|
||||||
|
"Conservation": "Conservation",
|
||||||
|
"ConfrontationHint": "Résolvez la confrontation en sélectionnant des dés",
|
||||||
|
"PurposeTrait": "Trait de but",
|
||||||
|
"SpleenTrait": "Trait de rate",
|
||||||
"group": "Groupe",
|
"group": "Groupe",
|
||||||
"abilities": "Caractéristiques",
|
"abilities": "Caractéristiques",
|
||||||
"ability": "Caractéristique",
|
"ability": "Caractéristique",
|
||||||
|
|||||||
+70
-18
@@ -1,11 +1,36 @@
|
|||||||
import { VERMINE } from "./config.mjs";
|
import { VERMINE } from "./config.mjs";
|
||||||
import { getActorSkillScore, updateActorSkillScore } from "./functions.mjs";
|
import { getActorSkillScore, updateActorSkillScore } from "./functions.mjs";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles combat-related dice rolls for Vermine2047.
|
||||||
|
* Uses d10-based system with success counting as per official rules.
|
||||||
|
*/
|
||||||
export class VermineFight {
|
export class VermineFight {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a d10-based test according to Vermine2047 official rules.
|
||||||
|
* Each die result is compared to a difficulty threshold.
|
||||||
|
* Each result >= difficulty counts as 1 Success.
|
||||||
|
*
|
||||||
|
* @param {number} enemyAchievement - Opponent's achievement score
|
||||||
|
* @param {number} enemyConservation - Opponent's conservation score
|
||||||
|
* @param {string} skillKey - The skill key being tested
|
||||||
|
* @param {number} skill - The skill value
|
||||||
|
* @param {Object} params - Additional test parameters
|
||||||
|
* @param {number} [params.difficulty=7] - Difficulty threshold (3-10)
|
||||||
|
* @param {boolean} [params.spleen] - Whether to use spleen rule
|
||||||
|
* @param {boolean} [params.purpose] - Whether to use purpose rule
|
||||||
|
* @param {number} [params.usure] - Wear/usage modifier
|
||||||
|
* @param {number} [params.trait] - Trait bonus
|
||||||
|
* @param {boolean} [params.specialization] - Whether specialization applies
|
||||||
|
* @param {Actor} actor - The actor performing the test
|
||||||
|
* @returns {Promise<Object>} Roll data including successes count
|
||||||
|
*/
|
||||||
async performTest(enemyAchievement, enemyConservation, skillKey, skill, params, actor) {
|
async performTest(enemyAchievement, enemyConservation, skillKey, skill, params, actor) {
|
||||||
|
// Use d10 as per Vermine2047 official rules
|
||||||
const dicePool = (params.spleen != undefined || params.purpose != undefined) ? '5' : '4';
|
const dicePool = (params.spleen != undefined || params.purpose != undefined) ? '5' : '4';
|
||||||
const r = new Roll(dicePool + `d6`);
|
const difficulty = params.difficulty || 7; // Default difficulty
|
||||||
|
const r = new Roll(dicePool + `d10`);
|
||||||
let diceString = '';
|
let diceString = '';
|
||||||
let dicePoolHint = '';
|
let dicePoolHint = '';
|
||||||
let discardedRoll = false;
|
let discardedRoll = false;
|
||||||
@@ -47,9 +72,14 @@ export class VermineFight {
|
|||||||
}
|
}
|
||||||
const discardedRollText = (discardedRoll.result != undefined) ? '<div class="discarded-roll">' + discardedRoll.result + '</div>' : "";
|
const discardedRollText = (discardedRoll.result != undefined) ? '<div class="discarded-roll">' + discardedRoll.result + '</div>' : "";
|
||||||
|
|
||||||
|
// Count successes (dice >= difficulty) as per Vermine2047 rules
|
||||||
|
let successes = 0;
|
||||||
for (let i = 0; i < r.terms[0].results.length; i++) {
|
for (let i = 0; i < r.terms[0].results.length; i++) {
|
||||||
let result = r.terms[0].results[i].result;
|
let result = r.terms[0].results[i].result;
|
||||||
diceString += '<li class="roll die d6 die-' + i + '">' + result + '</li>';
|
if (result >= difficulty) {
|
||||||
|
successes++;
|
||||||
|
}
|
||||||
|
diceString += '<li class="roll die d10 die-' + i + (result >= difficulty ? ' success' : '') + '">' + result + '</li>';
|
||||||
}
|
}
|
||||||
|
|
||||||
let hintText = game.i18n.format('VERMINE.ConfrontationHint');
|
let hintText = game.i18n.format('VERMINE.ConfrontationHint');
|
||||||
@@ -60,7 +90,7 @@ export class VermineFight {
|
|||||||
<div class="dice-roll">
|
<div class="dice-roll">
|
||||||
<div class="dice-result">
|
<div class="dice-result">
|
||||||
<div class="dice-formula">
|
<div class="dice-formula">
|
||||||
` + dicePool + `d6 ` + dicePoolHint + `
|
` + dicePool + `d10 ` + dicePoolHint + `
|
||||||
</div>
|
</div>
|
||||||
<div class="dice-tooltip expanded">
|
<div class="dice-tooltip expanded">
|
||||||
<section class="tooltip-part">
|
<section class="tooltip-part">
|
||||||
@@ -79,7 +109,10 @@ export class VermineFight {
|
|||||||
<a class="inline-block button add-to-conservation">` + game.i18n.format('VERMINE.Conservation') + `</a>
|
<a class="inline-block button add-to-conservation">` + game.i18n.format('VERMINE.Conservation') + `</a>
|
||||||
<a class="inline-block button reset"><i class="fa-solid fa-rotate-right"></i></a>
|
<a class="inline-block button reset"><i class="fa-solid fa-rotate-right"></i></a>
|
||||||
<a class="inline-block button resolve"><i class="fa-solid fa-check"></i></a>
|
<a class="inline-block button resolve"><i class="fa-solid fa-check"></i></a>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="success-count">
|
||||||
|
<strong>` + game.i18n.format('VERMINE.success_count') + `:</strong> <span class="success-value">` + successes + `</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -95,7 +128,14 @@ export class VermineFight {
|
|||||||
this.sendToChat(html, r, actor);
|
this.sendToChat(html, r, actor);
|
||||||
};
|
};
|
||||||
|
|
||||||
// on fait les comptes
|
// Return roll data for further processing with d10 success counting
|
||||||
|
return {
|
||||||
|
roll: r,
|
||||||
|
successes: successes,
|
||||||
|
difficulty: difficulty,
|
||||||
|
dicePool: parseInt(dicePool, 10),
|
||||||
|
total: r.total
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,42 +181,54 @@ export class VermineFight {
|
|||||||
// console.log("accès au fin du fin", message._id);
|
// console.log("accès au fin du fin", message._id);
|
||||||
|
|
||||||
// sélection du dé actif
|
// sélection du dé actif
|
||||||
html.on("click", '.confrontation .die.d6', event => {
|
html.on("click", '.confrontation .die.d10', event => {
|
||||||
const diceResult = parseInt($(event.target).html(), 10);
|
const diceResult = parseInt($(event.target).html(), 10);
|
||||||
html.find('.confrontation .die.d6').removeClass('active');
|
html.find('.confrontation .die.d10').removeClass('active');
|
||||||
$(event.target).addClass('active');
|
$(event.target).addClass('active');
|
||||||
});
|
});
|
||||||
|
|
||||||
// sélection des dés d'accomplissement
|
// sélection des dés d'accomplissement (achievement = successes)
|
||||||
html.on("click", '.confrontation .add-to-achievement', event => {
|
html.on("click", '.confrontation .add-to-achievement', event => {
|
||||||
const diceResult = parseInt(html.find('.confrontation .die.d6.active').html(), 10);
|
const diceResult = parseInt(html.find('.confrontation .die.d10.active').html(), 10);
|
||||||
html.find('.confrontation .die.d6.active').removeClass('min').addClass('max');
|
html.find('.confrontation .die.d10.active').removeClass('min').addClass('max');
|
||||||
});
|
});
|
||||||
|
|
||||||
// sélection des dés de conservation
|
// sélection des dés de conservation
|
||||||
html.on("click", '.confrontation .add-to-conservation', event => {
|
html.on("click", '.confrontation .add-to-conservation', event => {
|
||||||
const diceResult = parseInt(html.find('.confrontation .die.d6.active').html(), 10);
|
const diceResult = parseInt(html.find('.confrontation .die.d10.active').html(), 10);
|
||||||
html.find('.confrontation .die.d6.active').removeClass('max').addClass('min');
|
html.find('.confrontation .die.d10.active').removeClass('max').addClass('min');
|
||||||
});
|
});
|
||||||
|
|
||||||
// reset de la sélection des pools
|
// reset de la sélection des pools
|
||||||
html.on("click", '.confrontation .reset', event => {
|
html.on("click", '.confrontation .reset', event => {
|
||||||
html.find('.confrontation .die.d6')
|
html.find('.confrontation .die.d10')
|
||||||
.removeClass('max')
|
.removeClass('max')
|
||||||
.removeClass('min');
|
.removeClass('min');
|
||||||
});
|
});
|
||||||
|
|
||||||
// résolution de la confrontation
|
// résolution de la confrontation
|
||||||
|
// Note: With d10 system, we count successes (dice >= difficulty) instead of summing
|
||||||
html.on("click", '.confrontation .resolve', async event => {
|
html.on("click", '.confrontation .resolve', async event => {
|
||||||
let achievementDice = 0;
|
let achievementDice = 0;
|
||||||
let conservationDice = 0;
|
let conservationDice = 0;
|
||||||
let achievementBasis = 0;
|
let achievementBasis = 0;
|
||||||
let conservationBasis = 0;
|
let conservationBasis = 0;
|
||||||
html.find('.confrontation .die.d6.max').each(function (index) {
|
const difficulty = 7; // Default difficulty for legacy compatibility
|
||||||
achievementDice += parseInt($(this).html(), 10);
|
|
||||||
|
// Count successes (dice >= difficulty) for achievement
|
||||||
|
html.find('.confrontation .die.d10.max').each(function (index) {
|
||||||
|
const value = parseInt($(this).html(), 10);
|
||||||
|
if (value >= difficulty) {
|
||||||
|
achievementDice++;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
html.find('.confrontation .die.d6.min').each(function (index) {
|
|
||||||
conservationDice += parseInt($(this).html(), 10);
|
// Count successes (dice >= difficulty) for conservation
|
||||||
|
html.find('.confrontation .die.d10.min').each(function (index) {
|
||||||
|
const value = parseInt($(this).html(), 10);
|
||||||
|
if (value >= difficulty) {
|
||||||
|
conservationDice++;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// saisie des résultats
|
// saisie des résultats
|
||||||
@@ -188,7 +240,7 @@ export class VermineFight {
|
|||||||
html.find('td.conservation-result').data('conservation-value', conservationDice);
|
html.find('td.conservation-result').data('conservation-value', conservationDice);
|
||||||
html.find('td.conservation-result').html(conservationBasis + conservationDice);
|
html.find('td.conservation-result').html(conservationBasis + conservationDice);
|
||||||
|
|
||||||
// calcul des marges
|
// calcul des marges (now based on successes, not sum)
|
||||||
const achievementMargin = achievementBasis + achievementDice - parseInt(html.find('td.adv-achievement-result').html(), 10);
|
const achievementMargin = achievementBasis + achievementDice - parseInt(html.find('td.adv-achievement-result').html(), 10);
|
||||||
const conservationMargin = conservationBasis + conservationDice - parseInt(html.find('td.adv-conservation-result').html(), 10);
|
const conservationMargin = conservationBasis + conservationDice - parseInt(html.find('td.adv-conservation-result').html(), 10);
|
||||||
html.find('td.achievement-margin').html(achievementMargin);
|
html.find('td.achievement-margin').html(achievementMargin);
|
||||||
|
|||||||
Reference in New Issue
Block a user