-
Notifications
You must be signed in to change notification settings - Fork 381
Expand file tree
/
Copy pathTestAttacks_spec.lua
More file actions
349 lines (309 loc) · 11.9 KB
/
TestAttacks_spec.lua
File metadata and controls
349 lines (309 loc) · 11.9 KB
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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
describe("TestAttacks", function()
before_each(function()
newBuild()
end)
teardown(function()
-- newBuild() takes care of resetting everything in setup()
end)
it("creates an item and has the correct crit chance", function()
assert.are.equals(build.calcsTab.mainOutput.CritChance, data.unarmedWeaponData[0].CritChance * build.calcsTab.mainOutput.HitChance / 100)
build.itemsTab:CreateDisplayItemFromRaw([[
New Item
Heavy Bow
]])
build.itemsTab:AddDisplayItem()
runCallback("OnFrame")
assert.are.equals(build.calcsTab.mainOutput.CritChance, 5 * build.calcsTab.mainOutput.HitChance / 100)
end)
it("creates an item and has the correct crit multi", function()
assert.are.equals(2, build.calcsTab.mainOutput.CritMultiplier)
build.itemsTab:CreateDisplayItemFromRaw([[
New Item
Heavy Bow
25% increased Critical Damage Bonus
]])
build.itemsTab:AddDisplayItem()
runCallback("OnFrame")
assert.are.equals(2 + 0.25, build.calcsTab.mainOutput.CritMultiplier)
end)
it("correctly converts spell damage per stat to attack damage", function()
assert.are.equals(0, build.calcsTab.mainEnv.player.modDB:Sum("INC", { flags = ModFlag.Attack }, "Damage"))
build.itemsTab:CreateDisplayItemFromRaw([[
New Item
Ring
10% increased attack damage
10% increased spell damage
+20 to Intelligence
1% increased spell damage per 10 intelligence
]])
build.itemsTab:AddDisplayItem()
runCallback("OnFrame")
assert.are.equals(10, build.calcsTab.mainEnv.player.modDB:Sum("INC", { flags = ModFlag.Attack }, "Damage"))
-- Scion starts with 20 Intelligence
assert.are.equals(12, build.calcsTab.mainEnv.player.modDB:Sum("INC", { flags = ModFlag.Spell }, "Damage"))
build.itemsTab:CreateDisplayItemFromRaw([[
New Item
Ring
increases and reductions to spell damage also apply to attacks
]])
build.itemsTab:AddDisplayItem()
runCallback("OnFrame")
assert.are.equals(22, build.calcsTab.mainEnv.player.mainSkill.skillModList:Sum("INC", { flags = ModFlag.Attack }, "Damage"))
end)
local integratedEfficiencyLoadout = function(modLine)
-- Activate via custom mod text to simplify testing
build.configTab.input.customMods = modLine
build.configTab:BuildModList()
runCallback("OnFrame")
build.itemsTab:CreateDisplayItemFromRaw([[
New Item
Razor Quarterstaff
Quality: 0
]])
build.itemsTab:AddDisplayItem()
runCallback("OnFrame")
-- Add 2 skills with 1 red, 1 blue, 1 green support each
-- Test against Quarterstaff Strike (skill slot 1)
build.skillsTab:PasteSocketGroup("Quarterstaff Strike 1/0 1\nArmour Break I 1/0 1\nShock 1/0 1\nBiting Frost I 1/0 1")
runCallback("OnFrame")
build.skillsTab:PasteSocketGroup("Falling Thunder 1/0 1\nIgnite I 1/0 1\nDaze 1/0 1\nShock Conduction 1/0 1")
runCallback("OnFrame")
build.configTab:BuildModList()
runCallback("OnFrame")
build.calcsTab:BuildOutput()
runCallback("OnFrame")
end
it("correctly calculates increased damage with gemling integrated efficiency", function()
integratedEfficiencyLoadout("skills deal 99% increased damage per connected red support gem")
local incDmg = build.calcsTab.mainEnv.player.activeSkillList[1].skillModList:Sum("INC", nil, "Damage")
assert.are.equals(incDmg, 99)
end)
it("correctly calculates crit chance with gemling integrated efficiency", function()
integratedEfficiencyLoadout("skills have 99% increased critical hit chance per connected blue support gem")
local incCritChance = build.calcsTab.mainEnv.player.activeSkillList[1].skillModList:Sum("INC", nil, "CritChance")
assert.are.equals(incCritChance, 99)
end)
it("correctly calculates increased skill speed with gemling integrated efficiency", function()
integratedEfficiencyLoadout("skills have 99% increased skill speed per connected green support gem")
local incSpeed = build.calcsTab.mainEnv.player.activeSkillList[1].skillModList:Sum("INC", nil, "Speed")
assert.are.equals(incSpeed, 99)
end)
it("correctly calculates critical hit damage", function()
-- Setup: Add weapon with no crit chance, and strip enemy defenses
-- changing enemy mods seems to get overwritten when mods are calculated, so it's easiest to just strip their defenses here
build.itemsTab:CreateDisplayItemFromRaw([[
New Item
Heavy Bow
-100% increased critical hit chance
nearby enemies have 100% less armour
nearby enemies have 100% less evasion
]])
build.itemsTab:AddDisplayItem()
runCallback("OnFrame")
build.calcsTab:BuildOutput()
runCallback("OnFrame")
-- 1: Get base damage with no crits
local critChance = 0
local critMult = 2
assert.are.equals(critChance, build.calcsTab.mainOutput.CritChance)
assert.are.equals(critMult, build.calcsTab.mainOutput.CritMultiplier)
local averageHit = build.calcsTab.mainOutput.MainHand.AverageHit
-- 2: Add crits and validate crit damage
build.configTab.input.customMods = "+10% to critical hit chance"
build.configTab:BuildModList()
runCallback("OnFrame")
build.calcsTab:BuildOutput()
runCallback("OnFrame")
local critChance = build.calcsTab.mainOutput.CritChance / 100
local newAvgHit = (1 - critChance) * averageHit + critChance * averageHit * critMult
assert.are.equals(newAvgHit, build.calcsTab.mainOutput.MainHand.AverageHit)
end)
it("correctly calculates critical hit damage with static values", function()
-- Setup: Create a 1 damage weapon with no crit chance, and strip enemy defenses
build.itemsTab:CreateDisplayItemFromRaw([[
New Item
Heavy Bow
Quality: 0
-100% increased critical hit chance
-100% increased physical damage
adds 1 to 1 physical damage to attacks
nearby enemies have 100% less armour
nearby enemies have 100% less evasion
]])
build.itemsTab:AddDisplayItem()
runCallback("OnFrame")
build.calcsTab:BuildOutput()
runCallback("OnFrame")
-- 1: Validate base damage = 1
assert.are.equals(0, build.calcsTab.mainOutput.MainHand.CritChance)
assert.are.equals(2, build.calcsTab.mainOutput.CritMultiplier)
assert.are.equals(1, build.calcsTab.mainOutput.MainHand.AverageHit)
-- 2: Add crits and validate new damage = 1.1 (for a 10% crit chance)
build.configTab.input.customMods = "+10% to critical hit chance"
build.configTab:BuildModList()
runCallback("OnFrame")
build.calcsTab:BuildOutput()
runCallback("OnFrame")
assert.are.equals(1.1, build.calcsTab.mainOutput.MainHand.AverageHit)
end)
it("matches in-game tooltip DPS for low-level spear skills", function()
build.spec:SelectClass(build.spec.tree.classNameMap.Huntress)
build.characterLevel = 11
build.characterLevelAutoMode = false
build.controls.characterLevel:SetText(11)
build.configTab.input.customMods = [[
10% increased Attack Damage
+10000 to Accuracy Rating
nearby enemies have 100% less armour
nearby enemies have 100% less evasion
]]
build.configTab:BuildModList()
build.itemsTab:CreateDisplayItemFromRaw([[
Apocalypse Edge
Ironhead Spear
Item Level: 7
Quality: 0
LevelReq: 5
Implicits: 1
Grants Skill: Spear Throw
Adds 2 to 4 Physical Damage
]])
build.itemsTab:AddDisplayItem()
local skills = {
{ gemId = "Metadata/Items/Gems/SkillGemPlayerDefaultSpear", level = 4, dps = 32.8 },
{ gemId = "Metadata/Items/Gems/SkillGemWhirlingSlash", level = 1, dps = 11.8 },
{ gemId = "Metadata/Items/Gems/SkillGemPlayerDefaultSpearThrow", level = 4, dps = 28.8 },
{ gemId = "Metadata/Items/Gems/SkillGemTwister", level = 2, dps = 17.5 },
}
for _, skill in ipairs(skills) do
local group = {
enabled = true,
gemList = { {
gemId = skill.gemId,
level = skill.level,
quality = 0,
enabled = true,
count = 1,
enableGlobal1 = true,
enableGlobal2 = true,
} },
}
table.insert(build.skillsTab.socketGroupList, group)
build.skillsTab:ProcessSocketGroup(group)
skill.groupIndex = #build.skillsTab.socketGroupList
end
for _, skill in ipairs(skills) do
local group = build.skillsTab.socketGroupList[skill.groupIndex]
build.mainSocketGroup = skill.groupIndex
build.calcsTab.input.skill_number = skill.groupIndex
group.mainActiveSkill = 1
group.mainActiveSkillCalcs = 1
build.buildFlag = true
build.modFlag = true
runCallback("OnFrame")
build.calcsTab:BuildOutput()
runCallback("OnFrame")
assert.are.equals(skill.dps, round(build.calcsTab.mainOutput.TotalDPS, 1))
end
end)
it("correctly adds damage with oracle forced outcome", function()
-- Setup: Add weapon with no crit chance, and strip enemy defenses
build.itemsTab:CreateDisplayItemFromRaw([[
New Item
Heavy Bow
-100% increased Critical Hit Chance
nearby enemies have 100% less armour
nearby enemies have 100% less evasion
]])
build.itemsTab:AddDisplayItem()
runCallback("OnFrame")
build.calcsTab:BuildOutput()
runCallback("OnFrame")
-- 1: Get base damage with no crits
local critChance = 0.0
local critMult = 2
assert.are.equals(critChance, build.calcsTab.mainOutput.CritChance)
assert.are.equals(critMult, build.calcsTab.mainOutput.CritMultiplier)
local averageHit = build.calcsTab.mainOutput.MainHand.AverageHit
-- 2: Add crits and forced outcome, and validate damage
build.configTab.input.customMods = [[
+10% to critical hit chance
inevitable critical hits
]]
build.configTab:BuildModList()
runCallback("OnFrame")
build.calcsTab:BuildOutput()
runCallback("OnFrame")
critChance = 0.1
local nonCritChance = 1 - critChance
local critBonusMultiplier =
1 * critChance
+ .7 * nonCritChance * critChance
+ .4 * nonCritChance * nonCritChance * critChance
+ .1 * nonCritChance * nonCritChance * nonCritChance * critChance
-- When adding them as MORE mods, they get auto rounded after *100, so we need to do the same
critBonusMultiplier = math.floor(critBonusMultiplier * 100 + 0.5)/100
local critBonus = critMult - 1
critBonus = critBonus * critBonusMultiplier
critMult = 1 + critBonus
local forcedExpectedAvgHit = averageHit * critMult
assert.are.equals(forcedExpectedAvgHit, build.calcsTab.mainOutput.MainHand.AverageHit)
end)
it("does not force critical hits when critical hit chance is zero", function()
build.itemsTab:CreateDisplayItemFromRaw([[
New Item
Heavy Bow
Quality: 0
-100% increased critical hit chance
-100% increased physical damage
adds 1 to 1 physical damage to attacks
nearby enemies have 100% less armour
nearby enemies have 100% less evasion
]])
build.itemsTab:AddDisplayItem()
runCallback("OnFrame")
build.configTab.input.customMods = "inevitable critical hits"
build.configTab:BuildModList()
runCallback("OnFrame")
build.calcsTab:BuildOutput()
runCallback("OnFrame")
assert.are.equals(0, build.calcsTab.mainOutput.MainHand.CritChance)
assert.are.equals(1, build.calcsTab.mainOutput.MainHand.AverageHit)
end)
it("correctly calculates forced outcome with bifurcated critical hits", function()
build.itemsTab:CreateDisplayItemFromRaw([[
New Item
Heavy Bow
Quality: 0
-100% increased critical hit chance
-100% increased physical damage
adds 1 to 1 physical damage to attacks
nearby enemies have 100% less armour
nearby enemies have 100% less evasion
]])
build.itemsTab:AddDisplayItem()
runCallback("OnFrame")
build.configTab.input.customMods = [[
+10% to critical hit chance
inevitable critical hits
bifurcates critical hits
]]
build.configTab:BuildModList()
runCallback("OnFrame")
build.calcsTab:BuildOutput()
runCallback("OnFrame")
local critChance = 0.1
local failedStageChance = (1 - critChance) ^ 2
local critBonusMultiplier = 2 * critChance * (
1
+ .7 * failedStageChance
+ .4 * failedStageChance ^ 2
+ .1 * failedStageChance ^ 3
)
critBonusMultiplier = math.floor(critBonusMultiplier * 100 + 0.5) / 100
assert.are.equals(100, build.calcsTab.mainOutput.MainHand.CritChance)
local bifurcateChance = (critChance * 100) ^ 2 / ((1 - failedStageChance) * 100)
assert.is_true(math.abs(bifurcateChance - build.calcsTab.mainOutput.MainHand.CritBifurcates) < 0.000001)
assert.are.equals(1 + critBonusMultiplier, build.calcsTab.mainOutput.MainHand.AverageHit)
end)
end)