forked from Openarl/PathOfBuilding
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathCalcTools.lua
183 lines (172 loc) · 5.91 KB
/
CalcTools.lua
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
-- Path of Building
--
-- Module: Calc Tools
-- Various functions used by the calculation modules
--
local pairs = pairs
local t_insert = table.insert
local t_remove = table.remove
local m_floor = math.floor
local m_min = math.min
local m_max = math.max
calcLib = { }
-- Calculate and combine INC/MORE modifiers for the given modifier names
function calcLib.mod(modStore, cfg, ...)
return (1 + (modStore:Sum("INC", cfg, ...)) / 100) * modStore:More(cfg, ...)
end
-- Calculate value
function calcLib.val(modStore, name, cfg)
local baseVal = modStore:Sum("BASE", cfg, name)
if baseVal ~= 0 then
return baseVal * calcLib.mod(modStore, cfg, name)
else
return 0
end
end
-- Calculate hit chance
function calcLib.hitChance(evasion, accuracy)
local rawChance = accuracy / (accuracy + (evasion / 4) ^ 0.8) * 100
return m_max(m_min(round(rawChance), 95), 5)
end
-- Calculate physical damage reduction from armour
function calcLib.armourReduction(armour, raw)
return round(armour / (armour + raw * 10) * 100)
end
-- Validate the level of the given gem
function calcLib.validateGemLevel(gemInstance)
local grantedEffect = gemInstance.grantedEffect or gemInstance.gemData.grantedEffect
if not grantedEffect.levels[gemInstance.level] then
if gemInstance.gemData and gemInstance.gemData.defaultLevel then
gemInstance.level = gemInstance.gemData.defaultLevel
else
-- Try limiting to the level range of the skill
gemInstance.level = m_max(1, gemInstance.level)
if #grantedEffect.levels > 0 then
gemInstance.level = m_min(#grantedEffect.levels, gemInstance.level)
end
if not grantedEffect.levels[gemInstance.level] then
-- That failed, so just grab any level
gemInstance.level = next(grantedEffect.levels)
end
end
end
end
-- Evaluate a skill type postfix expression
function calcLib.doesTypeExpressionMatch(checkTypes, skillTypes, minionTypes)
local stack = { }
for _, skillType in pairs(checkTypes) do
if skillType == SkillType.OR then
local other = t_remove(stack)
stack[#stack] = stack[#stack] or other
elseif skillType == SkillType.AND then
local other = t_remove(stack)
stack[#stack] = stack[#stack] and other
elseif skillType == SkillType.NOT then
stack[#stack] = not stack[#stack]
else
t_insert(stack, skillTypes[skillType] or (minionTypes and minionTypes[skillType]) or false)
end
end
for _, val in ipairs(stack) do
if val then
return true
end
end
return false
end
-- Check if given support skill can support the given active skill
function calcLib.canGrantedEffectSupportActiveSkill(grantedEffect, activeSkill)
if grantedEffect.unsupported or activeSkill.activeEffect.grantedEffect.cannotBeSupported then
return false
end
if grantedEffect.supportGemsOnly and not activeSkill.activeEffect.gemData then
return false
end
if activeSkill.summonSkill then
return calcLib.canGrantedEffectSupportActiveSkill(grantedEffect, activeSkill.summonSkill)
end
if grantedEffect.excludeSkillTypes[1] and calcLib.doesTypeExpressionMatch(grantedEffect.excludeSkillTypes, activeSkill.skillTypes) then
return false
end
return not grantedEffect.requireSkillTypes[1] or calcLib.doesTypeExpressionMatch(grantedEffect.requireSkillTypes, activeSkill.skillTypes, not grantedEffect.ignoreMinionTypes and activeSkill.minionSkillTypes)
end
-- Check if given gem is of the given type ("all", "strength", "melee", etc)
function calcLib.gemIsType(gem, type)
return (type == "all" or
(type == "elemental" and (gem.tags.fire or gem.tags.cold or gem.tags.lightning)) or
(type == "aoe" and gem.tags.area) or
(type == "trap or mine" and (gem.tags.trap or gem.tags.mine)) or
gem.tags[type])
end
-- From PyPoE's formula.py
function calcLib.getGemStatRequirement(level, isSupport, multi)
if multi == 0 then
return 0
end
local a, b
if isSupport then
b = 6 * multi / 100
if multi == 100 then
a = 1.495
elseif multi == 60 then
a = 0.945
elseif multi == 40 then
a = 0.6575
else
return 0
end
else
b = 8 * multi / 100
if multi == 100 then
a = 2.1
b = 7.75
elseif multi == 60 then
a = 1.325
elseif multi == 40 then
a = 0.924
else
return 0
end
end
local req = round(level * a + b)
return req < 14 and 0 or req
end
-- Build table of stats for the given skill instance
function calcLib.buildSkillInstanceStats(skillInstance, grantedEffect)
local stats = { }
if skillInstance.quality > 0 then
for _, stat in ipairs(grantedEffect.qualityStats) do
stats[stat[1]] = (stats[stat[1]] or 0) + m_floor(stat[2] * skillInstance.quality)
end
end
local level = grantedEffect.levels[skillInstance.level]
local availableEffectiveness
if not skillInstance.actorLevel then
skillInstance.actorLevel = level.levelRequirement
end
for index, stat in ipairs(grantedEffect.stats) do
local statValue
if grantedEffect.statInterpolation[index] == 3 then
-- Effectiveness interpolation
if not availableEffectiveness then
availableEffectiveness =
(3.885209 + 0.360246 * (skillInstance.actorLevel - 1)) * grantedEffect.baseEffectiveness
* (1 + grantedEffect.incrementalEffectiveness) ^ (skillInstance.actorLevel - 1)
end
statValue = round(availableEffectiveness * level[index])
elseif grantedEffect.statInterpolation[index] == 2 then
-- Linear interpolation; I'm actually just guessing how this works
local nextLevel = m_min(skillInstance.level + 1, #grantedEffect.levels)
local nextReq = grantedEffect.levels[nextLevel].levelRequirement
local prevReq = grantedEffect.levels[nextLevel - 1].levelRequirement
local nextStat = grantedEffect.levels[nextLevel][index]
local prevStat = grantedEffect.levels[nextLevel - 1][index]
statValue = round(prevStat + (nextStat - prevStat) * (skillInstance.actorLevel - prevReq) / (nextReq - prevReq))
else
-- Static value
statValue = level[index] or 1
end
stats[stat] = (stats[stat] or 0) + statValue
end
return stats
end