Adds mod calculation
This commit is contained in:
parent
c8d59588e0
commit
c827693e72
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"ACTOR.TypeCharacter": "Spielfigur",
|
"ACTOR.TypeCharacter": "Charakter",
|
||||||
"ACTOR.TypeNpc": "Kreatur / Nichtspielerfigur",
|
"ACTOR.TypeNpc": "Kreatur / Nichtspielerfigur",
|
||||||
"ACTOR.TypeVehicle": "Transportmittel / Pferd etc.",
|
"ACTOR.TypeVehicle": "Transportmittel / Pferd etc.",
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ const preloadTemplates = async (): Promise<Handlebars.TemplateDelegate<any>[]> =
|
||||||
// const templates = [ rootPath + "actor/actor-sheet.hbs" ]
|
// const templates = [ rootPath + "actor/actor-sheet.hbs" ]
|
||||||
// This would map to our local folder of /Assets/Templates/Actor/actor-sheet.hbs
|
// This would map to our local folder of /Assets/Templates/Actor/actor-sheet.hbs
|
||||||
const templates: Array<string> = [
|
const templates: Array<string> = [
|
||||||
|
"sheets/character/attribute.hbs",
|
||||||
"sheets/character/base_values.hbs",
|
"sheets/character/base_values.hbs",
|
||||||
"sheets/character/skills.hbs",
|
"sheets/character/skills.hbs",
|
||||||
"sheets/character/gear.hbs",
|
"sheets/character/gear.hbs",
|
||||||
|
|
|
||||||
|
|
@ -20,12 +20,11 @@ Hooks.once("init", async () => {
|
||||||
return values.map(val => val.toString()).join(join)
|
return values.map(val => val.toString()).join(join)
|
||||||
})
|
})
|
||||||
|
|
||||||
// static concat(...values) {
|
Handlebars.registerHelper("add", (...values) => {
|
||||||
// const options = values.pop();
|
const options = values.pop();
|
||||||
// const join = options.hash?.join || "";
|
return values.reduce((prev, cur) => prev + cur)
|
||||||
// return new Handlebars.SafeString(values.join(join));
|
})
|
||||||
// }
|
|
||||||
|
|
||||||
Handlebars.registerHelper("localizeMidgard", (str: string) => {
|
Handlebars.registerHelper("localizeMidgard", (str: string) => {
|
||||||
const template = Handlebars.compile("{{localize value}}")
|
const template = Handlebars.compile("{{localize value}}")
|
||||||
return template({
|
return template({
|
||||||
|
|
|
||||||
|
|
@ -1,48 +1,4 @@
|
||||||
|
|
||||||
export interface M5CharacterCalculatedData {
|
|
||||||
level: number
|
|
||||||
attributes: {
|
|
||||||
st: { value: number, bonus: number }
|
|
||||||
gs: { value: number, bonus: number }
|
|
||||||
gw: { value: number, bonus: number }
|
|
||||||
ko: { value: number, bonus: number }
|
|
||||||
in: { value: number, bonus: number }
|
|
||||||
zt: { value: number, bonus: number }
|
|
||||||
au: { value: number, bonus: number }
|
|
||||||
pa: { value: number, bonus: number }
|
|
||||||
wk: { value: number, bonus: number }
|
|
||||||
}
|
|
||||||
stats: {
|
|
||||||
armor: number
|
|
||||||
defense: number
|
|
||||||
damageBonus: number
|
|
||||||
attackBonus: number
|
|
||||||
defenseBonus: number
|
|
||||||
movementBonus: number
|
|
||||||
resistanceMind: number
|
|
||||||
resistanceBody: number
|
|
||||||
spellCasting: number
|
|
||||||
brawl: number
|
|
||||||
brawlEw: number
|
|
||||||
poisonResistance: number
|
|
||||||
enduranceBonus: number
|
|
||||||
}
|
|
||||||
skills: {
|
|
||||||
innate: {}
|
|
||||||
general: {}
|
|
||||||
combat: {}
|
|
||||||
language: {}
|
|
||||||
custom: {}
|
|
||||||
}
|
|
||||||
gear: {
|
|
||||||
weapons: {}
|
|
||||||
defensiveWeapons: {}
|
|
||||||
armor: {}
|
|
||||||
items: {}
|
|
||||||
}
|
|
||||||
spells: {}
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface M5Skill {
|
export interface M5Skill {
|
||||||
fw: number
|
fw: number
|
||||||
attribute: string
|
attribute: string
|
||||||
|
|
@ -144,6 +100,73 @@ export interface M5ItemMod {
|
||||||
value: number
|
value: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface M5ModPair {
|
||||||
|
mod: M5ItemMod
|
||||||
|
source: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface M5ModSource {
|
||||||
|
item: string
|
||||||
|
operation: M5ModOperation
|
||||||
|
value: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface M5ModResult {
|
||||||
|
mods: Array<M5ModSource>
|
||||||
|
value: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface M5AttributeCalculated extends M5ModResult {
|
||||||
|
bonus: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface M5CharacterCalculatedData {
|
||||||
|
level: number
|
||||||
|
attributes: {
|
||||||
|
st: M5AttributeCalculated
|
||||||
|
gs: M5AttributeCalculated
|
||||||
|
gw: M5AttributeCalculated
|
||||||
|
ko: M5AttributeCalculated
|
||||||
|
in: M5AttributeCalculated
|
||||||
|
zt: M5AttributeCalculated
|
||||||
|
au: M5AttributeCalculated
|
||||||
|
pa: M5AttributeCalculated
|
||||||
|
wk: M5AttributeCalculated
|
||||||
|
}
|
||||||
|
stats: {
|
||||||
|
lp: M5ModResult
|
||||||
|
ap: M5ModResult
|
||||||
|
armor: number
|
||||||
|
defense: M5ModResult
|
||||||
|
damageBonus: M5ModResult
|
||||||
|
attackBonus: M5ModResult
|
||||||
|
defenseBonus: M5ModResult
|
||||||
|
movementBonus: M5ModResult
|
||||||
|
resistanceMind: M5ModResult
|
||||||
|
resistanceBody: M5ModResult
|
||||||
|
spellCasting: M5ModResult
|
||||||
|
brawl: M5ModResult
|
||||||
|
brawlEw: number
|
||||||
|
poisonResistance: M5ModResult
|
||||||
|
enduranceBonus: number
|
||||||
|
}
|
||||||
|
skillMods: {}
|
||||||
|
skills: {
|
||||||
|
innate: {}
|
||||||
|
general: {}
|
||||||
|
combat: {}
|
||||||
|
language: {}
|
||||||
|
custom: {}
|
||||||
|
}
|
||||||
|
gear: {
|
||||||
|
weapons: {}
|
||||||
|
defensiveWeapons: {}
|
||||||
|
armor: {}
|
||||||
|
items: {}
|
||||||
|
}
|
||||||
|
spells: {}
|
||||||
|
}
|
||||||
|
|
||||||
export function enumKeys<O extends object, K extends keyof O = keyof O>(obj: O): K[] {
|
export function enumKeys<O extends object, K extends keyof O = keyof O>(obj: O): K[] {
|
||||||
return Object.keys(obj).filter(k => Number.isNaN(+k)) as K[]
|
return Object.keys(obj).filter(k => Number.isNaN(+k)) as K[]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { M5Item } from "../items/M5Item"
|
import { M5Item } from "../items/M5Item"
|
||||||
import { M5Attribute, M5CharacterCalculatedData, M5RollData, M5Skill, M5SkillCalculated, M5SkillLearned } from "../M5Base"
|
import { M5Attribute, M5CharacterCalculatedData, M5ItemMod, M5ModOperation, M5ModResult, M5RollData, M5Skill, M5SkillCalculated, M5SkillLearned } from "../M5Base"
|
||||||
|
import M5ModAggregate from "./M5ModAggregate"
|
||||||
|
|
||||||
export class M5Character extends Actor {
|
export class M5Character extends Actor {
|
||||||
|
|
||||||
|
|
@ -28,35 +29,38 @@ export class M5Character extends Actor {
|
||||||
return -2
|
return -2
|
||||||
}
|
}
|
||||||
|
|
||||||
derivedData(skip: { skills?: boolean, weapons?: boolean, defensiveWeapons?: boolean, armor?: boolean, items?: boolean, spells?: boolean } = {} ): M5CharacterCalculatedData {
|
derivedData(skip: { mods?: boolean, skills?: boolean, weapons?: boolean, defensiveWeapons?: boolean, armor?: boolean, items?: boolean, spells?: boolean } = {} ): M5CharacterCalculatedData {
|
||||||
let ret: M5CharacterCalculatedData = {
|
let ret: M5CharacterCalculatedData = {
|
||||||
level: 0,
|
level: 0,
|
||||||
attributes: {
|
attributes: {
|
||||||
st: { value: 0, bonus: 0 },
|
st: { value: 0, bonus: 0, mods: [] },
|
||||||
gs: { value: 0, bonus: 0 },
|
gs: { value: 0, bonus: 0, mods: [] },
|
||||||
gw: { value: 0, bonus: 0 },
|
gw: { value: 0, bonus: 0, mods: [] },
|
||||||
ko: { value: 0, bonus: 0 },
|
ko: { value: 0, bonus: 0, mods: [] },
|
||||||
in: { value: 0, bonus: 0 },
|
in: { value: 0, bonus: 0, mods: [] },
|
||||||
zt: { value: 0, bonus: 0 },
|
zt: { value: 0, bonus: 0, mods: [] },
|
||||||
au: { value: 0, bonus: 0 },
|
au: { value: 0, bonus: 0, mods: [] },
|
||||||
pa: { value: 0, bonus: 0 },
|
pa: { value: 0, bonus: 0, mods: [] },
|
||||||
wk: { value: 0, bonus: 0 }
|
wk: { value: 0, bonus: 0, mods: [] }
|
||||||
},
|
},
|
||||||
stats: {
|
stats: {
|
||||||
|
lp: { value: 0, mods: [] },
|
||||||
|
ap: { value: 0, mods: [] },
|
||||||
armor: 0,
|
armor: 0,
|
||||||
defense: 0,
|
defense: { value: 0, mods: [] },
|
||||||
damageBonus: 0,
|
damageBonus: { value: 0, mods: [] },
|
||||||
attackBonus: 0,
|
attackBonus: { value: 0, mods: [] },
|
||||||
defenseBonus: 0,
|
defenseBonus: { value: 0, mods: [] },
|
||||||
movementBonus: 0,
|
movementBonus: { value: 0, mods: [] },
|
||||||
resistanceMind: 0,
|
resistanceMind: { value: 0, mods: [] },
|
||||||
resistanceBody: 0,
|
resistanceBody: { value: 0, mods: [] },
|
||||||
spellCasting: 0,
|
spellCasting: { value: 0, mods: [] },
|
||||||
brawl: 0,
|
brawl: { value: 0, mods: [] },
|
||||||
brawlEw: 0,
|
brawlEw: 0,
|
||||||
poisonResistance: 0,
|
poisonResistance: { value: 0, mods: [] },
|
||||||
enduranceBonus: 0
|
enduranceBonus: 0
|
||||||
},
|
},
|
||||||
|
skillMods: {},
|
||||||
skills: {
|
skills: {
|
||||||
innate: {},
|
innate: {},
|
||||||
general: {},
|
general: {},
|
||||||
|
|
@ -103,20 +107,37 @@ export class M5Character extends Actor {
|
||||||
ret.attributes.pa.bonus = M5Character.attributeBonus(data.attributes.pa)
|
ret.attributes.pa.bonus = M5Character.attributeBonus(data.attributes.pa)
|
||||||
ret.attributes.wk.bonus = M5Character.attributeBonus(data.attributes.wk)
|
ret.attributes.wk.bonus = M5Character.attributeBonus(data.attributes.wk)
|
||||||
|
|
||||||
|
ret.stats.lp = this.modResult(data.lp)
|
||||||
|
ret.stats.ap = this.modResult(data.ap)
|
||||||
ret.stats.armor = 0
|
ret.stats.armor = 0
|
||||||
ret.stats.defense = M5Character.defenseFromLevel(ret.level)
|
ret.stats.defense = this.modResult(M5Character.defenseFromLevel(ret.level))
|
||||||
ret.stats.damageBonus = Math.floor(ret.attributes.st.value/20) + Math.floor(ret.attributes.gs.value/30) - 3
|
ret.stats.damageBonus = this.modResult(Math.floor(ret.attributes.st.value/20) + Math.floor(ret.attributes.gs.value/30) - 3)
|
||||||
ret.stats.attackBonus = ret.attributes.gs.bonus
|
ret.stats.attackBonus = this.modResult(ret.attributes.gs.bonus)
|
||||||
ret.stats.defenseBonus = ret.attributes.gw.bonus
|
ret.stats.defenseBonus = this.modResult(ret.attributes.gw.bonus)
|
||||||
ret.stats.movementBonus = 0
|
ret.stats.movementBonus = this.modResult(0)
|
||||||
ret.stats.resistanceMind = ret.stats.defense
|
ret.stats.resistanceMind = this.modResult(ret.stats.defense.value)
|
||||||
ret.stats.resistanceBody = ret.stats.defense + 1
|
ret.stats.resistanceBody = this.modResult(ret.stats.defense.value + 1)
|
||||||
ret.stats.spellCasting = (data.info.magicUsing ? M5Character.spellCastingFromLevel(ret.level) : 3) + ret.attributes.zt.bonus
|
ret.stats.spellCasting = this.modResult((data.info.magicUsing ? M5Character.spellCastingFromLevel(ret.level) : 3) + ret.attributes.zt.bonus)
|
||||||
ret.stats.brawl = Math.floor((ret.attributes.st.value + ret.attributes.gw.value) / 20)
|
ret.stats.brawl = this.modResult(Math.floor((ret.attributes.st.value + ret.attributes.gw.value) / 20))
|
||||||
ret.stats.brawlEw = ret.stats.brawl + ret.stats.attackBonus
|
ret.stats.brawlEw = ret.stats.brawl.value + ret.stats.attackBonus.value
|
||||||
ret.stats.poisonResistance = 30 + Math.floor(ret.attributes.ko.value / 2)
|
ret.stats.poisonResistance = this.modResult(30 + Math.floor(ret.attributes.ko.value / 2))
|
||||||
ret.stats.enduranceBonus = Math.floor(ret.attributes.ko.value/10) + Math.floor(ret.attributes.st.value/20)
|
ret.stats.enduranceBonus = Math.floor(ret.attributes.ko.value/10) + Math.floor(ret.attributes.st.value/20)
|
||||||
|
|
||||||
|
if (!skip.mods) {
|
||||||
|
const aggregate = new M5ModAggregate(data, ret)
|
||||||
|
|
||||||
|
context.items?.filter(item => item.data.type === "item").forEach(item => {
|
||||||
|
const mods = item.data.data.mods
|
||||||
|
console.log("Actor item mods", mods)
|
||||||
|
Object.keys(mods).forEach(modIndex => {
|
||||||
|
const mod = mods[modIndex] as M5ItemMod
|
||||||
|
aggregate.push(mod, item.data.name)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
ret.skillMods = aggregate.calculate()
|
||||||
|
}
|
||||||
|
|
||||||
if (!skip.items) {
|
if (!skip.items) {
|
||||||
context.items?.filter(item => item.data.type === "item").forEach(item => {
|
context.items?.filter(item => item.data.type === "item").forEach(item => {
|
||||||
item.prepareDerivedData()
|
item.prepareDerivedData()
|
||||||
|
|
@ -306,4 +327,15 @@ export class M5Character extends Actor {
|
||||||
return (this as any).getEmbeddedDocument("Item", itemId)
|
return (this as any).getEmbeddedDocument("Item", itemId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private modResult(value: number): M5ModResult {
|
||||||
|
return {
|
||||||
|
value: value,
|
||||||
|
mods: [{
|
||||||
|
item: (game as Game).i18n.localize("ACTOR.TypeCharacter"),
|
||||||
|
operation: M5ModOperation.SET,
|
||||||
|
value: value
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,164 @@
|
||||||
|
import { M5Attribute, M5AttributeCalculated, M5Attributes, M5CharacterCalculatedData, M5ItemMod, M5ModOperation, M5ModResult, M5ModSource, M5ModType, M5Stats, M5ModPair } from "../M5Base"
|
||||||
|
|
||||||
|
export default class M5ModAggregate {
|
||||||
|
private attributes = new Map<string, Array<M5ModPair>>()
|
||||||
|
private stats = new Map<string, Array<M5ModPair>>()
|
||||||
|
private skills = new Map<string, Array<M5ModPair>>()
|
||||||
|
|
||||||
|
constructor(public data: any, public calc: M5CharacterCalculatedData) {
|
||||||
|
const characterString = (game as Game).i18n.localize("ACTOR.TypeCharacter")
|
||||||
|
|
||||||
|
this.push({ type: M5ModType.ATTRIBUTE, id: M5Attributes.ST, operation: M5ModOperation.SET, value: data.attributes.st.value }, characterString)
|
||||||
|
this.push({ type: M5ModType.ATTRIBUTE, id: M5Attributes.GS, operation: M5ModOperation.SET, value: data.attributes.gs.value }, characterString)
|
||||||
|
this.push({ type: M5ModType.ATTRIBUTE, id: M5Attributes.GW, operation: M5ModOperation.SET, value: data.attributes.gw.value }, characterString)
|
||||||
|
this.push({ type: M5ModType.ATTRIBUTE, id: M5Attributes.KO, operation: M5ModOperation.SET, value: data.attributes.ko.value }, characterString)
|
||||||
|
this.push({ type: M5ModType.ATTRIBUTE, id: M5Attributes.IN, operation: M5ModOperation.SET, value: data.attributes.in.value }, characterString)
|
||||||
|
this.push({ type: M5ModType.ATTRIBUTE, id: M5Attributes.ZT, operation: M5ModOperation.SET, value: data.attributes.zt.value }, characterString)
|
||||||
|
this.push({ type: M5ModType.ATTRIBUTE, id: M5Attributes.AU, operation: M5ModOperation.SET, value: data.attributes.au.value }, characterString)
|
||||||
|
this.push({ type: M5ModType.ATTRIBUTE, id: M5Attributes.PA, operation: M5ModOperation.SET, value: data.attributes.pa.value }, characterString)
|
||||||
|
this.push({ type: M5ModType.ATTRIBUTE, id: M5Attributes.WK, operation: M5ModOperation.SET, value: data.attributes.wk.value }, characterString)
|
||||||
|
|
||||||
|
this.push({ type: M5ModType.ATTRIBUTE, id: M5Attributes.ST, operation: M5ModOperation.ADD_100, value: data.attributes.st.bonus }, characterString)
|
||||||
|
this.push({ type: M5ModType.ATTRIBUTE, id: M5Attributes.GS, operation: M5ModOperation.ADD_100, value: data.attributes.gs.bonus }, characterString)
|
||||||
|
this.push({ type: M5ModType.ATTRIBUTE, id: M5Attributes.GW, operation: M5ModOperation.ADD_100, value: data.attributes.gw.bonus }, characterString)
|
||||||
|
this.push({ type: M5ModType.ATTRIBUTE, id: M5Attributes.KO, operation: M5ModOperation.ADD_100, value: data.attributes.ko.bonus }, characterString)
|
||||||
|
this.push({ type: M5ModType.ATTRIBUTE, id: M5Attributes.IN, operation: M5ModOperation.ADD_100, value: data.attributes.in.bonus }, characterString)
|
||||||
|
this.push({ type: M5ModType.ATTRIBUTE, id: M5Attributes.ZT, operation: M5ModOperation.ADD_100, value: data.attributes.zt.bonus }, characterString)
|
||||||
|
this.push({ type: M5ModType.ATTRIBUTE, id: M5Attributes.AU, operation: M5ModOperation.ADD_100, value: data.attributes.au.bonus }, characterString)
|
||||||
|
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.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)
|
||||||
|
this.push({ type: M5ModType.STAT, id: M5Stats.RESISTANCE_MIND, operation: M5ModOperation.SET, value: calc.stats.resistanceMind.value }, characterString)
|
||||||
|
this.push({ type: M5ModType.STAT, id: M5Stats.RESISTANCE_BODY, operation: M5ModOperation.SET, value: calc.stats.resistanceBody.value }, characterString)
|
||||||
|
this.push({ type: M5ModType.STAT, id: M5Stats.SPELL_CASTING, operation: M5ModOperation.SET, value: calc.stats.spellCasting.value }, characterString)
|
||||||
|
this.push({ type: M5ModType.STAT, id: M5Stats.BRAWL, operation: M5ModOperation.SET, value: calc.stats.brawl.value }, characterString)
|
||||||
|
this.push({ type: M5ModType.STAT, id: M5Stats.POISON_RESISTANCE, operation: M5ModOperation.SET, value: calc.stats.poisonResistance.value }, characterString)
|
||||||
|
this.push({ type: M5ModType.STAT, id: M5Stats.LP, operation: M5ModOperation.SET, value: calc.stats.lp.value }, characterString)
|
||||||
|
this.push({ type: M5ModType.STAT, id: M5Stats.AP, operation: M5ModOperation.SET, value: calc.stats.ap.value }, characterString)
|
||||||
|
}
|
||||||
|
|
||||||
|
push(mod: M5ItemMod, source: string) {
|
||||||
|
if (!mod?.id || mod.id === "")
|
||||||
|
return
|
||||||
|
|
||||||
|
let map: Map<string, Array<M5ModPair>> = null
|
||||||
|
if (mod.type === M5ModType.ATTRIBUTE)
|
||||||
|
map = this.attributes
|
||||||
|
else if (mod.type === M5ModType.STAT)
|
||||||
|
map = this.stats
|
||||||
|
else if (mod.type === M5ModType.SKILL)
|
||||||
|
map = this.skills
|
||||||
|
|
||||||
|
if (map) {
|
||||||
|
const pair: M5ModPair = {
|
||||||
|
mod: mod,
|
||||||
|
source: source
|
||||||
|
}
|
||||||
|
|
||||||
|
let key = mod.id
|
||||||
|
if (mod.type === M5ModType.ATTRIBUTE)
|
||||||
|
key = (mod.id + "").toLowerCase()
|
||||||
|
else if (mod.type === M5ModType.STAT)
|
||||||
|
key = (mod.id + "").toLowerCase()
|
||||||
|
|
||||||
|
if (map.has(key))
|
||||||
|
map.get(key).push(pair)
|
||||||
|
else
|
||||||
|
map.set(key, [pair])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
calculate() {
|
||||||
|
const calc = this.calc
|
||||||
|
|
||||||
|
this.attributes.forEach((pairs, id) => {
|
||||||
|
const res = M5ModAggregate.processPairs(pairs)
|
||||||
|
calc.attributes[id] = {
|
||||||
|
value: res.value,
|
||||||
|
bonus: M5ModAggregate.attributeBonus(res.value),
|
||||||
|
mods: res.mods
|
||||||
|
} as M5AttributeCalculated
|
||||||
|
//console.log("calc.attributes." + id, calc.attributes[id])
|
||||||
|
})
|
||||||
|
|
||||||
|
this.stats.forEach((pairs, id) => {
|
||||||
|
const res = M5ModAggregate.processPairs(pairs)
|
||||||
|
calc.stats[id] = res
|
||||||
|
})
|
||||||
|
|
||||||
|
const ret = {}
|
||||||
|
this.skills.forEach((pairs, id) => {
|
||||||
|
ret[id] = pairs
|
||||||
|
})
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
static pairAsSource(pair: M5ModPair): M5ModSource {
|
||||||
|
return {
|
||||||
|
operation: pair.mod.operation,
|
||||||
|
value: pair.mod.value,
|
||||||
|
item: pair.source
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static processPairs(arr: Array<M5ModPair>): M5ModResult {
|
||||||
|
let ret: M5ModResult = {
|
||||||
|
mods: [],
|
||||||
|
value: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
let mods = arr.filter(pair => pair.mod.operation === M5ModOperation.FIXED).sort((a, b) => b.mod.value - a.mod.value)
|
||||||
|
let pair = mods.length === 0 ? null : mods[0]
|
||||||
|
|
||||||
|
if (pair) {
|
||||||
|
ret.mods.push(this.pairAsSource(pair))
|
||||||
|
ret.value = pair.mod.value
|
||||||
|
} else {
|
||||||
|
mods = arr.filter(pair => pair.mod.operation === M5ModOperation.SET).sort((a, b) => b.mod.value - a.mod.value)
|
||||||
|
if (mods.length !== 0) {
|
||||||
|
ret.mods.push(this.pairAsSource(mods[0]))
|
||||||
|
ret.value = mods[0].mod.value
|
||||||
|
}
|
||||||
|
|
||||||
|
mods = arr.filter(pair => pair.mod.operation === M5ModOperation.ADD_100)
|
||||||
|
if (mods.length !== 0) {
|
||||||
|
ret.mods = ret.mods.concat(mods.map(this.pairAsSource))
|
||||||
|
const bonus = mods.map(p => p.mod.value).reduce((a, b) => a + b, 0)
|
||||||
|
ret.value = Math.min(100, Math.max(0, ret.value + bonus))
|
||||||
|
}
|
||||||
|
|
||||||
|
mods = arr.filter(pair => pair.mod.operation === M5ModOperation.ADD)
|
||||||
|
if (mods.length !== 0) {
|
||||||
|
ret.mods = ret.mods.concat(mods.map(this.pairAsSource))
|
||||||
|
const bonus = mods.map(p => p.mod.value).reduce((a, b) => a + b, 0)
|
||||||
|
ret.value = Math.max(0, ret.value + bonus)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
static attributeMinMax(attribute: M5Attribute) {
|
||||||
|
return Math.min(100, Math.max(0, attribute.value + attribute.bonus))
|
||||||
|
}
|
||||||
|
|
||||||
|
static attributeBonus(value: number) {
|
||||||
|
if (value > 95)
|
||||||
|
return 2
|
||||||
|
if (value > 80)
|
||||||
|
return 1
|
||||||
|
if (value > 20)
|
||||||
|
return 0
|
||||||
|
if (value > 5)
|
||||||
|
return -1
|
||||||
|
return -2
|
||||||
|
}
|
||||||
|
|
||||||
|
//static modToString(mod: M5ItemMod): string { }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import { ItemData } from "@league-of-foundry-developers/foundry-vtt-types/src/foundry/common/data/module.mjs"
|
|
||||||
import { ConstructorDataType } from "@league-of-foundry-developers/foundry-vtt-types/src/types/helperTypes"
|
|
||||||
import { M5Character } from "../actors/M5Character"
|
import { M5Character } from "../actors/M5Character"
|
||||||
import { enumKeys, M5Attributes, M5ItemMod, M5ModType, M5RollData, M5RollResult, M5Skill, M5Stats } from "../M5Base"
|
import M5ModAggregate from "../actors/M5ModAggregate"
|
||||||
|
import { enumKeys, M5Attributes, M5ModOperation, M5ModPair, M5ModType, M5RollData, M5RollResult, M5Stats } from "../M5Base"
|
||||||
import { M5Roll } from "../rolls/M5Roll"
|
import { M5Roll } from "../rolls/M5Roll"
|
||||||
|
|
||||||
export class M5Item extends Item {
|
export class M5Item extends Item {
|
||||||
|
|
@ -14,15 +13,48 @@ export class M5Item extends Item {
|
||||||
const calc = context.data.calc
|
const calc = context.data.calc
|
||||||
|
|
||||||
if (context.type === "skill") {
|
if (context.type === "skill") {
|
||||||
|
calc.fw = context.data.fw
|
||||||
calc.bonus = 0
|
calc.bonus = 0
|
||||||
|
|
||||||
if (context.data?.attribute && context.data?.attribute !== "" && character) {
|
let pairs: Array<M5ModPair> = [{
|
||||||
const attribute = character.attribute(context.data.attribute)
|
source: context.name,
|
||||||
if (attribute)
|
mod: {
|
||||||
calc.bonus += M5Character.attributeBonus(attribute)
|
type: M5ModType.SKILL,
|
||||||
|
id: context._id,
|
||||||
|
operation: M5ModOperation.SET,
|
||||||
|
value: context.data.fw
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
|
||||||
|
if (character) {
|
||||||
|
const actorCalc = character.derivedData({ skills: true, weapons: true, defensiveWeapons: true, armor: true, items: true, spells: true })
|
||||||
|
if (actorCalc?.skillMods && Object.keys(actorCalc.skillMods).indexOf(context._id) !== -1) {
|
||||||
|
pairs = pairs.concat(actorCalc.skillMods[context._id])
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context.data?.attribute && context.data?.attribute !== "") {
|
||||||
|
pairs.push({
|
||||||
|
source: context.name,
|
||||||
|
mod: {
|
||||||
|
type: M5ModType.SKILL,
|
||||||
|
id: context._id,
|
||||||
|
operation: M5ModOperation.ADD,
|
||||||
|
value: actorCalc.attributes[context.data.attribute].bonus
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
calc.ew = context.data.fw + calc.bonus
|
const res = M5ModAggregate.processPairs(pairs)
|
||||||
|
res.mods.forEach(mod => {
|
||||||
|
if ([M5ModOperation.SET, M5ModOperation.FIXED].includes(mod.operation))
|
||||||
|
calc.fw = mod.value
|
||||||
|
else
|
||||||
|
calc.bonus += mod.value
|
||||||
|
})
|
||||||
|
|
||||||
|
calc.ew = calc.fw + calc.bonus
|
||||||
|
calc.sources = res.mods
|
||||||
} else if (context.type === "weapon") {
|
} else if (context.type === "weapon") {
|
||||||
calc.fw = 0
|
calc.fw = 0
|
||||||
calc.bonus = 0
|
calc.bonus = 0
|
||||||
|
|
@ -57,7 +89,7 @@ export class M5Item extends Item {
|
||||||
if (actor) {
|
if (actor) {
|
||||||
const actorCalc = character.derivedData({ weapons: true, defensiveWeapons: true, armor: true, items: true, spells: true })
|
const actorCalc = character.derivedData({ weapons: true, defensiveWeapons: true, armor: true, items: true, spells: true })
|
||||||
if (actorCalc) {
|
if (actorCalc) {
|
||||||
calc.ew += actorCalc.stats.defense + actorCalc.stats.defenseBonus
|
calc.ew += actorCalc.stats.defense.value + actorCalc.stats.defenseBonus.value
|
||||||
calc.combatSkills = actorCalc.skills.combat
|
calc.combatSkills = actorCalc.skills.combat
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -79,43 +111,6 @@ export class M5Item extends Item {
|
||||||
calc.ew += actorCalc.stats.spellCasting
|
calc.ew += actorCalc.stats.spellCasting
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (context.type === "mod") {
|
|
||||||
const parent = (this as any).parent
|
|
||||||
const actor = parent?.actor
|
|
||||||
const character = actor as M5Character
|
|
||||||
|
|
||||||
calc.ids = {}
|
|
||||||
|
|
||||||
switch (context.data.type as M5ModType) {
|
|
||||||
case M5ModType.ATTRIBUTE: {
|
|
||||||
for (const key of enumKeys(M5Attributes)) {
|
|
||||||
const val = M5Attributes[key]
|
|
||||||
calc.ids[key] = (game as Game).i18n.localize(`midgard5.actor-${val}-long`)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case M5ModType.STAT: {
|
|
||||||
for (const key of enumKeys(M5Stats)) {
|
|
||||||
const val = M5Stats[key]
|
|
||||||
calc.ids[key] = (game as Game).i18n.localize(`midgard5.mod-stat-${val}`)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case M5ModType.SKILL: {
|
|
||||||
if (character) {
|
|
||||||
const actorCalc = character.derivedData({ weapons: true, defensiveWeapons: true, armor: true, items: true, spells: true })
|
|
||||||
if (actorCalc) {
|
|
||||||
let category = (game as Game).i18n.localize("midgard5.innate-ability")
|
|
||||||
Object.keys(actorCalc.skills.innate).forEach(skillId => {
|
|
||||||
const skill = character.getItem(skillId)
|
|
||||||
if (skill)
|
|
||||||
calc.ids[skillId] = `${category}: ${skill.data.name}`
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (context.type === "item") {
|
} else if (context.type === "item") {
|
||||||
calc.mods = {}
|
calc.mods = {}
|
||||||
Object.keys(context.data?.mods).forEach(key => {
|
Object.keys(context.data?.mods).forEach(key => {
|
||||||
|
|
@ -140,7 +135,28 @@ export class M5Item extends Item {
|
||||||
if (character) {
|
if (character) {
|
||||||
const actorCalc = character.derivedData({ weapons: true, defensiveWeapons: true, armor: true, items: true, spells: true })
|
const actorCalc = character.derivedData({ weapons: true, defensiveWeapons: true, armor: true, items: true, spells: true })
|
||||||
if (actorCalc) {
|
if (actorCalc) {
|
||||||
let category = (game as Game).i18n.localize("midgard5.innate-ability")
|
let category = (game as Game).i18n.localize("midgard5.skill")
|
||||||
|
Object.keys(actorCalc.skills.general).forEach(skillId => {
|
||||||
|
const skill = character.getItem(skillId)
|
||||||
|
if (skill)
|
||||||
|
modCalc[skillId] = `${category}: ${skill.data.name}`
|
||||||
|
})
|
||||||
|
|
||||||
|
category = (game as Game).i18n.localize("midgard5.language")
|
||||||
|
Object.keys(actorCalc.skills.language).forEach(skillId => {
|
||||||
|
const skill = character.getItem(skillId)
|
||||||
|
if (skill)
|
||||||
|
modCalc[skillId] = `${category}: ${skill.data.name}`
|
||||||
|
})
|
||||||
|
|
||||||
|
category = (game as Game).i18n.localize("midgard5.weapon-skill")
|
||||||
|
Object.keys(actorCalc.skills.combat).forEach(skillId => {
|
||||||
|
const skill = character.getItem(skillId)
|
||||||
|
if (skill)
|
||||||
|
modCalc[skillId] = `${category}: ${skill.data.name}`
|
||||||
|
})
|
||||||
|
|
||||||
|
category = (game as Game).i18n.localize("midgard5.innate-ability")
|
||||||
Object.keys(actorCalc.skills.innate).forEach(skillId => {
|
Object.keys(actorCalc.skills.innate).forEach(skillId => {
|
||||||
const skill = character.getItem(skillId)
|
const skill = character.getItem(skillId)
|
||||||
if (skill)
|
if (skill)
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,13 @@ export default class M5CharacterSheet extends ActorSheet {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override setPosition(options = {}) {
|
||||||
|
const position = super.setPosition(options)
|
||||||
|
const fillerWidth = this.element.find(".attributes .filler:first").width()
|
||||||
|
this.element.find(".attributes .attribute-filler-fixed").width(fillerWidth)
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
override activateListeners(html: JQuery) {
|
override activateListeners(html: JQuery) {
|
||||||
super.activateListeners(html)
|
super.activateListeners(html)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
// main: midgard5.less
|
// main: midgard5.less
|
||||||
|
@borderGroove: 2px groove #eeede0;
|
||||||
|
@attributeBorderColor: rgba(0, 0, 0, 0.5);
|
||||||
|
|
||||||
.midgard5 {
|
.midgard5 {
|
||||||
.flexrow {
|
.flexrow {
|
||||||
|
|
@ -83,4 +85,69 @@
|
||||||
color:rgb(93, 93, 93);
|
color:rgb(93, 93, 93);
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.filler {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attributes {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
|
||||||
|
.attribute {
|
||||||
|
flex: 0 0 7rem;
|
||||||
|
margin: 0;
|
||||||
|
border: 1px solid @attributeBorderColor;
|
||||||
|
//border-bottom: none;
|
||||||
|
border-radius: 10;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.attribute-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
font-weight: bold;
|
||||||
|
background-color: @attributeBorderColor;
|
||||||
|
color:rgba(255, 255, 255, 1);
|
||||||
|
|
||||||
|
//font-size: 1.0rem;
|
||||||
|
height: 2.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attribute-main {
|
||||||
|
padding: 0.2rem;
|
||||||
|
|
||||||
|
.attribute-main-value {
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attribute-main-bonus {
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.attribute-footer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 0.3rem;
|
||||||
|
padding: 0.3rem;
|
||||||
|
background-color: @attributeBorderColor;
|
||||||
|
|
||||||
|
input {
|
||||||
|
flex-grow: 1;
|
||||||
|
text-align: center;
|
||||||
|
background-color: rgba(255, 255, 255, 0.8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -216,7 +216,7 @@
|
||||||
"rolls": {
|
"rolls": {
|
||||||
"formulas": {
|
"formulas": {
|
||||||
"0": {
|
"0": {
|
||||||
"formula": "1d20 + @i.fw + @i.calc.bonus",
|
"formula": "1d20 + @i.calc.fw + @i.calc.bonus",
|
||||||
"type": "ew",
|
"type": "ew",
|
||||||
"label": "EW",
|
"label": "EW",
|
||||||
"enabled": true
|
"enabled": true
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
<div class="attribute">
|
||||||
|
<div class="attribute-header">{{localize (m5concat "midgard5.actor-" attributeId "-long")}}</div>
|
||||||
|
|
||||||
|
<div class="attribute-main">
|
||||||
|
<div class="attribute-main-value">{{calc.value}}</div>
|
||||||
|
<div class="attribute-main-bonus">{{calc.bonus}}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="attribute-footer">
|
||||||
|
<input class="attribute-footer-value" name="data.attributes.{{attributeId}}.value" value="{{attribute.value}}" type="text" data-dtype="Number" />
|
||||||
|
<input class="attribute-footer-bonus" name="data.attributes.{{attributeId}}.bonus" value="{{attribute.bonus}}" type="text" data-dtype="Number" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
@ -29,6 +29,32 @@
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<h3>Leiteigenschaften</h3>
|
<h3>Leiteigenschaften</h3>
|
||||||
|
|
||||||
|
<div class="attributes">
|
||||||
|
{{> "systems/midgard5/templates/sheets/character/attribute.hbs" attributeId="st" attribute=data.attributes.st calc=data.calc.attributes.st}}
|
||||||
|
<div class="filler"></div>
|
||||||
|
{{> "systems/midgard5/templates/sheets/character/attribute.hbs" attributeId="gs" attribute=data.attributes.gs calc=data.calc.attributes.gs}}
|
||||||
|
<div class="filler"></div>
|
||||||
|
{{> "systems/midgard5/templates/sheets/character/attribute.hbs" attributeId="gw" attribute=data.attributes.gw calc=data.calc.attributes.gw}}
|
||||||
|
<div class="filler"></div>
|
||||||
|
{{> "systems/midgard5/templates/sheets/character/attribute.hbs" attributeId="ko" attribute=data.attributes.ko calc=data.calc.attributes.ko}}
|
||||||
|
<div class="filler"></div>
|
||||||
|
{{> "systems/midgard5/templates/sheets/character/attribute.hbs" attributeId="in" attribute=data.attributes.in calc=data.calc.attributes.in}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="attributes">
|
||||||
|
<div class="filler"></div>
|
||||||
|
{{> "systems/midgard5/templates/sheets/character/attribute.hbs" attributeId="zt" attribute=data.attributes.zt calc=data.calc.attributes.zt}}
|
||||||
|
<div class="attribute-filler-fixed"></div>
|
||||||
|
{{> "systems/midgard5/templates/sheets/character/attribute.hbs" attributeId="au" attribute=data.attributes.au calc=data.calc.attributes.au}}
|
||||||
|
<div class="attribute-filler-fixed"></div>
|
||||||
|
{{> "systems/midgard5/templates/sheets/character/attribute.hbs" attributeId="pa" attribute=data.attributes.pa calc=data.calc.attributes.pa}}
|
||||||
|
<div class="attribute-filler-fixed"></div>
|
||||||
|
{{> "systems/midgard5/templates/sheets/character/attribute.hbs" attributeId="wk" attribute=data.attributes.wk calc=data.calc.attributes.wk}}
|
||||||
|
<div class="filler"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!--
|
||||||
<table>
|
<table>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
|
|
@ -81,6 +107,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
-->
|
||||||
|
|
||||||
<h3>Berechnete Werte</h3>
|
<h3>Berechnete Werte</h3>
|
||||||
<table>
|
<table>
|
||||||
|
|
|
||||||
|
|
@ -75,3 +75,18 @@
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{{localize "ITEM.TypeItem"}}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{{#each data.calc.gear.items as |item itemId|}}
|
||||||
|
<tr data-item="{{itemId}}">
|
||||||
|
<td class="padding edit-item">{{item.label}}</td>
|
||||||
|
</tr>
|
||||||
|
{{/each}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@
|
||||||
<td>
|
<td>
|
||||||
<select class="select-id" name="data.mods.{{modId}}.id" data-type="String">
|
<select class="select-id" name="data.mods.{{modId}}.id" data-type="String">
|
||||||
{{#select mod.id}}
|
{{#select mod.id}}
|
||||||
|
<option value="">{{localize "midgard5.no-skill"}}</option>
|
||||||
{{#each (lookup ../data.calc.mods modId) as |name key|}}
|
{{#each (lookup ../data.calc.mods modId) as |name key|}}
|
||||||
<option value="{{key}}">{{name}}</option>
|
<option value="{{key}}">{{name}}</option>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@
|
||||||
<td>
|
<td>
|
||||||
<select class="select-id" name="data.id" data-type="String">
|
<select class="select-id" name="data.id" data-type="String">
|
||||||
{{#select data.id}}
|
{{#select data.id}}
|
||||||
|
<option value="">{{localize "midgard5.no-skill"}}</option>
|
||||||
{{#each data.calc.ids as |name key|}}
|
{{#each data.calc.ids as |name key|}}
|
||||||
<option value="{{key}}">{{name}}</option>
|
<option value="{{key}}">{{name}}</option>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue