Add customizable rolls to items
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { BooleanField } from "@league-of-foundry-developers/foundry-vtt-types/src/foundry/common/data/fields.mjs"
|
||||
|
||||
export interface M5Skill {
|
||||
fw: number
|
||||
@@ -33,11 +34,13 @@ export interface M5RollData {
|
||||
}
|
||||
}
|
||||
|
||||
export interface M5RollResult {
|
||||
export interface M5RollTemplate {
|
||||
formula: string
|
||||
label: string
|
||||
type: string
|
||||
enabled: boolean
|
||||
}
|
||||
|
||||
export interface M5RollResult extends M5RollTemplate {
|
||||
total: number
|
||||
totalStr: string
|
||||
result: string
|
||||
@@ -67,9 +70,9 @@ export enum M5Attributes {
|
||||
}
|
||||
|
||||
export enum M5Stats {
|
||||
DEFENSE = "defense",
|
||||
ATTACK = "attack",
|
||||
DAMAGE = "damage",
|
||||
DEFENSE = "defenseBonus",
|
||||
ATTACK = "attackBonus",
|
||||
DAMAGE = "damageBonus",
|
||||
MOVEMENT = "movement",
|
||||
RESISTANCE_MIND = "resistanceMind",
|
||||
RESISTANCE_BODY = "resistanceBody",
|
||||
|
||||
@@ -28,7 +28,7 @@ export default class M5ModAggregate {
|
||||
this.push({ type: M5ModType.ATTRIBUTE, id: M5Attributes.PA, operation: M5ModOperation.ADD_100, value: data.attributes.pa.bonus }, characterString)
|
||||
this.push({ type: M5ModType.ATTRIBUTE, id: M5Attributes.WK, operation: M5ModOperation.ADD_100, value: data.attributes.wk.bonus }, characterString)
|
||||
|
||||
this.push({ type: M5ModType.STAT, id: M5Stats.DEFENSE, operation: M5ModOperation.SET, value: calc.stats.defense.value }, characterString)
|
||||
this.push({ type: M5ModType.STAT, id: M5Stats.DEFENSE, operation: M5ModOperation.SET, value: calc.stats.defenseBonus.value }, characterString)
|
||||
this.push({ type: M5ModType.STAT, id: M5Stats.ATTACK, operation: M5ModOperation.SET, value: calc.stats.attackBonus.value }, characterString)
|
||||
this.push({ type: M5ModType.STAT, id: M5Stats.DAMAGE, operation: M5ModOperation.SET, value: calc.stats.damageBonus.value }, characterString)
|
||||
this.push({ type: M5ModType.STAT, id: M5Stats.MOVEMENT, operation: M5ModOperation.SET, value: calc.stats.movementBonus.value }, characterString)
|
||||
@@ -59,19 +59,10 @@ export default class M5ModAggregate {
|
||||
source: source
|
||||
}
|
||||
|
||||
let key = mod.id
|
||||
// if (mod.type === M5ModType.ATTRIBUTE) {
|
||||
// key = M5Attributes[mod.id] as string
|
||||
// console.log("M5ModType.ATTRIBUTE", mod.id, key)
|
||||
// } else if (mod.type === M5ModType.STAT) {
|
||||
// key = M5Stats[mod.id] as string
|
||||
// console.log("M5ModType.STAT", mod.id, key)
|
||||
// }
|
||||
|
||||
if (map.has(key))
|
||||
map.get(key).push(pair)
|
||||
if (map.has(mod.id))
|
||||
map.get(mod.id).push(pair)
|
||||
else
|
||||
map.set(key, [pair])
|
||||
map.set(mod.id, [pair])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ export class M5Item extends Item {
|
||||
if (actor) {
|
||||
const actorCalc = character.derivedData({ weapons: true, defensiveWeapons: true, armor: true, items: true, spells: true })
|
||||
if (actorCalc) {
|
||||
calc.ew += actorCalc.stats.attackBonus
|
||||
calc.ew += actorCalc.stats.attackBonus.value
|
||||
calc.combatSkills = actorCalc.skills.combat
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ export class M5Item extends Item {
|
||||
if (actor) {
|
||||
const actorCalc = character.derivedData({ weapons: true, defensiveWeapons: true, armor: true, items: true, spells: true })
|
||||
if (actorCalc) {
|
||||
calc.ew += actorCalc.stats.spellCasting
|
||||
calc.ew += actorCalc.stats.spellCasting.value
|
||||
}
|
||||
}
|
||||
} else if (context.type === "item") {
|
||||
@@ -204,15 +204,17 @@ export class M5Item extends Item {
|
||||
const rollData = this.getRollData()
|
||||
formulaNames.forEach(formulaName => {
|
||||
const formula = item.data.rolls.formulas[formulaName]
|
||||
rollData.rolls[formulaName] = {
|
||||
formula: formula.formula,
|
||||
label: formula.label,
|
||||
type: formula.type,
|
||||
result: "",
|
||||
total: 0,
|
||||
totalStr: "",
|
||||
dice: {}
|
||||
} as M5RollResult
|
||||
if (formula) {
|
||||
rollData.rolls[formulaName] = {
|
||||
formula: formula.formula,
|
||||
label: formula.label,
|
||||
enabled: formula.enabled,
|
||||
result: "",
|
||||
total: 0,
|
||||
totalStr: "",
|
||||
dice: {}
|
||||
} as M5RollResult
|
||||
}
|
||||
})
|
||||
|
||||
const roll = new M5Roll(rollData, this.actor, item.name)
|
||||
|
||||
@@ -17,19 +17,24 @@ export class M5Roll { // extends Roll<M5RollData>
|
||||
// @ts-ignore
|
||||
//override evaluate(options?: InexactPartial<RollTerm.EvaluationOptions>): Evaluated<Roll<M5RollData>> | Promise<Evaluated<Roll<M5RollData>>> {
|
||||
evaluate() {
|
||||
const indexMap = new Map<number, string>()
|
||||
const rollNames = Object.keys(this.data.rolls)
|
||||
const rolls = rollNames.map(rollName => {
|
||||
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 rollResult = this.data.rolls[index.toString()] as M5RollResult
|
||||
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()
|
||||
@@ -37,27 +42,29 @@ export class M5Roll { // extends Roll<M5RollData>
|
||||
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 (rollResult.type === "ew") {
|
||||
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 (rollResult.type === "pw") {
|
||||
if (d.total === 1)
|
||||
rowRes = M5EwResult.FUMBLE
|
||||
else if (d.total === 20)
|
||||
rowRes = M5EwResult.CRITICAL
|
||||
} else if (d.faces === 100) {
|
||||
face100 = d.total as number
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (rollResult.type === "ew") {
|
||||
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
|
||||
@@ -65,10 +72,18 @@ export class M5Roll { // extends Roll<M5RollData>
|
||||
if (rowRes === M5EwResult.TBD)
|
||||
rowRes = M5EwResult.PASS
|
||||
}
|
||||
} else if (rollResult.type === "pw") {
|
||||
if (roll.total < 0) {
|
||||
} 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
|
||||
@@ -114,7 +129,7 @@ export class M5Roll { // extends Roll<M5RollData>
|
||||
rollData.i = attribute.value + attribute.bonus
|
||||
rollData.rolls["0"] = {
|
||||
formula: "@i - 1d100",
|
||||
type: "pw",
|
||||
enabled: true,
|
||||
label: (game as Game).i18n.localize("midgard5.pw"),
|
||||
result: "",
|
||||
total: 0,
|
||||
@@ -135,7 +150,7 @@ export class M5Roll { // extends Roll<M5RollData>
|
||||
|
||||
rollData.rolls["0"] = {
|
||||
formula: "1d20 + @c.calc.stats.brawl + @c.calc.stats.attackBonus + @i.attackBonus",
|
||||
type: "ew",
|
||||
enabled: true,
|
||||
label: (game as Game).i18n.localize("midgard5.attack"),
|
||||
result: "",
|
||||
total: 0,
|
||||
@@ -146,7 +161,7 @@ export class M5Roll { // extends Roll<M5RollData>
|
||||
|
||||
rollData.rolls["1"] = {
|
||||
formula: "1d6 - 4 + @c.calc.stats.damageBonus + @i.damageBonus",
|
||||
type: "dmg",
|
||||
enabled: true,
|
||||
label: (game as Game).i18n.localize("midgard5.damage"),
|
||||
result: "",
|
||||
total: 0,
|
||||
@@ -157,4 +172,35 @@ export class M5Roll { // extends Roll<M5RollData>
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { M5Item } from "../items/M5Item"
|
||||
import { M5Attributes, M5ItemMod, M5ModOperation, M5ModType } from "../M5Base"
|
||||
import { M5Attributes, M5ItemMod, M5ModOperation, M5ModType, M5RollTemplate } from "../M5Base"
|
||||
|
||||
export class M5ItemSheet extends ItemSheet {
|
||||
|
||||
@@ -78,6 +78,54 @@ export class M5ItemSheet extends ItemSheet {
|
||||
this.render(false)
|
||||
})
|
||||
|
||||
html.find(".roll-delete").on("click", async (event) => {
|
||||
//console.log("roll-delete", this.item.data.data.rolls.formulas)
|
||||
|
||||
let row = event.target.parentElement
|
||||
let rollIndex = row.dataset["roll"]
|
||||
while (!rollIndex) {
|
||||
row = row.parentElement
|
||||
if (!row)
|
||||
return
|
||||
rollIndex = row.dataset["roll"]
|
||||
}
|
||||
|
||||
const rolls = this.item.data.data.rolls.formulas
|
||||
rolls[rollIndex] = null
|
||||
|
||||
this.item.update({
|
||||
data: {
|
||||
rolls: {
|
||||
formulas: rolls
|
||||
}
|
||||
}
|
||||
})
|
||||
this.render(false)
|
||||
})
|
||||
|
||||
html.find(".roll-create").on("click", async (event) => {
|
||||
const rolls = this.item.data.data.rolls.formulas
|
||||
|
||||
const indeces = Object.keys(rolls).map(index => parseInt(index)).sort().reverse()
|
||||
const index = (indeces.find(index => !!rolls[index.toString()]) ?? -1) + 1
|
||||
console.log("roll-create", rolls, indeces, index)
|
||||
|
||||
rolls[index.toString()] = {
|
||||
formula: "1d6",
|
||||
label: (game as Game).i18n.localize("midgard5.roll"),
|
||||
enabled: true
|
||||
} as M5RollTemplate
|
||||
|
||||
this.item.update({
|
||||
data: {
|
||||
rolls: {
|
||||
formulas: rolls
|
||||
}
|
||||
}
|
||||
})
|
||||
this.render(false)
|
||||
})
|
||||
|
||||
// Drag & Drop
|
||||
if (["item"].includes(this.object.data?.type)) {
|
||||
const itemToItemAssociation = new DragDrop({
|
||||
|
||||
Reference in New Issue
Block a user