389 lines
15 KiB
JavaScript
389 lines
15 KiB
JavaScript
import { Misc } from "./misc.js";
|
|
import { RdDDice } from "./rdd-dice.js";
|
|
|
|
/**
|
|
* 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.score && 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.score) },
|
|
{ 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 reussiteSignificative = reussites.find(r => r.code == "sign");
|
|
const reussiteNormale = reussites.find(r => r.code == "norm");
|
|
const echecNormal = reussites.find(r => r.code == "echec");
|
|
const caracMaximumResolution = 60;
|
|
/* -------------------------------------------- */
|
|
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;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static getResultat(code) {
|
|
let resultat = reussites.find(r => code == r.code);
|
|
if (resultat == undefined) {
|
|
resultat = reussites.find(r => r.code == "error");
|
|
}
|
|
return resultat;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static explain(rolled) {
|
|
let message = "<br>Jet : <strong>" + rolled.roll + "</strong> sur " + rolled.score + "% ";
|
|
if (rolled.caracValue != null && rolled.finalLevel != null) {
|
|
message += (rolled.needSignificative ? "(significative sur " : "(")
|
|
+ rolled.caracValue + " à " + Misc.toSignedString(rolled.finalLevel) + ") ";
|
|
}
|
|
message += '<strong>' + rolled.quality + '</strong>'
|
|
return message;
|
|
}
|
|
|
|
static async explainRollDataV2(rollData, template = 'chat-resultat-rdd.html') {
|
|
rollData.ajustements = RdDResolutionTable._buildAjustements(rollData);
|
|
rollData.show = rollData.show || {};
|
|
|
|
let html = await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/${template}`, rollData);
|
|
return html;
|
|
}
|
|
|
|
static _buildAjustements(rollData) {
|
|
let list = [];
|
|
if (rollData.competence) {
|
|
list.push({ label: rollData.competence.name, value: rollData.competence.data.niveau});
|
|
}
|
|
if (rollData.diffLibre != undefined) {
|
|
const label = rollData.selectedSort ? rollData.selectedSort.name : 'libre';
|
|
list.push({ label: label, value: rollData.diffLibre });
|
|
}
|
|
if (rollData.diffConditions != undefined) {
|
|
list.push({ label: 'conditions', value: rollData.diffConditions });
|
|
}
|
|
if (rollData.etat != undefined) {
|
|
list.push({ label: 'état', value: rollData.etat });
|
|
}
|
|
if (rollData.selectedCarac != undefined && rollData.moral != undefined && rollData.selectedCarac.label == 'Volonté') {
|
|
list.push({ label: 'moral', value: rollData.selectedCarac != undefined && rollData.moral != undefined && rollData.selectedCarac.label == 'Volonté' ? rollData.moral : undefined });
|
|
}
|
|
if (RdDResolutionTable.isAjustementAstrologique(rollData)) {
|
|
list.push({ label: 'astrologique', value: rollData.ajustementAstrologique||0 });
|
|
}
|
|
if (rollData.rolled.bonus && rollData.selectedSort) {
|
|
list.push({ label: 'Bonus de case', value: rollData.rolled.bonus, unit: '%' });
|
|
}
|
|
return list;
|
|
}
|
|
|
|
static explainRollData(rollData) {
|
|
let parts = RdDResolutionTable._buildAjustementsList(rollData);
|
|
let message = parts.length > 0 ? "<br>Difficulté " + parts.reduce((a, b) => a + ' / ' + b) : "";
|
|
|
|
return message+ RdDResolutionTable.explain(rollData.rolled)
|
|
}
|
|
|
|
|
|
static _buildAjustementsList(rollData) {
|
|
let parts = [];
|
|
if (rollData.diffLibre != undefined) {
|
|
parts.push(`<strong>libre: ${rollData.diffLibre}</strong>`);
|
|
}
|
|
if (rollData.diffConditions != undefined) {
|
|
parts.push(`conditions: ${Misc.toSignedString(rollData.diffConditions)}`);
|
|
}
|
|
if (rollData.etat != undefined) {
|
|
parts.push(`état: ${rollData.etat}`);
|
|
}
|
|
if (rollData.selectedCarac != undefined && rollData.moral != undefined && rollData.selectedCarac.label == 'Volonté') {
|
|
parts.push(`moral: ${rollData.moral}`);
|
|
}
|
|
return parts;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static async rollData(rollData) {
|
|
rollData.rolled = await this.roll(rollData.caracValue, rollData.finalLevel, rollData.bonus, rollData.needSignificative, rollData.showDice);
|
|
return rollData;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static async roll(caracValue, finalLevel, bonus = undefined, needSignificative = undefined, showDice = true) {
|
|
let chances = this.computeChances(caracValue, finalLevel);
|
|
this._updateChancesWithBonus(chances, bonus);
|
|
this._updateChancesNeedSignificative(chances, needSignificative);
|
|
chances.showDice = showDice;
|
|
|
|
let rolled = await this.rollChances(chances);
|
|
rolled.caracValue = caracValue;
|
|
rolled.finalLevel = finalLevel;
|
|
rolled.bonus = bonus;
|
|
rolled.needSignificative = needSignificative;
|
|
return rolled;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static _updateChancesNeedSignificative(chances, needSignificative) {
|
|
if (needSignificative) {
|
|
let newScore = Math.floor(Number(chances.score) / 2);
|
|
mergeObject(chances, this._computeCell(null, newScore), { overwrite: true });
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static _updateChancesWithBonus(chances, bonus) {
|
|
if (bonus) {
|
|
let newScore = Number(chances.score) + Number(bonus);
|
|
mergeObject(chances, this._computeCell(null, newScore), { overwrite: true });
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static async rollChances(chances) {
|
|
let myRoll = new Roll("1d100").roll();
|
|
myRoll.showDice = chances.showDice;
|
|
await RdDDice.show(myRoll);
|
|
chances.roll = myRoll.total;
|
|
mergeObject(chances, this._computeReussite(chances, chances.roll));
|
|
return chances;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static computeChances(caracValue, difficulte) {
|
|
if (difficulte < -16) {
|
|
return duplicate(levelImpossible);
|
|
}
|
|
if (difficulte < -10) {
|
|
return duplicate(levelDown.find(levelData => levelData.level == difficulte));
|
|
}
|
|
return duplicate(this.resolutionTable[caracValue][difficulte + 10]);
|
|
}
|
|
|
|
static isAjustementAstrologique(rollData) {
|
|
if (rollData.selectedCarac.label.toLowerCase().includes('chance')) {
|
|
return true;
|
|
}
|
|
if (rollData.selectedSort && rollData.selectedSort.data.isrituel) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
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) {
|
|
const reussite = reussites.find(x => x.condition(chances, roll));
|
|
if (chances.needSignificative) {
|
|
if (reussite.isSign) {
|
|
return reussiteNormale;
|
|
}
|
|
if (reussite.isSuccess) {
|
|
return echecNormal;
|
|
}
|
|
}
|
|
return reussite;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
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;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static _computeCell(niveau, percentage) {
|
|
return {
|
|
niveau: niveau,
|
|
score: percentage,
|
|
sign: this._reussiteSignificative(percentage),
|
|
part: this._reussitePart(percentage),
|
|
epart: this._echecParticulier(percentage),
|
|
etotal: this._echecTotal(percentage)
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static _reussiteSignificative(score) {
|
|
return Math.floor(score / 2);
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static _reussitePart(score) {
|
|
return Math.ceil(score / 5);
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static _echecParticulier(score) {
|
|
return Math.ceil(score / 5) + 80;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static _echecTotal(score) {
|
|
return Math.ceil(score / 10) + 91;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static buildHTMLResults(caracValue, levelValue) {
|
|
let cell = this.computeChances(caracValue, levelValue);
|
|
let descr = $('<span class="span-valeur competence-label">');
|
|
descr.append("Particulière : " + cell.part + " - Significative : " + cell.sign + " - Réussite : " + cell.score);
|
|
descr.append("<br>Echec Particulier : " + cell.epart + " - Echec Total : " + cell.etotal);
|
|
descr.append("</span>");
|
|
return descr;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
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(this.resolutionTable[0], minLevel, maxLevel));
|
|
|
|
for (var rowIndex = minCarac; rowIndex <= maxCarac; rowIndex++) {
|
|
table.append(this._buildHTMLRow(this.resolutionTable[rowIndex], rowIndex, caracValue, levelValue, minLevel, maxLevel));
|
|
}
|
|
table.append("</table>");
|
|
return table;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
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;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
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;
|
|
}
|
|
|
|
} |