-
Notifications
You must be signed in to change notification settings - Fork 223
/
dataUtil.tsx
152 lines (143 loc) · 6.73 KB
/
dataUtil.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
import { SubstatKey } from "pipeline";
import { input } from "../../Formula";
import { inferInfoMut, mergeData } from "../../Formula/api";
import { reactions } from "../../Formula/reaction";
import { Data, DisplaySub, NumNode } from "../../Formula/type";
import { constant, data, infoMut, lookup, percent, prod, stringPrio, subscript, sum, unit } from "../../Formula/utils";
import { allMainStatKeys, MainStatKey } from "../../Types/artifact";
import { CharacterKey, ElementKey, Region } from "../../Types/consts";
import { layeredAssignment, objectKeyMap, objectMap } from "../../Util/Util";
import _charCurves from "./expCurve_gen.json";
// TODO: Remove this conversion after changing the file format
const charCurves = objectMap(_charCurves, value => [0, ...Object.values(value)])
const commonBasic = objectKeyMap(["hp", "atk", "def", "eleMas", "enerRech_", "critRate_", "critDMG_", "heal_"], key => input.total[key])
commonBasic.critRate_ = input.total.cappedCritRate
export const infusionNode = stringPrio(
input.infusion.nonOverridableSelf,
input.infusion.team,
input.infusion.overridableSelf)
const inferredHitEle = stringPrio(
lookup(input.hit.move, {
"skill": input.charEle, "burst": input.charEle,
}, undefined),
lookup(input.weaponType, {
sword: infusionNode, claymore: infusionNode, polearm: infusionNode,
catalyst: input.charEle,
}, undefined),
"physical"
)
function getTalentType(move: "normal" | "charged" | "plunging" | "skill" | "burst") {
switch (move) {
case "normal": case "charged": case "plunging": return "auto";
case "skill": return "skill";
case "burst": return "burst";
}
}
/** Note: `additional` applies only to this formula */
export function customDmgNode(base: NumNode, move: "normal" | "charged" | "plunging" | "skill" | "burst" | "elemental", additional: Data = {}): NumNode {
return data(input.hit.dmg, mergeData([{
hit: { base, move: constant(move), ele: additional?.hit?.ele ? undefined : inferredHitEle },
}, additional]))
}
/** Note: `additional` applies only to this formula */
export function customShieldNode(base: NumNode, additional?: Data): NumNode {
const shieldNode = prod(base, sum(unit, input.total.shield_))
return additional ? data(shieldNode, additional) : shieldNode
}
/** Note: `additional` applies only to this formula */
export function customHealNode(base: NumNode, additional?: Data): NumNode {
const healNode = prod(base, sum(unit, input.total.heal_, input.total.incHeal_))
return additional ? data(healNode, additional) : healNode
}
/** Note: `additional` applies only to this formula */
export function dmgNode(base: MainStatKey | SubstatKey, lvlMultiplier: number[], move: "normal" | "charged" | "plunging" | "skill" | "burst", additional: Data = {}): NumNode {
const talentType = getTalentType(move)
return customDmgNode(prod(subscript(input.total[`${talentType}Index`], lvlMultiplier, { key: '_' }), input.total[base]), move, additional)
}
/** Note: `additional` applies only to this formula */
export function shieldNode(base: MainStatKey | SubstatKey, percent: NumNode | number, flat: NumNode | number, additional?: Data): NumNode {
return customShieldNode(sum(prod(percent, input.total[base]), flat), additional)
}
/** Note: `additional` applies only to this formula */
export function healNode(base: MainStatKey | SubstatKey, percent: NumNode | number, flat: NumNode | number, additional?: Data): NumNode {
return customHealNode(sum(prod(percent, input.total[base]), flat), additional)
}
/** Note: `additional` applies only to this formula */
export function shieldNodeTalent(base: MainStatKey | SubstatKey, baseMultiplier: number[], flat: number[], move: "normal" | "charged" | "plunging" | "skill" | "burst", additional?: Data): NumNode {
const talentType = getTalentType(move)
const talentIndex = input.total[`${talentType}Index`]
return customShieldNode(sum(
prod(subscript(talentIndex, baseMultiplier, { key: '_' }), input.total[base]),
subscript(talentIndex, flat)
), additional)
}
export function shieldElement(element: "electro" | "cryo" | "hydro" | "pyro" | "geo", shieldNode: NumNode) {
return infoMut(prod(percent(element === "geo" ? 1.5 : 2.5), shieldNode), { variant: element })
}
/** Note: `additional` applies only to this formula */
export function healNodeTalent(base: MainStatKey | SubstatKey, baseMultiplier: number[], flat: number[], move: "normal" | "charged" | "plunging" | "skill" | "burst", additional?: Data): NumNode {
const talentType = getTalentType(move)
const talentIndex = input.total[`${talentType}Index`]
return customHealNode(sum(
prod(subscript(talentIndex, baseMultiplier, { key: '_' }), input.total[base]),
subscript(talentIndex, flat)
), additional)
}
export function dataObjForCharacterSheet(
key: CharacterKey,
element: ElementKey | undefined,
region: Region | undefined,
gen: {
weaponTypeKey: string,
base: { hp: number, atk: number, def: number },
curves: { [key in string]?: string },
ascensions: { props: { [key in string]?: number } }[]
},
display: { [key: string]: DisplaySub },
additional: Data = {},
): Data {
function curve(base: number, lvlCurve: string): NumNode {
return prod(base, subscript(input.lvl, charCurves[lvlCurve]))
}
display.basic = { ...commonBasic }
const data: Data = {
charKey: constant(key),
base: {},
weaponType: constant(gen.weaponTypeKey),
premod: {},
display,
}
if (element) {
data.charEle = constant(element)
data.teamBuff = { tally: { [element]: constant(1) } }
data.display!.basic[`${element}_dmg_`] = input.total[`${element}_dmg_`]
data.display!.reaction = reactions[element]
}
if (region)
layeredAssignment(data, ["teamBuff", "tally", region], constant(1))
if (gen.weaponTypeKey !== "catalyst") {
if (!data.display!.basic) data.display!.basic = {}
data.display!.basic!.physical_dmg_ = input.total.physical_dmg_
}
let foundSpecial: boolean | undefined
for (const stat of [...allMainStatKeys, "def" as const]) {
const list: NumNode[] = []
if (gen.curves[stat])
list.push(curve(gen.base[stat], gen.curves[stat]!))
const asc = gen.ascensions.some(x => x.props[stat])
if (asc)
list.push(subscript(input.asc, gen.ascensions.map(x => x.props[stat] ?? NaN)))
if (!list.length) continue
const result = infoMut(list.length === 1 ? list[0] : sum(...list), { key: stat, prefix: "char", asConst: true })
if (stat.endsWith("_dmg_")) result.info!.variant = stat.slice(0, -5) as any
if (stat === "atk" || stat === "def" || stat === "hp")
data.base![stat] = result
else {
if (foundSpecial) throw new Error("Duplicated Char Special")
foundSpecial = true
data.special = result
data.premod![stat] = input.special
}
}
return mergeData([data, inferInfoMut(additional)])
}