import { Evaluated } from "@league-of-foundry-developers/foundry-vtt-types/src/foundry/client/dice/roll"; import { M5Character } from "../actors/M5Character"; import { M5EwResult, M5RollData, M5RollResult } from "../M5Base"; export class M5Roll { // extends Roll static readonly TEMPLATE_PATH = "systems/midgard5/templates/chat/roll-m5.hbs" public _evaluated: boolean = false public _total: number = 0 public pool: PoolTerm = null constructor(public data: M5RollData, public actor: any, public label: string) { //super(null) //this.data = rollData } // @ts-ignore //override evaluate(options?: InexactPartial): Evaluated> | Promise>> { evaluate() { const indexMap = new Map() const rollNames = Object.keys(this.data.rolls) const rolls = rollNames.filter(rollName => this.data.rolls[rollName].enabled).map((rollName, index) => { indexMap.set(index, rollName) const formula = this.data.rolls[rollName] const roll = new Roll(formula.formula, this.data) return roll }) this.pool = PoolTerm.fromRolls(rolls) console.log("evaluate", this._evaluated, this.pool) return this.pool.evaluate({ async: true }).then(results => { this._total = 0 results.rolls.forEach((roll, index) => { const rollIndex = indexMap.get(index) const rollResult = this.data.rolls[rollIndex] as M5RollResult rollResult.result = roll.result rollResult.total = roll.total rollResult.totalStr = roll.total.toString() this._total += roll.total let rowRes = M5EwResult.TBD let face100 = -1 roll.dice.forEach((d, dIndex) => { rollResult.dice[dIndex.toString()] = d.total if (rowRes === M5EwResult.TBD && dIndex === 0) { if (d.faces === 20) { //if (rollResult.type === "ew") { if (d.total === 1) rowRes = M5EwResult.FUMBLE else if (d.total === 20) rowRes = M5EwResult.CRITICAL else if (d.total >= 16) rowRes = M5EwResult.HIGH } else if (d.faces === 100) { face100 = d.total as number } } }) const parseResult = M5Roll.parseDiceSides(rollResult.formula) //console.log("evaluate roll", parseResult) if (parseResult?.sides === 20) { if (roll.total < 20) { if (rowRes === M5EwResult.TBD || rowRes === M5EwResult.HIGH) rowRes = M5EwResult.FAIL } else { if (rowRes === M5EwResult.TBD) rowRes = M5EwResult.PASS } } else if (face100 >= 0) { const threshold100 = roll.total + face100 const threshold = Math.floor(threshold100 / 10) if (face100 === 100) { if (rowRes === M5EwResult.TBD) rowRes = M5EwResult.FUMBLE } else if (roll.total < 0) { if (rowRes === M5EwResult.TBD) rowRes = M5EwResult.FAIL } else if (face100 <= threshold) { if (rowRes === M5EwResult.TBD) rowRes = M5EwResult.CRITICAL } else { if (rowRes === M5EwResult.TBD) rowRes = M5EwResult.PASS } } rollResult.css = rowRes }) this.data.res.label = this.label this._evaluated = true return this }) } async render(): Promise { return renderTemplate(M5Roll.TEMPLATE_PATH, this.data) } async toMessage() { if (!this._evaluated) await this.evaluate() const rMode = (game as Game).settings.get("core", "rollMode") const chatData = { type: CONST.CHAT_MESSAGE_TYPES.ROLL, content: await this.render(), speaker: ChatMessage.getSpeaker({actor: this.actor}), sound: CONFIG.sounds.dice, roll: Roll.fromTerms([this.pool]) } ChatMessage.applyRollMode(chatData, rMode) return ChatMessage.create(chatData) } static fromAttribute(actor: any, attributeKey: string) { const character = actor as M5Character const attribute = character.attribute(attributeKey) const rollData = actor.getRollData() as M5RollData rollData.i = attribute.value + attribute.bonus rollData.rolls["0"] = { formula: "@i - 1d100", enabled: true, label: (game as Game).i18n.localize("midgard5.pw"), result: "", total: 0, totalStr: "", dice: {}, css: "" } as M5RollResult return new M5Roll(rollData, actor, (game as Game).i18n.localize(`midgard5.actor-${attributeKey}-long`)) } static brawl(actor: any) { const rollData = actor.getRollData() as M5RollData rollData.i = { attackBonus: 0, damageBonus: 0 } rollData.rolls["0"] = { formula: "1d20 + @c.calc.stats.brawl + @c.calc.stats.attackBonus + @i.attackBonus", enabled: true, label: (game as Game).i18n.localize("midgard5.attack"), result: "", total: 0, totalStr: "", dice: {}, css: "" } as M5RollResult rollData.rolls["1"] = { formula: "1d6 - 4 + @c.calc.stats.damageBonus + @i.damageBonus", enabled: true, label: (game as Game).i18n.localize("midgard5.damage"), result: "", total: 0, totalStr: "", dice: {}, css: "" } as M5RollResult return new M5Roll(rollData, actor, (game as Game).i18n.localize("midgard5.brawl")) } static parseDiceSides(formula: string): FormulaParseResult { const ewMatcher: RegExp = /\d*[dD]20/g const pwMatcher: RegExp = /(\d+)\s*\-\s*\d*[dD]100/g let res = formula.match(ewMatcher) if (res && !!res[0]) { return { sides: 20, type: "ew", threshold: null } } res = formula.match(pwMatcher) if (res && !!res[1]) { return { sides: 100, type: "pw", threshold: parseInt(res[1]) } } return null } } interface FormulaParseResult { sides: number, type: string, threshold: number }