100 est un échec si les chances sont supérieures un échec total si elles sont inférieures jamais un échec particulier
398 lines
16 KiB
JavaScript
398 lines
16 KiB
JavaScript
import { ChatUtility } from "./chat-utility.js";
|
|
import { RdDItemArme } from "./item-arme.js";
|
|
import { Misc } from "./misc.js";
|
|
import { RdDBonus } from "./rdd-bonus.js";
|
|
import { RdDCombat } from "./rdd-combat.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.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 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.diviseur > 1 ? `(1/${rolled.diviseur} de ` : "(")
|
|
+ rolled.caracValue + " à " + Misc.toSignedString(rolled.finalLevel) + ") ";
|
|
}
|
|
message += '<strong>' + rolled.quality + '</strong>'
|
|
return message;
|
|
}
|
|
|
|
static async buildRollDataHtml(rollData, template = 'chat-resultat-general.html') {
|
|
rollData.ajustements = RdDResolutionTable._buildAjustements(rollData);
|
|
rollData.show = rollData.show || {};
|
|
|
|
return await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/${template}`, rollData);
|
|
}
|
|
|
|
static async displayRollData(rollData, userName, template = 'chat-resultat-general.html') {
|
|
|
|
ChatUtility.chatWithRollMode(
|
|
{ content: await RdDResolutionTable.buildRollDataHtml(rollData, template) },
|
|
userName)
|
|
}
|
|
|
|
static _buildAjustements(rollData) {
|
|
let list = [];
|
|
if (rollData.competence) {
|
|
list.push({ label: rollData.competence.name, value: rollData.competence.data.niveau });
|
|
}
|
|
if (rollData.tactique) {
|
|
const surprise = RdDBonus.find(rollData.tactique);
|
|
list.push({ label: surprise.descr, value: surprise.attaque });
|
|
}
|
|
if (rollData.surpriseDefenseur) {
|
|
const surprise = RdDBonus.find(rollData.surpriseDefenseur);
|
|
list.push({ label: surprise.descr, value: surprise.attaque });
|
|
}
|
|
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: 'Etat', 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({ descr: `Bonus de case: ${rollData.rolled.bonus}%` });
|
|
}
|
|
if (rollData.diviseur > 1) {
|
|
list.push({ descr: `Facteur significative ×${RdDResolutionTable._getFractionHtml(rollData.diviseur)}` });
|
|
}
|
|
if (RdDCombat.isAttaqueFinesse(rollData.attackerRoll)) {
|
|
list.push({ descr: 'Attaque particulière en finesse' });
|
|
}
|
|
if (rollData.needParadeSignificative) {
|
|
const catAttaque = RdDItemArme.getNomCategorieParade(rollData.attackerRoll.arme);
|
|
const catParade = RdDItemArme.getNomCategorieParade(rollData.arme);
|
|
list.push({ descr: `${catAttaque} vs ${catParade}` });
|
|
}
|
|
if (rollData.surprise) {
|
|
list.push({ descr: RdDBonus.find(rollData.surprise).descr });
|
|
}
|
|
return list;
|
|
}
|
|
static _getFractionHtml(diviseur) {
|
|
if (!diviseur || diviseur <= 1) return undefined;
|
|
switch (diviseur || 1) {
|
|
case 2: return '½';
|
|
case 4: return '¼';
|
|
default: return '1/' + diviseur;
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static async rollData(rollData) {
|
|
rollData.rolled = await this.roll(rollData.caracValue, rollData.finalLevel, rollData.bonus, rollData.diviseur, rollData.showDice);
|
|
return rollData;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
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);
|
|
chances.showDice = showDice;
|
|
|
|
let rolled = await this.rollChances(chances);
|
|
rolled.caracValue = caracValue;
|
|
rolled.finalLevel = finalLevel;
|
|
rolled.bonus = bonus;
|
|
rolled.factor = RdDResolutionTable._getFractionHtml(diviseur);
|
|
return rolled;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static _updateChancesFactor(chances, diviseur) {
|
|
if (diviseur && diviseur > 1) {
|
|
let newScore = Math.floor(Number(chances.score) / diviseur);
|
|
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(RdDResolutionTable.resolutionTable[caracValue][difficulte + 10]);
|
|
}
|
|
|
|
static isAjustementAstrologique(rollData) {
|
|
if (rollData.selectedCarac && 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) {
|
|
return reussites.find(x => x.condition(chances, roll));
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
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,
|
|
norm: Math.min(99, percentage),
|
|
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);
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
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(RdDResolutionTable.resolutionTable[0], minLevel, maxLevel));
|
|
|
|
for (var rowIndex = minCarac; rowIndex <= maxCarac; rowIndex++) {
|
|
table.append(this._buildHTMLRow(RdDResolutionTable.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;
|
|
}
|
|
|
|
} |