diff --git a/module/actor.js b/module/actor.js index c6e68da7..0b61b321 100644 --- a/module/actor.js +++ b/module/actor.js @@ -7,6 +7,7 @@ import { RdDUtility } from "./rdd-utility.js"; import { TMRUtility } from "./tmr-utility.js"; import { RdDRollDialog } from "./rdd-roll-dialog.js"; import { RdDTMRDialog } from "./rdd-tmr-dialog.js"; +import { RdDResolutionTable } from "./rdd-resolution-table.js"; export class RdDActor extends Actor { @@ -119,148 +120,110 @@ export class RdDActor extends Actor { } /* -------------------------------------------- */ - async performRoll( rollData ) { - - let result = new Roll("d100").roll().total; - let quality = "Echec"; - let xpmsg = ""; - let tache = 0; - //console.log(">>> ROLL", rollData.selectedCarac.label, rollData.rollTarget.score, myroll.total ); - if (result <= rollData.rollTarget.part) { - quality = "Réussite Particulière!"; - if ( rollData.finalLevel < 0 ) { - let xpcarac = Math.floor( Math.abs(rollData.finalLevel) / 2); - let xpcomp = (Math.abs(rollData.finalLevel) % 2 == 1) ? xpcarac+1 : xpcarac; - xpmsg = "
Points d'expérience gagné ! " + xpcarac + " - " + xpcomp; - } - rollData.pointsDeTache = 4; - rollData.qualite = 2; - } else if (result <= (rollData.rollTarget.score /2) ) { - quality = "Réussite Significative"; - rollData.pointsDeTache = 2; - rollData.qualite = 1; - } else if (result <= (rollData.rollTarget.score) ) { - quality = "Réussite Normale"; - rollData.pointsDeTache = 1; - rollData.qualite = 0; - } else if (result < (rollData.rollTarget.epart) ) { - quality = "Echec Normal"; - rollData.pointsDeTache = 0; - rollData.qualite = -2; - } else if (result < (rollData.rollTarget.etotal) ) { - quality = "Echec Particulier"; - rollData.pointsDeTache = -2; - rollData.qualite = -4; - } else if (result >= (rollData.rollTarget.etotal) ) { - quality = "Echec Total"; - rollData.pointsDeTache = -4; - rollData.qualite = -6; - } - + async performRoll(rollData) { + // Manage weapon categories when parrying (cf. page 115 ) - let need_significative = false; // Do we need to have a sgnificative ? - let need_resist = false; // Do we need to have a sgnificative ? - if ( rollData.arme && rollData.attackerRoll ) { // Manage parade depeding on weapon type, and change roll results - let attCategory = RdDUtility.getArmeCategory( rollData.attackerRoll.arme ); - let defCategory = RdDUtility.getArmeCategory( rollData.arme ); - if ( defCategory == "bouclier" ) - need_significative = true; - else if ( attCategory != defCategory ) - need_significative = true; - if ( attCategory.match("epee") && ( defCategory == "hache" || defCategory == "lance") ) + let need_resist = false; // Do we need to make resistance roll for defender ? + if (rollData.arme && rollData.attackerRoll) { // Manage parade depeding on weapon type, and change roll results + let attCategory = RdDUtility.getArmeCategory(rollData.attackerRoll.arme); + let defCategory = RdDUtility.getArmeCategory(rollData.arme); + if (defCategory == "bouclier") + rollData.needSignificative = true; + else if (attCategory != defCategory) + rollData.needSignificative = true; + if (attCategory.match("epee") && (defCategory == "hache" || defCategory == "lance")) need_resist = true; } - - // Sonne management or if need_significative is set - if ( this.data.data.sante.sonne.value || need_significative) { - if (rollData.pointsDeTache >= 2 ) { // Reussite normale dès que significative - quality = "Réussite Normale"; - rollData.pointsDeTache = 1; - rollData.qualite = 0; - } else if (rollData.pointsDeTache < 2 ) { // Not a "significative" - quality = "Echec Normal"; - rollData.pointsDeTache = 0; - rollData.qualite = -2; - } + if (this.data.data.sante.sonne.value) + { + rollData.needSignificative = true; } - + + let rolled = RdDResolutionTable.rollChances(rollData.rollTarget); + let result = rolled.roll; + let quality = rolled.quality + + console.log(">>> ROLL", rollData, rolled); + let xpmsg = RdDResolutionTable.buildXpMessage(rolled, rollData.finalLevel); + + let specialStr = "
Points de taches : " + rolled.tache + ", Points de qualité: " + rolled.qualite; + // Fight management ! let defenseMsg; let encaisser = false; - let specialStr = "
Points de taches : " + rollData.pointsDeTache; // Per default - if ( rollData.arme ) { // In case of fight, replace the "tache" message per dommages + localization. "tache" indicates if result is OK or not - if ( rollData.attackerRoll) { // Defense case ! - if ( rollData.pointsDeTache > 0 ) { // Réussite ! + if (rollData.arme) { + // In case of fight, replace the message per dommages + localization. it indicates if result is OK or not + if (rollData.attackerRoll) { // Defense case ! + if (rolled.isSuccess) { specialStr = "
Attaque parée/esquivée !"; } else { specialStr = "
Esquive/Parade échouée, encaissement !"; - encaisser = true; - } + encaisser = true; + } } else { // This is the attack roll! - if ( rollData.pointsDeTache > 0 ) { - let myroll = new Roll("2d10"); - myroll.roll(); + if (rolled.isSuccess > 0) { rollData.domArmePlusDom = parseInt(rollData.arme.data.dommages); - if ( rollData.selectedCarac.label == "Mêlée" ) // +dom only for Melee + if (rollData.selectedCarac.label == "Mêlée") // +dom only for Melee rollData.domArmePlusDom += parseInt(this.data.data.attributs.plusdom.value); - if ( rollData.selectedCarac.label == "Lancer" ) { // +dom only for Melee/Lancer + if (rollData.selectedCarac.label == "Lancer") { // +dom only for Melee/Lancer let bdom = parseInt(this.data.data.attributs.plusdom.value); - if ( bdom > parseInt(rollData.arme.data.dommages)) + if (bdom > parseInt(rollData.arme.data.dommages)) bdom = parseInt(rollData.arme.data.dommages); rollData.domArmePlusDom += bdom } - rollData.degats = parseInt(myroll.result) + rollData.domArmePlusDom; + let encaissement = new Roll("2d10 + @domArmePlusDom", {domArmePlusDom : rollData.domArmePlusDom}); + rollData.degats = parseInt(encaissement.roll().total); rollData.loc = RdDUtility.getLocalisation(); for (let target of game.user.targets) { - defenseMsg = RdDUtility.buildDefenseChatCard(this, target, rollData ); + defenseMsg = RdDUtility.buildDefenseChatCard(this, target, rollData); specialStr = "
Cible : " + target.actor.data.name; } specialStr += "
Dommages : " + rollData.degats + "
Localisation : " + rollData.loc.label; } else { - specialStr = "
Echec ! Pas de dommages"; + specialStr = "
Echec ! Pas de dommages"; } } } - + // Sort management let lvl = "" - if ( rollData.selectedSort) { // Lancement de sort ! + if (rollData.selectedSort) { // Lancement de sort ! let draconic = rollData.selectedSort.data.draconic; - specialStr = "
Lancement du sort " + rollData.selectedSort.name + " : " + draconic.charAt(0).toUpperCase() + draconic.slice(1) + "/" + rollData.selectedSort.data.difficulte + - "/" + rollData.selectedSort.data.caseTMR + "/R" + rollData.selectedSort.data.ptreve; + specialStr = "
Lancement du sort " + rollData.selectedSort.name + " : " + draconic.charAt(0).toUpperCase() + draconic.slice(1) + "/" + rollData.selectedSort.data.difficulte + + "/" + rollData.selectedSort.data.caseTMR + "/R" + rollData.selectedSort.data.ptreve; specialStr += "
Depuis la case " + rollData.coord + " (" + TMRUtility.getTMRDescription(rollData.coord).label + ")"; - lvl = rollData.selectedDraconic.name +"/"+ rollData.selectedSort.name; + lvl = rollData.selectedDraconic.name + "/" + rollData.selectedSort.name; let costReve = rollData.selectedSort.data.ptreve; let myReve = duplicate(this.data.data.reve.reve); - if ( rollData.pointsDeTache > 0 ) { // Réussite du sort ! - if (rollData.pointsDeTache >= 4 ) costReve = Math.ceil(costReve/2); - if (costReve < 1 ) costReve = 1; + if (rollData.tache > 0) { // Réussite du sort ! + if (rollData.tache >= 4) costReve = Math.ceil(costReve / 2); + if (costReve < 1) costReve = 1; myReve.value = myReve.value - costReve; // Todo 0 pts de reve !!!! if (myReve.value < 0) myReve.value = 0; - await this.update( {"data.reve.reve": myReve } ); + await this.update({ "data.reve.reve": myReve }); specialStr += "
Réussite du sort pour " + costReve + " Points de Rêve"; - if ( !rollData.isSortReserve) { + if (!rollData.isSortReserve) { this.currentTMR.close(); // Close TMR ! } else { // Mise en réserve let reserve = duplicate(this.data.data.reve.reserve); - reserve.list.push( { coord: rollData.coord, sort: duplicate(rollData.selectedSort), draconic: duplicate(rollData.selectedDraconic) }); - await this.update( {"data.reve.reserve": reserve} ); + reserve.list.push({ coord: rollData.coord, sort: duplicate(rollData.selectedSort), draconic: duplicate(rollData.selectedDraconic) }); + await this.update({ "data.reve.reserve": reserve }); this.currentTMR.updateSortReserve(); } } else { - if ( rollData.pointsDeTache == -4) { // Echec total ! + if (rollData.tache == -4) { // Echec total ! costReve *= 2; myReve.value = myReve.value - costReve; // Todo 0 pts de reve !!!! if (myReve.value < 0) myReve.value = 0; - await this.update( {"data.reve.reve": myReve } ); + await this.update({ "data.reve.reve": myReve }); specialStr += "
Echec TOTAL du sort : " + costReve + " Points de Rêve"; - } else { + } else { specialStr += "
Echec du sort !"; } this.currentTMR.close(); // Close TMR ! } if (myReve.value == 0) { // 0 points de reve - ChatMessage.create( {title: "Zero Points de Reve !", content: this.name + " est réduit à 0 Points de Rêve, et tombe endormi !" } ); + ChatMessage.create({ title: "Zero Points de Reve !", content: this.name + " est réduit à 0 Points de Rêve, et tombe endormi !" }); this.currentTMR.close(); // Close TMR ! } } else { @@ -268,37 +231,38 @@ export class RdDActor extends Actor { } // Save it for fight in the flags area - await this.setFlag( 'world', 'rollData', null ); - await this.setFlag( 'world', 'rollData', rollData ); - + await this.setFlag('world', 'rollData', null); + await this.setFlag('world', 'rollData', rollData); + // Final chat message - let chatOptions = { content: "Test : " + rollData.selectedCarac.label + " / " + lvl + "
Jet : " + - rollData.selectedCarac.value + " / " + rollData.finalLevelStr + " -> " + rollData.rollTarget.score + "%
Résutat : " + result + "
" + - "" + quality + "" + specialStr + xpmsg, - user: game.user._id, - title: "Résultat du test" - } - ChatMessage.create( chatOptions ); - + let chatOptions = { + content: "Test : " + rollData.selectedCarac.label + " / " + lvl + "
Jet : " + + rollData.selectedCarac.value + " / " + rollData.finalLevelStr + " -> " + rolled.score + "%
Résutat : " + result + "
" + + "" + quality + "" + specialStr + xpmsg, + user: game.user._id, + title: "Résultat du test" + } + ChatMessage.create(chatOptions); + // This an attack, generate the defense message - if ( defenseMsg ) { - if ( defenseMsg.toSocket) { + if (defenseMsg) { + if (defenseMsg.toSocket) { game.socket.emit("system.foundryvtt-reve-de-dragon", { msg: "msg_defense", - data: defenseMsg - } ); - } else { - defenseMsg.whisper = [ game.user]; - ChatMessage.create( defenseMsg ); + data: defenseMsg + }); + } else { + defenseMsg.whisper = [game.user]; + ChatMessage.create(defenseMsg); } } // Get damages! - if ( encaisser ) { - this.encaisserDommages( rollData.attackerRoll ); + if (encaisser) { + this.encaisserDommages(rollData.attackerRoll); } -} - + } + /* -------------------------------------------- */ updateCarac( caracName, caracValue ) { @@ -635,62 +599,45 @@ export class RdDActor extends Actor { this.update( { "data.blessures": blessures } ); } - - /* -------------------------------------------- */ - async stressTest( ) { + + /* -------------------------------------------- */ + async stressTest() { + let target = RdDResolutionTable.computeChances(this.data.data.carac.reve.value, 0); + let stressRoll = this._stressRoll(target); + let compteurs = duplicate(this.data.data.compteurs); - let stress = compteurs.stress; - let xp = compteurs.experience; + let convertion = Math.floor(compteurs.stress.value * stressRoll.factor); + + compteurs.experience.value += convertion; + compteurs.stress.value = Math.max(compteurs.stress.value - convertion - 1, 0); - let scoreTarget = RdDUtility.getResolutionField( this.data.data.carac.reve.value, 0 ); - scoreTarget.sig = Math.floor(scoreTarget.score / 2); - - let scoreValue = new Roll("d100").roll().total; - let newXP = xp.value; - let factor = 0; - let comment = "Echec Total (0%)" - - if (scoreValue <= scoreTarget.part ) { - scoreValue = new Roll("d100").roll().total; - if (scoreValue <= scoreTarget.sig) { - factor = 1.5; - comment = "Double Particulière (150%)" - } else { - factor = 1; - comment = "Particulière (100%)" - } - } else { - if ( scoreValue <= scoreTarget.sig ) { - factor = 0.75; - comment = "Significative (75%)" - } else { - if ( scoreValue <= scoreTarget.score ) { - factor = 0.5; - comment = "Normale (50%)" - } else if (scoreValue <= scoreTarget.epart) { - factor = 0.2; - comment = "Echec (20%)" - } else if (scoreValue <= scoreTarget.etotal) { - factor = 0.1; - comment = "Echec (10%)" - } - } - } - - let xpValue = Math.floor(stress.value * factor); - stress.value -= xpValue; - stress.value -= 1; - if (stress.value < 0 ) stress.value = 0; - - ChatMessage.create( { title: "Jet de Stress", content: "Vous avez transformé "+ xpValue + " points de Stress en Expérience avec une réussite " + comment , - whisper: ChatMessage.getWhisperRecipients(game.user.name) } ); - - xp.value += xpValue; - await this.update( { "data.compteurs": compteurs } ); + ChatMessage.create({ + title: "Jet de Stress", content: "Vous avez transformé " + convertion + " points de Stress en Expérience avec une réussite " + stressRoll.comment, + whisper: ChatMessage.getWhisperRecipients(game.user.name) + }); + await this.update({ "data.compteurs": compteurs }); } - /* -------------------------------------------- */ - async rollUnSort( coord ) { + _stressRoll(target) { + let result = RdDResolutionTable.rollChances(target) + switch (result.quality) { + case "sign": return { factor: 0.75, comment: "Significative (75%) - " + result.roll } + case "norm": return { factor: 0.5, comment: "Normale (50%) - " + result.roll } + case "echec": return { factor: 0.2, comment: "Echec (20%) - " + result.roll } + case "epart": return { factor: 0.1, comment: "Echec particulier(10%) - " + result.roll } + case "etotal": return { factor: 0, comment: "Echec Total (0%) - " + result.roll } + } + let second = RdDResolutionTable.rollChances(target) + switch (second.qualite) { + case "part": case "sign": + return { factor: 1.5, comment: "Double Particulière (150%) - " + result + " puis " + second } + default: + return { factor: 1, comment: "Particulière (100%) - " + result + " puis " + second } + } + } + + /* -------------------------------------------- */ + async rollUnSort(coord) { let draconicList = this.getDraconicList(); let sortList = this.getSortList(); diff --git a/module/rdd-resolution-table.js b/module/rdd-resolution-table.js index 5aa236fa..f49b4748 100644 --- a/module/rdd-resolution-table.js +++ b/module/rdd-resolution-table.js @@ -1,4 +1,58 @@ +/** + * 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, isEPart: true, isETotal: true, tache: -4, qualite: -6, quality: "Echec total", condition: (target, roll) => roll >= target.etotal && roll <= 100 }, + { code: "epart", isPart: false, isSign: false, isSuccess: false, isEPart: true, isETotal: false, tache: -2, qualite: -4, quality: "Echec particulier", condition: (target, roll) => (roll >= target.epart && roll < target.etotal) }, + { code: "echec", isPart: false, isSign: false, isSuccess: false, isEPart: false, isETotal: false, tache: 0, qualite: -2, quality: "Echec normal", condition: (target, roll) => (roll > target.score && roll < target.etotal) }, + { code: "norm", isPart: false, isSign: false, isSuccess: true, isEPart: false, isETotal: false, tache: 1, qualite: 0, quality: "Réussite normale", condition: (target, roll) => (roll > target.sign && roll <= target.score) }, + { code: "sign", isPart: false, isSign: true, isSuccess: true, isEPart: false, isETotal: false, tache: 2, qualite: 1, quality: "Réussite significative", condition: (target, roll) => (roll > target.part && roll <= target.sign) }, + { code: "part", isPart: true, isSign: true, isSuccess: true, isEPart: false, isETotal: false, tache: 3, qualite: 2, quality: "Réussite Particulière!", condition: (target, roll) => (roll > 0 && roll <= target.part) }, + { code: "error", isPart: false, isSign: false, isSuccess: false, isEPart: true, isETotal: true, tache: 0, qualite: 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"); + export class RdDResolutionTable { static resolutionTable = this.build() @@ -12,25 +66,71 @@ export class RdDResolutionTable { } /* -------------------------------------------- */ + static roll(carac, difficulte) { + return this.rollChances(this.computeChances(carac, difficulte)); + } + + static rollChances(chances) { + chances.roll = new Roll("d100").roll().total; + mergeObject(chances, this._computeReussite(chances, chances.roll)); + return chances; + } + + static computeChances(carac, difficulte) { + if (difficulte < -16) { + return duplicate(levelImpossible); + } + if (difficulte < -10) { + return duplicate(levelDown.find(levelData => levelData.level == difficulte)); + } + return duplicate(this.resolutionTable[carac][difficulte + 10]); + } + + static buildXpMessage(rolled, level) + { + if (rolled.isPart && level < 0) { + const xp = Math.abs(level); + const xpcarac = Math.floor(xp / 2); + const xpcomp = xp - xpcarac; + return "
Points d'expérience gagnés ! Carac: " + xpcarac + ", Comp: " + xpcomp; + } + return ""; + } + + + /* -------------------------------------------- */ + 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(carac) { let dataRow = [ - this._computeScore(-10, Math.max(Math.floor(carac / 4), 1)), - this._computeScore(-9, Math.max(Math.floor(carac / 2), 1)) + this._computeCell(-10, Math.max(Math.floor(carac / 4), 1)), + this._computeCell(-9, Math.max(Math.floor(carac / 2), 1)) ] for (var diff = -8; diff <= 22; diff++) { - dataRow[diff + 10] = this._computeScore(diff, Math.max(Math.floor(carac * (diff + 10) / 2), 1)); + dataRow[diff + 10] = this._computeCell(diff, Math.max(Math.floor(carac * (diff + 10) / 2), 1)); } return dataRow; } - static _computeScore(diff, score) { + static _computeCell(niveau, percentage) { return { - niveau: diff, - score: score, - sign: this._reussiteSignificative(score), - part: this._reussitePart(score), - epart: this._echecParticulier(score), - etotal: this._echecTotal(score) + niveau: niveau, + score: percentage, + sign: this._reussiteSignificative(percentage), + part: this._reussitePart(percentage), + epart: this._echecParticulier(percentage), + etotal: this._echecTotal(percentage) } } diff --git a/module/rdd-roll-dialog.js b/module/rdd-roll-dialog.js index c66316ae..d56978be 100644 --- a/module/rdd-roll-dialog.js +++ b/module/rdd-roll-dialog.js @@ -1,5 +1,4 @@ import { RdDResolutionTable } from "./rdd-resolution-table.js"; -import { RdDUtility } from "./rdd-utility.js"; /** * Extend the base Dialog entity by defining a custom window to perform roll. @@ -67,7 +66,7 @@ export class RdDRollDialog extends Dialog { rollData.finalLevel = rollLevel; rollData.finalLevelStr = (rollLevel > 0 ? "+" : "") + rollLevel; - rollData.rollTarget = RdDUtility.getResolutionField(rollData.selectedCarac.value, rollData.finalLevel); + rollData.rollTarget = RdDResolutionTable.computeChances(rollData.selectedCarac.value, rollData.finalLevel); $("#roll-param").text(rollData.selectedCarac.value + " / " + rollData.finalLevelStr); $("#compdialogTitle").text(RdDRollDialog._getTitle(rollData)); @@ -80,7 +79,8 @@ export class RdDRollDialog extends Dialog { // Update html, according to data if (rollData.competence) { // Set the default carac from the competence item - console.log(rollData.competence.data.defaut_carac, rollData.carac); + console.log("RdDDialogRoll", rollData.competence.data.defaut_carac, rollData.carac); + rollData.selectedCarac = rollData.carac[rollData.competence.data.defaut_carac]; $("#carac").val(rollData.competence.data.defaut_carac); } @@ -91,30 +91,32 @@ export class RdDRollDialog extends Dialog { // Update ! html.find('#bonusmalus').click((event) => { rollData.bmValue = event.currentTarget.value; // Update the selected bonus/malus - //console.log("BM CLICKED !!!", rollData.bmValue, rollData.competence.data.niveau, parseInt(rollData.competence.data.niveau) + parseInt(rollData.bmValue) ); + console.debug("RdDRollDialog","BM CLICKED !!!", rollData.bmValue, rollData.competence.data.niveau, parseInt(rollData.competence.data.niveau) + parseInt(rollData.bmValue) ); updateRollResult(rollData); }); html.find('#carac').click((event) => { let caracKey = event.currentTarget.value; rollData.selectedCarac = rollData.carac[caracKey]; // Update the selectedCarac - //console.log("CARAC CLICKED !!!", rollData.selectedCarac, rollData.competence.data.niveau, rollData.bmValue); + console.debug("RdDRollDialog","CARAC CLICKED !!!", rollData.selectedCarac, rollData.competence.data.niveau, rollData.bmValue); updateRollResult(rollData); }); html.find('#draconic').click((event) => { let draconicKey = event.currentTarget.value; rollData.selectedDraconic = rollData.draconicList[draconicKey]; // Update the selectedCarac - //console.log("CARAC CLICKED !!!", rollData.selectedCarac, rollData.competence.data.niveau, rollData.bmValue); + console.debug("RdDRollDialog","CARAC CLICKED !!!", rollData.selectedCarac, rollData.competence.data.niveau, rollData.bmValue); updateRollResult(rollData); }); html.find('#sort').click((event) => { let sortKey = event.currentTarget.value; rollData.selectedSort = rollData.sortList[sortKey]; // Update the selectedCarac - //console.log("CARAC CLICKED !!!", rollData.selectedCarac, rollData.competence.data.niveau, rollData.bmValue); + console.debug("RdDRollDialog","CARAC CLICKED !!!", rollData.selectedCarac, rollData.competence.data.niveau, rollData.bmValue); updateRollResult(rollData); }); } + + /* -------------------------------------------- */ static _computeFinalLevel(rollData) { let etat = rollData.etat === undefined ? 0 : parseInt(rollData.etat); if (rollData.competence) { diff --git a/module/rdd-tmr-dialog.js b/module/rdd-tmr-dialog.js index b6dbea2c..d3a24fd2 100644 --- a/module/rdd-tmr-dialog.js +++ b/module/rdd-tmr-dialog.js @@ -5,6 +5,7 @@ import { RdDUtility } from "./rdd-utility.js"; import { TMRUtility } from "./tmr-utility.js"; import { RdDRollTables } from "./rdd-rolltables.js"; +import { RdDResolutionTable } from "./rdd-resolution-table.js"; /** Helper functions */ export class RdDTMRDialog extends Dialog { @@ -130,13 +131,14 @@ export class RdDTMRDialog extends Dialog { this.actor.deleteTMRRencontreAtPosition( ); // Remove the stored rencontre if necessary this.updatePreviousRencontres(); - let draconic = this.actor.getBestDraconic(); - let carac = this.actor.getCurrentReve(); - let level = draconic.data.niveau - this.currentRencontre.force; + const draconic = this.actor.getBestDraconic(); + const carac = this.actor.getCurrentReve(); + const difficulte = draconic.data.niveau - this.currentRencontre.force; console.log("Maitriser", carac, draconic.data.niveau, this.currentRencontre.force); - let scoreDef = CONFIG.RDD.resolutionTable[carac][level+10]; - let result = new Roll("d100").roll().total; - if ( result > scoreDef.score ) { + + const roll = RdDResolutionTable.roll(carac, difficulte); + + if ( roll.roll > scoreDef.score ) { TMRUtility.processRencontreEchec( this.actor, this.currentRencontre); ChatMessage.create( { title: "TMR", content: "Vous avez échoué à votre maîtrise d'un " . this.currentRencontre.name + " de force " + this.currentRencontre.force + @@ -232,46 +234,49 @@ export class RdDTMRDialog extends Dialog { this.close(); } - /* -------------------------------------------- */ - async manageCaseHumide( cellDescr ) { - if ( cellDescr.type == "lac" || cellDescr.type == "fleuve" || cellDescr.type == "marais" ) { + /* -------------------------------------------- */ + async manageCaseHumide(cellDescr) { + if (cellDescr.type == "lac" || cellDescr.type == "fleuve" || cellDescr.type == "marais") { let draconic = this.actor.getBestDraconic(); let carac = this.actor.getCurrentReve(); let level = draconic.data.niveau - 7; - let scoreDef = CONFIG.RDD.resolutionTable[carac][level+10]; - let result = new Roll("d100").roll().total; + let rolled = RdDResolutionTable.roll(carac, level);//RdDResolutionTable.computeChances(carac, level); + + console.log(">>>>", rolled); + + this.toclose = rolled.isSuccess; let content = ""; - let mycallback; - console.log(">>>>", scoreDef); - if ( result > scoreDef.score ) { - content = "Vous êtes entré sur une case humide, et vous avez raté votre maîtrise ! Vous quittez les Terres Médianes ! ("+ draconic.name +") :" + carac + " / " + level + " -> " + result + " / " + scoreDef.score; - if ( result >= scoreDef.etotal ) { - let souffle = RdDRollTables.getSouffle(true); - content += "
Vous avez fait un Echec Total. Vous subissez un Souffle de Dragon : " + souffle.name ; - this.actor.createOwnedItem( souffle ); - } - this.toclose = true; - } else { - content = "Vous êtes entré sur une case humide, et vous avez réussi votre maîtrise ! ("+ draconic.name +") :" + carac + " / " + level + " -> " + result + " / " + scoreDef.score; - if ( result <= scoreDef.part ) { - content += "
Vous avez fait une Réussite Particulière"; - if ( level < 0 ) { - let xpcarac = Math.floor( Math.abs(level) / 2); - let xpcomp = (Math.abs(level) % 2 == 1) ? xpcarac+1 : xpcarac; - content += "
Points d'expérience gagné ! " + xpcarac + " - " + xpcomp; + if (!rolled.isSuccess) { + content += "Vous êtes entré sur une case humide, et vous avez raté votre maîtrise ! Vous quittez les Terres Médianes !" + } + else { + content += "Vous êtes entré sur une case humide, et vous avez réussi votre maîtrise !" + } + + content += " " + draconic.name + ": " + carac + " / " + level + " -> " + rolled.roll + " / " + rolled.score; + + if (rolled.isETotal) { + let souffle = RdDRollTables.getSouffle(true); + content += "
Vous avez fait un Echec Total. Vous subissez un Souffle de Dragon : " + souffle.name; + this.actor.createOwnedItem(souffle); + } + if (rolled.isPart) { + content += "
Vous avez fait une Réussite Particulière"; + content += RdDResolutionTable.buildXpMessage(rolled, level) + } + + let humideDiag = new Dialog({ + title: "Case humide", + content: content, + buttons: { + choice: { + icon: '', + label: "Fermer", + callback: () => this.manageCaseHumideResult() } } } - let humideDiag = new Dialog( {title: "Case humide", - content: content, - buttons: { - choice: { icon: '', - label: "Fermer", - callback: () => this.manageCaseHumideResult() - } - } - } - ); + ); humideDiag.render(true); } } diff --git a/module/rdd-utility.js b/module/rdd-utility.js index 47910657..5e71b654 100644 --- a/module/rdd-utility.js +++ b/module/rdd-utility.js @@ -22,39 +22,11 @@ const competence_xp = { "-6" : [ 10, 20, 35, 50, 65, 80, 100, 120, 140], "-4" : [ 15, 30, 45, 60, 80, 100, 120] } + // This table starts at 0 -> niveau -10 const competence_xp_par_niveau = [ 5, 5, 5, 10, 10, 10, 10, 15, 15, 15, 15, 20, 20, 20, 20, 30, 30, 40, 40, 60, 60, 100, 100, 100, 100, 100, 100, 100, 100, 100]; const carac_array = [ "taille", "apparence", "constitution", "force", "agilite", "dexterite", "vue", "ouie", "odoratgout", "volonte", "intellect", "empathie", "reve", "chance", "melee", "tir", "lancer", "derobee"]; const bonusmalus = [-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, +1, +2, +3, +4, +5, +6, +7, +8, +9, +10]; -const specialResults = [ { "part": 0, "epart": 0, "etotal": 0 }, // 0 - { "part": 1, "epart": 81, "etotal": 92 }, // 01-05 - { "part": 2, "epart": 82, "etotal": 92 }, // 06-10 - { "part": 3, "epart": 83, "etotal": 93 }, // 11-15 - { "part": 4, "epart": 84, "etotal": 93 }, // 16-20 - { "part": 5, "epart": 85, "etotal": 94 }, // 21-25 - { "part": 6, "epart": 86, "etotal": 94 }, // 26-30 - { "part": 7, "epart": 87, "etotal": 95 }, // 31-35 - { "part": 8, "epart": 88, "etotal": 95 }, // 36-40 - { "part": 9, "epart": 89, "etotal": 96 }, // 41-45 - { "part": 10, "epart": 90, "etotal": 96 }, // 46-50 - { "part": 11, "epart": 91, "etotal": 97 }, // 51-55 - { "part": 12, "epart": 92, "etotal": 97 }, // 56-60 - { "part": 13, "epart": 93, "etotal": 98 }, // 61-65 - { "part": 14, "epart": 94, "etotal": 98 }, // 65-70 - { "part": 15, "epart": 95, "etotal": 99 }, // 71-75 - { "part": 16, "epart": 96, "etotal": 99 }, // 76-80 - { "part": 17, "epart": 97, "etotal": 100 }, // 81-85 - { "part": 18, "epart": 98, "etotal": 100 }, // 86-90 - { "part": 19, "epart": 99, "etotal": 100 }, // 81-95 - { "part": 20, "epart": 100, "etotal": 100 } // 96-00 - ]; -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": 2, "etotal": 2 } - ]; const fatigueMatrix = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // Dummy filler for the array. [2, 3, 3, 2, 3, 3, 2, 3, 3, 2, 3, 3 ], [2, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 3 ], @@ -239,18 +211,6 @@ export class RdDUtility { return false; } - /* -------------------------------------------- */ - static getResolutionField(caracValue, levelValue ) - { - if ( levelValue < -16 ) { - return { score: 0, sign:0, part: 0, epart: 1, etotal: 1}; - } - if ( levelValue < -10 ) { - return levelDown.find(levelData => levelData.level == levelValue); - } - return CONFIG.RDD.resolutionTable[caracValue][levelValue+10]; - } - /* -------------------------------------------- */ static computeCompetenceXPCost( competence ) {