foundryvtt-reve-de-dragon/module/rdd-resolution-table.js

372 lines
15 KiB
JavaScript
Raw Normal View History

import { ChatUtility } from "./chat-utility.js";
import { Misc } from "./misc.js";
2020-11-16 03:54:43 +01:00
import { RdDDice } from "./rdd-dice.js";
2020-11-12 16:35:51 +01:00
/**
* difficultés au delà de -10
*/
const levelDown = [
{ level: -11, score: 1, sign: 0, part: 0, epart: 2, etotal: 90 },
{ level: -12, score: 1, sign: 0, part: 0, epart: 2, etotal: 70 },
{ level: -13, score: 1, sign: 0, part: 0, epart: 2, etotal: 50 },
{ level: -14, score: 1, sign: 0, part: 0, epart: 2, etotal: 30 },
{ level: -15, score: 1, sign: 0, part: 0, epart: 2, etotal: 10 },
{ level: -16, score: 1, sign: 0, part: 0, epart: 0, etotal: 2 }
];
const levelImpossible = { score: 0, sign: 0, part: 0, epart: 0, etotal: 1 };
/**
* Table des résultats spéciaux - inutilisée, conservée si on veut afficher la table
*/
const specialResults = [
{ part: 0, epart: 0, etotal: 0, min: 0, max: 0 },
{ part: 1, epart: 81, etotal: 92, min: 1, max: 5 },
{ part: 2, epart: 82, etotal: 92, min: 6, max: 10 },
{ part: 3, epart: 83, etotal: 93, min: 11, max: 15 },
{ part: 4, epart: 84, etotal: 93, min: 16, max: 20 },
{ part: 5, epart: 85, etotal: 94, min: 21, max: 25 },
{ part: 6, epart: 86, etotal: 94, min: 26, max: 30 },
{ part: 7, epart: 87, etotal: 95, min: 31, max: 35 },
{ part: 8, epart: 88, etotal: 95, min: 36, max: 40 },
{ part: 9, epart: 89, etotal: 96, min: 41, max: 45 },
{ part: 10, epart: 90, etotal: 96, min: 46, max: 50 },
{ part: 11, epart: 91, etotal: 97, min: 51, max: 55 },
{ part: 12, epart: 92, etotal: 97, min: 56, max: 60 },
{ part: 13, epart: 93, etotal: 98, min: 61, max: 65 },
{ part: 14, epart: 94, etotal: 98, min: 65, max: 70 },
{ part: 15, epart: 95, etotal: 99, min: 71, max: 75 },
{ part: 16, epart: 96, etotal: 99, min: 76, max: 80 },
{ part: 17, epart: 97, etotal: 100, min: 81, max: 85 },
{ part: 18, epart: 98, etotal: 100, min: 86, max: 90 },
{ part: 19, epart: 99, etotal: 100, min: 81, max: 95 },
{ part: 20, epart: 100, etotal: 100, min: 96, max: 100 }
];
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) }
2020-11-12 16:35:51 +01:00
];
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 }
2020-12-05 00:04:40 +01:00
/* -------------------------------------------- */
const caracMaximumResolution = 60;
2020-12-05 00:04:40 +01:00
/* -------------------------------------------- */
export class RdDResolutionTable {
static resolutionTable = this.build()
/* -------------------------------------------- */
static build() {
let table = []
for (var caracValue = 0; caracValue <= caracMaximumResolution; caracValue++) {
table[caracValue] = this._computeRow(caracValue);
}
return table;
}
2020-11-15 16:38:45 +01:00
/* -------------------------------------------- */
2020-11-16 03:54:43 +01:00
static getResultat(code) {
2020-12-31 02:20:52 +01:00
let resultat = reussites.find(r => code == r.code);
2020-11-16 03:54:43 +01:00
if (resultat == undefined) {
resultat = reussites.find(r => r.code == "error");
}
return resultat;
}
2020-11-15 16:38:45 +01:00
2020-12-04 20:52:04 +01:00
/* -------------------------------------------- */
2020-11-16 03:54:43 +01:00
static explain(rolled) {
let message = "<br>Jet : <strong>" + rolled.roll + "</strong> sur " + rolled.score + "% ";
if (rolled.caracValue != null && rolled.finalLevel != null) {
message += (rolled.diviseurSignificative > 1 ? `(1/${rolled.diviseurSignificative} de ` : "(")
+ rolled.caracValue + " à " + Misc.toSignedString(rolled.finalLevel) + ") ";
2020-11-16 03:54:43 +01:00
}
message += '<strong>' + rolled.quality + '</strong>'
2020-11-16 03:54:43 +01:00
return message;
2020-12-18 23:48:41 +01:00
}
2020-11-16 03:54:43 +01:00
/* -------------------------------------------- */
static async displayRollData(rollData, actor = undefined, template = 'chat-resultat-general.html') {
ChatUtility.createChatWithRollMode(actor?.userName ?? game.user.name, {
content: await RdDResolutionTable.buildRollDataHtml(rollData, actor, template)
});
2020-12-31 02:08:58 +01:00
}
/* -------------------------------------------- */
static async buildRollDataHtml(rollData, actor, template = 'chat-resultat-general.html') {
rollData.show = rollData.show || {};
return await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/${template}`, rollData);
2020-11-12 16:35:51 +01:00
}
2020-12-04 20:52:04 +01:00
2020-11-15 16:38:45 +01:00
/* -------------------------------------------- */
static async rollData(rollData) {
rollData.rolled = await this.roll(rollData.caracValue, rollData.finalLevel, rollData.bonus, rollData.diviseurSignificative, rollData.showDice);
2020-12-17 00:44:32 +01:00
return rollData;
}
2020-12-08 23:07:41 +01:00
2020-12-04 20:52:04 +01:00
/* -------------------------------------------- */
static async roll(caracValue, finalLevel, bonus = undefined, diviseur = undefined, showDice = true) {
let chances = this.computeChances(caracValue, finalLevel);
this._updateChancesWithBonus(chances, bonus);
this._updateChancesFactor(chances, diviseur);
2020-12-12 23:31:19 +01:00
chances.showDice = showDice;
let rolled = await this.rollChances(chances, diviseur);
rolled.caracValue = caracValue;
2020-11-16 03:54:43 +01:00
rolled.finalLevel = finalLevel;
rolled.bonus = bonus;
rolled.factorHtml = Misc.getFractionHtml(diviseur);
2021-05-28 09:12:18 +02:00
rolled.niveauNecessaire = this.findNiveauNecessaire(caracValue, rolled.roll );
rolled.ajustementNecessaire = rolled.niveauNecessaire - finalLevel;
2020-11-16 03:54:43 +01:00
return rolled;
2020-11-15 16:38:45 +01:00
}
2020-11-12 16:35:51 +01:00
2021-05-28 09:12:18 +02:00
/* -------------------------------------------- */
static findNiveauNecessaire(caracValue, rollValue) {
for (let cell of this.resolutionTable[caracValue]) {
if ( rollValue <= cell.norm) {
return cell.niveau;
}
}
return 16; // Dummy default
}
/* -------------------------------------------- */
static _updateChancesFactor(chances, diviseur) {
if (chances.level > -11 && diviseur && diviseur > 1) {
let newScore = Math.floor(chances.score / diviseur);
mergeObject(chances, this._computeCell(null, newScore), { overwrite: true });
}
}
/* -------------------------------------------- */
static _updateChancesWithBonus(chances, bonus) {
if (bonus) {
2021-01-22 14:35:14 +01:00
let newScore = Number(chances.score) + bonus;
mergeObject(chances, this._computeCell(null, newScore), { overwrite: true });
}
}
2021-05-28 09:12:18 +02:00
/* -------------------------------------------- */
static significativeRequise(chances) {
2021-01-09 19:33:19 +01:00
chances.roll = Math.floor(chances.score / 2);
mergeObject(chances, reussites.find(x => x.code == 'sign'), { overwrite: true });
}
2021-05-28 09:12:18 +02:00
/* -------------------------------------------- */
static succesRequis(chances) {
chances.roll = chances.score;
mergeObject(chances, reussites.find(x => x.code == 'norm'), { overwrite: true });
}
2020-11-15 16:38:45 +01:00
/* -------------------------------------------- */
static async rollChances(chances, diviseur) {
2021-05-11 21:45:43 +02:00
chances.roll = await RdDDice.rollTotal("1d100", {showDice:chances.showDice});
mergeObject(chances, this.computeReussite(chances, chances.roll, diviseur), { overwrite: true });
2020-11-12 16:35:51 +01:00
return chances;
}
2020-11-15 16:38:45 +01:00
/* -------------------------------------------- */
static computeChances(caracValue, difficulte) {
2020-11-12 16:35:51 +01:00
if (difficulte < -16) {
return duplicate(levelImpossible);
}
if (difficulte < -10) {
return duplicate(levelDown.find(levelData => levelData.level == difficulte));
}
return duplicate(RdDResolutionTable.resolutionTable[caracValue][difficulte + 10]);
2020-11-12 16:35:51 +01:00
}
/* -------------------------------------------- */
2020-12-31 02:08:58 +01:00
static isAjustementAstrologique(rollData) {
if (rollData.selectedCarac?.label.toLowerCase().includes('chance')) {
2020-12-31 02:08:58 +01:00
return true;
}
if (rollData.selectedSort?.data.isrituel) {
2020-12-31 02:08:58 +01:00
return true;
}
return false;
}
2020-11-15 16:38:45 +01:00
/* -------------------------------------------- */
static isEchec(rollData) {
switch (rollData.surprise) {
case 'demi': return !rollData.rolled.isSign;
case 'totale': return true;
}
return rollData.rolled.isEchec;
}
2020-12-15 23:54:09 +01:00
/* -------------------------------------------- */
static isEchecTotal(rollData) {
if (rollData.arme && rollData.surprise == 'demi') {
return rollData.rolled.isEchec;
}
return rollData.rolled.isETotal;
}
2020-12-18 23:48:41 +01:00
2020-12-15 23:54:09 +01:00
/* -------------------------------------------- */
static isParticuliere(rollData) {
if (rollData.arme && rollData.surprise) {
return false;
}
return rollData.rolled.isPart;
}
2020-12-15 23:54:09 +01:00
/* -------------------------------------------- */
static isReussite(rollData) {
switch (rollData.surprise) {
case 'demi': return rollData.rolled.isSign;
case 'totale': return false;
}
return rollData.rolled.isSuccess;
2020-11-12 16:35:51 +01:00
}
/* -------------------------------------------- */
static computeReussite(chances, roll, diviseur) {
const reussite = reussites.find(x => x.condition(chances, roll));
if (diviseur > 1 && reussite.code == 'norm') {
return reussiteInsuffisante;
}
return reussite;
2020-11-12 16:35:51 +01:00
}
/* -------------------------------------------- */
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, Math.max(Math.floor(caracValue * (diff + 10) / 2), 1));
}
return dataRow;
}
/* -------------------------------------------- */
2020-11-12 16:35:51 +01:00
static _computeCell(niveau, percentage) {
return {
2020-11-12 16:35:51 +01:00
niveau: niveau,
score: percentage,
norm: Math.min(99, percentage),
2020-11-12 16:35:51 +01:00
sign: this._reussiteSignificative(percentage),
part: this._reussitePart(percentage),
epart: this._echecParticulier(percentage),
etotal: this._echecTotal(percentage)
};
}
/* -------------------------------------------- */
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);
}
2020-12-15 23:54:09 +01:00
/* -------------------------------------------- */
2020-12-18 23:48:41 +01:00
static buildHTMLResults(caracValue, levelValue) {
if (caracValue == undefined || isNaN(caracValue)) caracValue = 10;
if (levelValue == undefined || isNaN(levelValue)) levelValue = 0;
2020-12-18 23:48:41 +01:00
let cell = this.computeChances(caracValue, levelValue);
cell.epart = cell.epart > 99 ? 'N/A' : cell.epart;
cell.etotal = cell.etotal > 100 ? 'N/A' : cell.etotal;
2021-01-02 19:27:15 +01:00
cell.score = Math.min(cell.score, 99);
return `
<span class="table-proba-reussite competence-label">
Particulière: <span class="rdd-roll-part">${cell.part}</span>
- Significative: <span class="rdd-roll-sign">${cell.sign}</span>
- Réussite: <span class="rdd-roll-norm">${cell.score}</span>
- Echec Particulier: <span class="rdd-roll-epart">${cell.epart}</span>
- Echec Total: <span class="rdd-roll-etotal">${cell.etotal}</span>
</span>
`
2020-12-15 23:54:09 +01:00
}
/* -------------------------------------------- */
static buildHTMLTableExtract(caracValue, levelValue) {
return this.buildHTMLTable(caracValue, levelValue, caracValue - 2, caracValue + 2, levelValue - 5, levelValue + 5)
}
static buildHTMLTable(caracValue, levelValue, minCarac = 1, maxCarac = 21, minLevel = -10, maxLevel = 11) {
return this._buildHTMLTable(caracValue, levelValue, minCarac, maxCarac, minLevel, maxLevel)
}
/* -------------------------------------------- */
static _buildHTMLTable(caracValue, levelValue, minCarac, maxCarac, minLevel, maxLevel) {
let countColonnes = maxLevel - minLevel;
minCarac = Math.max(minCarac, 1);
maxCarac = Math.min(maxCarac, caracMaximumResolution);
minLevel = Math.max(minLevel, -10);
maxLevel = Math.max(Math.min(maxLevel, 22), minLevel + countColonnes);
let table = $("<table class='table-resolution'/>")
.append(this._buildHTMLHeader(RdDResolutionTable.resolutionTable[0], minLevel, maxLevel));
for (var rowIndex = minCarac; rowIndex <= maxCarac; rowIndex++) {
table.append(this._buildHTMLRow(RdDResolutionTable.resolutionTable[rowIndex], rowIndex, caracValue, levelValue, minLevel, maxLevel));
}
2020-12-15 23:37:35 +01:00
table.append("</table>");
return table;
}
2020-12-04 20:52:04 +01:00
/* -------------------------------------------- */
static _buildHTMLHeader(dataRow, minLevel, maxLevel) {
let tr = $("<tr/>");
if (minLevel > -8) {
tr.append($("<th class='table-resolution-level'/>").text("-8"))
}
if (minLevel > -7) {
tr.append($("<th class='table-resolution-level'/>").text("..."));
}
for (let difficulte = minLevel; difficulte <= maxLevel; difficulte++) {
tr.append($("<th class='table-resolution-level'/>").text(Misc.toSignedString(difficulte)));
}
return tr;
}
2020-12-04 20:52:04 +01:00
/* -------------------------------------------- */
static _buildHTMLRow(dataRow, rowIndex, caracValue, levelValue, minLevel, maxLevel) {
let tr = $("<tr/>");
let max = maxLevel;
if (minLevel > -8) {
let score = dataRow[-8 + 10].score;
tr.append($("<td class='table-resolution-carac'/>").text(score))
}
if (minLevel > -7) {
tr.append($("<td/>"))
}
for (let difficulte = minLevel; difficulte <= max; difficulte++) {
let td = $("<td/>");
let score = dataRow[difficulte + 10].score;
if (rowIndex == caracValue && levelValue == difficulte) {
td.addClass('table-resolution-target');
} else if (difficulte == -8) {
td.addClass('table-resolution-carac');
}
tr.append(td.text(score));
}
return tr;
}
}