Vincent Vandemeulebrouck
28878b74fc
Les demandes de résistance pour les possessions sont envoyées au défenseur Les demlandes de défense contre une empoignade dont envoyées au défenseur
295 lines
12 KiB
JavaScript
295 lines
12 KiB
JavaScript
import { ChatUtility } from "./chat-utility.js";
|
|
import { Misc } from "./misc.js";
|
|
import { RdDDice } from "./rdd-dice.js";
|
|
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
|
|
|
|
/**
|
|
* difficultés au delà de -10
|
|
*/
|
|
const levelDown = [
|
|
{ level: -11, score: 1, norm: 1, sign: 0, part: 0, epart: 2, etotal: 90 },
|
|
{ level: -12, score: 1, norm: 1, sign: 0, part: 0, epart: 2, etotal: 70 },
|
|
{ level: -13, score: 1, norm: 1, sign: 0, part: 0, epart: 2, etotal: 50 },
|
|
{ level: -14, score: 1, norm: 1, sign: 0, part: 0, epart: 2, etotal: 30 },
|
|
{ level: -15, score: 1, norm: 1, sign: 0, part: 0, epart: 2, etotal: 10 },
|
|
{ level: -16, score: 1, norm: 1, sign: 0, part: 0, epart: 0, etotal: 2 }
|
|
];
|
|
const levelImpossible = { score: 0, norm: 0, sign: 0, part: 0, epart: 0, etotal: 1 };
|
|
|
|
const reussites = [
|
|
{ code: "etotal", isPart: false, isSign: false, isSuccess: false, isEchec: true, isEPart: true, isETotal: true, ptTache: -4, ptQualite: -6, quality: "Echec total", condition: (target, roll) => roll >= target.etotal && roll <= 100 },
|
|
{ code: "epart", isPart: false, isSign: false, isSuccess: false, isEchec: true, isEPart: true, isETotal: false, ptTache: -2, ptQualite: -4, quality: "Echec particulier", condition: (target, roll) => (roll >= target.epart && roll < target.etotal) },
|
|
{ code: "echec", isPart: false, isSign: false, isSuccess: false, isEchec: true, isEPart: false, isETotal: false, ptTache: 0, ptQualite: -2, quality: "Echec normal", condition: (target, roll) => (roll > target.norm && roll < target.etotal) },
|
|
{ code: "norm", isPart: false, isSign: false, isSuccess: true, isEchec: false, isEPart: false, isETotal: false, ptTache: 1, ptQualite: 0, quality: "Réussite normale", condition: (target, roll) => (roll > target.sign && roll <= target.norm) },
|
|
{ code: "sign", isPart: false, isSign: true, isSuccess: true, isEchec: false, isEPart: false, isETotal: false, ptTache: 2, ptQualite: 1, quality: "Réussite significative", condition: (target, roll) => (roll > target.part && roll <= target.sign) },
|
|
{ code: "part", isPart: true, isSign: true, isSuccess: true, isEchec: false, isEPart: false, isETotal: false, ptTache: 3, ptQualite: 2, quality: "Réussite Particulière!", condition: (target, roll) => (roll > 0 && roll <= target.part) },
|
|
{ code: "error", isPart: false, isSign: false, isSuccess: false, isEchec: true, isEPart: true, isETotal: true, ptTache: 0, ptQualite: 0, quality: "Jet de dés invalide", condition: (target, roll) => (roll <= 0 || roll > 100) }
|
|
];
|
|
|
|
const reussiteInsuffisante = { code: "notSign", isPart: false, isSign: false, isSuccess: false, isEchec: true, isEPart: false, isETotal: false, ptTache: 0, ptQualite: -2, quality: "Réussite insuffisante", condition: (target, roll) => false }
|
|
/* -------------------------------------------- */
|
|
const CARAC_MAXIMUM_RESOLUTION = 40;
|
|
/* -------------------------------------------- */
|
|
export class RdDResolutionTable {
|
|
static resolutionTable = this.build()
|
|
|
|
/* -------------------------------------------- */
|
|
static build() {
|
|
let table = []
|
|
for (var caracValue = 0; caracValue <= CARAC_MAXIMUM_RESOLUTION; caracValue++) {
|
|
table[caracValue] = this._computeRow(caracValue);
|
|
}
|
|
return table;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static computeChances(carac, level) {
|
|
if (level < -16) {
|
|
return levelImpossible;
|
|
}
|
|
if (level < -10) {
|
|
return levelDown.find(it => it.level == level);
|
|
}
|
|
const percentage = RdDResolutionTable.computePercentage(carac, level);
|
|
return this._computeCell(level, percentage);
|
|
}
|
|
|
|
|
|
/* -------------------------------------------- */
|
|
static _computeRow(caracValue) {
|
|
let dataRow = [
|
|
this._computeCell(-10, Math.max(Math.floor(caracValue / 4), 1)),
|
|
this._computeCell(-9, Math.max(Math.floor(caracValue / 2), 1))
|
|
]
|
|
for (var diff = -8; diff <= 22; diff++) {
|
|
dataRow[diff + 10] = this._computeCell(diff, RdDResolutionTable.computePercentage(caracValue, diff));
|
|
}
|
|
return dataRow;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static _computeCell(niveau, percentage) {
|
|
return {
|
|
niveau: niveau,
|
|
score: percentage,
|
|
norm: Math.min(99, percentage),
|
|
sign: this._reussiteSignificative(percentage),
|
|
part: this._reussitePart(percentage),
|
|
epart: this._echecParticulier(percentage),
|
|
etotal: this._echecTotal(percentage)
|
|
};
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static getResultat(code) {
|
|
let resultat = reussites.find(r => code == r.code);
|
|
if (resultat == undefined) {
|
|
resultat = reussites.find(r => r.code == "error");
|
|
}
|
|
return resultat;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static async displayRollData(rollData, actor = undefined, template = 'chat-resultat-general.html') {
|
|
return await ChatUtility.createChatWithRollMode(RdDResolutionTable.actorChatName(actor), {
|
|
content: await RdDResolutionTable.buildRollDataHtml(rollData, template)
|
|
});
|
|
}
|
|
|
|
static actorChatName(actor) {
|
|
return actor?.name ?? game.user.name;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static async buildRollDataHtml(rollData, template = 'chat-resultat-general.html') {
|
|
rollData.show = rollData.show || {};
|
|
return await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/${template}`, rollData);
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static async rollData(rollData) {
|
|
rollData.rolled = await this.roll(rollData.caracValue, rollData.finalLevel, rollData);
|
|
return rollData;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static async roll(caracValue, finalLevel, rollData = {}) {
|
|
let chances = foundry.utils.duplicate(this.computeChances(caracValue, finalLevel));
|
|
this._updateChancesWithBonus(chances, rollData.bonus, finalLevel);
|
|
this._updateChancesFactor(chances, rollData.diviseurSignificative);
|
|
chances.showDice = rollData.showDice;
|
|
chances.rollMode = rollData.rollMode;
|
|
|
|
let rolled = await this.rollChances(chances, rollData.diviseurSignificative, rollData.forceDiceResult);
|
|
rolled.caracValue = caracValue;
|
|
rolled.finalLevel = finalLevel;
|
|
rolled.bonus = rollData.bonus;
|
|
rolled.factorHtml = Misc.getFractionHtml(rollData.diviseurSignificative);
|
|
|
|
if (ReglesOptionnelles.isUsing("afficher-colonnes-reussite")) {
|
|
rolled.niveauNecessaire = this.findNiveauNecessaire(caracValue, rolled.roll);
|
|
rolled.ajustementNecessaire = rolled.niveauNecessaire - finalLevel;
|
|
}
|
|
return rolled;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static findNiveauNecessaire(carac, rolled) {
|
|
if (carac == 0) {
|
|
return NaN;
|
|
}
|
|
if (rolled >= carac){
|
|
const upper = Math.ceil(rolled/carac);
|
|
return 2*upper -10
|
|
}
|
|
if (rolled > Math.floor(carac/2)) {
|
|
return -8
|
|
}
|
|
if (rolled > Math.floor(carac/4)) {
|
|
return -9
|
|
}
|
|
if (rolled > 1) {
|
|
return -10
|
|
}
|
|
return -11;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static _updateChancesFactor(chances, diviseur) {
|
|
if (chances.level > -11 && diviseur && diviseur > 1) {
|
|
let newScore = Math.floor(chances.score / diviseur);
|
|
foundry.utils.mergeObject(chances, this._computeCell(undefined, newScore), { overwrite: true });
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static _updateChancesWithBonus(chances, bonus, finalLevel) {
|
|
if (bonus && finalLevel > -11) {
|
|
let newScore = Number(chances.score) + bonus;
|
|
foundry.utils.mergeObject(chances, this._computeCell(undefined, newScore), { overwrite: true });
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static significativeRequise(chances) {
|
|
chances.roll = Math.floor(chances.score / 2);
|
|
foundry.utils.mergeObject(chances, reussites.find(x => x.code == 'sign'), { overwrite: true });
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static succesRequis(chances) {
|
|
chances.roll = chances.score;
|
|
foundry.utils.mergeObject(chances, reussites.find(x => x.code == 'norm'), { overwrite: true });
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static async rollChances(chances, diviseur, forceDiceResult = -1) {
|
|
chances.forceDiceResult = forceDiceResult <= 0 || forceDiceResult > 100 ? undefined : { total: forceDiceResult };
|
|
chances.roll = await RdDDice.rollTotal("1d100", chances);
|
|
foundry.utils.mergeObject(chances, this.computeReussite(chances, chances.roll, diviseur), { overwrite: true });
|
|
return chances;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static computePercentage(carac, diff) {
|
|
if (diff < -16) return 0
|
|
if (diff < -10) return 1
|
|
if (diff == -10) return Math.max(Math.floor(carac / 4), 1)
|
|
if (diff == -9) return Math.max(Math.floor(carac / 2), 1)
|
|
return Math.max(Math.floor(carac * (diff + 10) / 2), 1);
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static isEchec(rollData) {
|
|
switch (rollData.surprise) {
|
|
case 'demi': return !rollData.rolled.isSign;
|
|
case 'totale': return true;
|
|
}
|
|
return rollData.rolled.isEchec;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static isEchecTotal(rollData) {
|
|
if (rollData.arme && rollData.surprise == 'demi') {
|
|
return rollData.rolled.isEchec;
|
|
}
|
|
return rollData.rolled.isETotal;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static isParticuliere(rollData) {
|
|
if (rollData.arme && rollData.surprise) {
|
|
return false;
|
|
}
|
|
return rollData.rolled.isPart;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static isReussite(rollData) {
|
|
switch (rollData.surprise) {
|
|
case 'demi': return rollData.rolled.isSign;
|
|
case 'totale': return false;
|
|
}
|
|
return rollData.rolled.isSuccess;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static computeReussite(chances, roll, diviseur) {
|
|
const reussite = reussites.find(x => x.condition(chances, roll));
|
|
if (diviseur > 1 && reussite.code == 'norm') {
|
|
return reussiteInsuffisante;
|
|
}
|
|
return reussite;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static _reussiteSignificative(percentage) {
|
|
return Math.floor(percentage / 2);
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static _reussitePart(percentage) {
|
|
return Math.ceil(percentage / 5);
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static _echecParticulier(percentage) {
|
|
const epart = Math.ceil(percentage / 5) + 80;
|
|
return epart >= 100 ? 101 : epart;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static _echecTotal(percentage) {
|
|
const etotal = Math.ceil(percentage / 10) + 91;
|
|
return percentage >= 100 ? 101 : Math.min(etotal, 100);
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static subTable(carac, level, delta = { carac: 2, level: 5}) {
|
|
return {
|
|
carac,
|
|
level,
|
|
minCarac: carac - (delta?.carac ?? 2),
|
|
maxCarac: carac + (delta?.carac ?? 2),
|
|
minLevel: level - (delta?.level ?? 5),
|
|
maxLevel: level + (delta?.level ?? 5)
|
|
};
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static async buildHTMLTable({ carac: carac, level: level, minCarac = 1, maxCarac = 21, minLevel = -10, maxLevel = 11 }) {
|
|
let colonnes = maxLevel - minLevel;
|
|
minCarac = Math.max(minCarac, 1);
|
|
maxCarac = Math.min(maxCarac, minCarac + 20);
|
|
minLevel = Math.max(minLevel, -10);
|
|
maxLevel = Math.max(Math.min(maxLevel, 30), minLevel + colonnes);
|
|
return await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/resolution-table.html', {
|
|
carac: carac,
|
|
difficulte: level,
|
|
min: minLevel,
|
|
rows: Misc.intArray(minCarac, maxCarac+1),
|
|
cols: Misc.intArray(minLevel, maxLevel+1)
|
|
});
|
|
}
|
|
|
|
} |