Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions spec/System/TestCommon_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ describe("Common", function()
function ParentClass:ConstructorTestParentClass()
return self
end
local ChildClass = newClass("ConstructorTestProblemChildClass", "ConstructorTestParentClass")
local ChildClass = newClass("ConstructorTestProblemChild", "ConstructorTestParentClass")
function ChildClass:ConstructorTestProblemChild()
-- Intentionally does not call self:ConstructorTestParentClass()
return self
Expand Down Expand Up @@ -40,7 +40,7 @@ describe("Common", function()
return self
end

local ChildClass = newClass("ConstructorTestProblemChildClass", "ConstructorTestParentClass")
local ChildClass = newClass("ConstructorTestProblemChild", "ConstructorTestParentClass")
function ChildClass:ConstructorTestProblemChild()
self.ConstructorTestParentClass()
return self
Expand Down
15 changes: 12 additions & 3 deletions src/Classes/ModDB.lua
Original file line number Diff line number Diff line change
Expand Up @@ -135,14 +135,17 @@ end

function ModDBClass:SumInternal(context, modType, cfg, flags, keywordFlags, source, ...)
local result = 0
local globalLimits = { }
local globalLimits
for i = 1, select('#', ...) do
local modList = self.mods[select(i, ...)]
if modList then
for i = 1, #modList do
local mod = modList[i]
if mod.type == modType and band(flags, mod.flags) == mod.flags and MatchKeywordFlags(keywordFlags, mod.keywordFlags) and (not source or ( mod.source and mod.source:match("[^:]+") == source )) then
if mod[1] then
if not globalLimits then
globalLimits = {}
end
local value = context:EvalMod(mod, cfg, globalLimits) or 0
result = result + value
else
Expand All @@ -161,7 +164,7 @@ end
function ModDBClass:MoreInternal(context, cfg, flags, keywordFlags, source, ...)
local result = 1
local modPrecision = nil
local globalLimits = { }
local globalLimits
for i = 1, select('#', ...) do
local modList = self.mods[select(i, ...)]
local modResult = 1 --The more multipliers for each mod are computed to the nearest percent then applied.
Expand All @@ -171,6 +174,9 @@ function ModDBClass:MoreInternal(context, cfg, flags, keywordFlags, source, ...)
if mod.type == "MORE" and band(flags, mod.flags) == mod.flags and MatchKeywordFlags(keywordFlags, mod.keywordFlags) and (not source or mod.source:match("[^:]+") == source) then
local value
if mod[1] then
if not globalLimits then
globalLimits = {}
end
value = context:EvalMod(mod, cfg, globalLimits) or 0
else
value = mod.value or 0
Expand Down Expand Up @@ -270,7 +276,7 @@ function ModDBClass:ListInternal(context, result, cfg, flags, keywordFlags, sour
end

function ModDBClass:TabulateInternal(context, result, modType, cfg, flags, keywordFlags, source, ...)
local globalLimits = { }
local globalLimits
for i = 1, select('#', ...) do
local modName = select(i, ...)
local modList = self.mods[modName]
Expand All @@ -280,6 +286,9 @@ function ModDBClass:TabulateInternal(context, result, modType, cfg, flags, keywo
if (mod.type == modType or not modType) and band(flags, mod.flags) == mod.flags and MatchKeywordFlags(keywordFlags, mod.keywordFlags) and (not source or mod.source:match("[^:]+") == source) then
local value
if mod[1] then
if not globalLimits then
globalLimits = {}
end
value = context:EvalMod(mod, cfg, globalLimits)
else
value = mod.value
Expand Down
140 changes: 112 additions & 28 deletions src/Modules/CalcSetup.lua
Original file line number Diff line number Diff line change
Expand Up @@ -114,60 +114,140 @@ function calcs.initModDB(env, modDB)
modDB.conditions["Effective"] = env.mode_effective
end

function calcs.buildModListForNode(env, node)
local modList = new("ModList"):ModList()
-- Recycle a modlist so that we do not allocate many tables for each node.
local function resetModList(list)
for i = #list, 1, -1 do
list[i] = nil
end
list.multipliers = wipeTable(list.multipliers)
list.conditions = wipeTable(list.conditions)
list.actor = wipeTable(list.actor)
list.parent = false
return list
end

---@param reuse table|nil A ModList to recycle instead of allocating. Only safe when the caller discards the result.
function calcs.buildModListForNode(env, node, reuse)
local modList = reuse and resetModList(reuse) or new("ModList"):ModList()
if node.type == "Keystone" then
modList:AddMod(node.keystoneMod)
else
modList:AddList(node.modList)
end

-- Run first pass radius jewels
for _, rad in pairs(env.radiusJewelList) do
if rad.type == "Other" and rad.nodes[node.id] and rad.nodes[node.id].type ~= "Mastery" then
rad.func(node, modList, rad.data)
for i = 1, #env.radiusJewelList do
local rad = env.radiusJewelList[i]
if rad.type == "Other" then
local radNode = rad.nodes[node.id]
if radNode and radNode.type ~= "Mastery" then
rad.func(node, modList, rad.data)
end
end
end

if modList:Flag(nil, "PassiveSkillHasNoEffect") or (env.allocNodes[node.id] and modList:Flag(nil, "AllocatedPassiveSkillHasNoEffect")) then
-- prefilter the modlist so that every :Flag() call does not have to go through the entire mod list
local hasNoEffect, hasAllocNoEffect, hasScale, hasOtherEffect, hasExtraSkill, hasExplode
for i = 1, #modList do
local name = modList[i].name
if name == "PassiveSkillHasNoEffect" then
hasNoEffect = true
elseif name == "AllocatedPassiveSkillHasNoEffect" then
hasAllocNoEffect = true
elseif name == "PassiveSkillEffect" then
hasScale = true
elseif name == "PassiveSkillHasOtherEffect" then
hasOtherEffect = true
elseif name == "ExtraSkill" then
hasExtraSkill = true
elseif name == "CanExplode" then
hasExplode = true
end
end

if (hasNoEffect and modList:Flag(nil, "PassiveSkillHasNoEffect")) or (env.allocNodes[node.id] and (hasAllocNoEffect and modList:Flag(nil, "AllocatedPassiveSkillHasNoEffect"))) then
wipeTable(modList)
hasScale = false
hasOtherEffect = nil
hasExtraSkill = nil
hasExplode = nil
end

-- Apply effect scaling
local scale = calcLib.mod(modList, nil, "PassiveSkillEffect")
if scale ~= 1 then
local scaledList = new("ModList"):ModList()
scaledList:ScaleAddList(modList, scale)
modList = scaledList
if hasScale then
local scale = calcLib.mod(modList, nil, "PassiveSkillEffect")
if scale ~= 1 then
local scaledList = new("ModList"):ModList()
scaledList:ScaleAddList(modList, scale)
modList = scaledList
end
end

-- Run second pass radius jewels
for _, rad in pairs(env.radiusJewelList) do
local rescan = false
for i = 1, #env.radiusJewelList do
local rad = env.radiusJewelList[i]
if rad.nodes[node.id] and rad.nodes[node.id].type ~= "Mastery" and (rad.type == "Threshold" or (rad.type == "Self" and env.allocNodes[node.id]) or (rad.type == "SelfUnalloc" and not env.allocNodes[node.id])) then
rad.func(node, modList, rad.data)
rescan = true
hasOtherEffect = nil
hasExtraSkill = nil
hasExplode = nil
end
end

if rescan then
for i = 1, #modList do
local name = modList[i].name
if name == "PassiveSkillHasOtherEffect" then
hasOtherEffect = true
elseif name == "ExtraSkill" then
hasExtraSkill = true
elseif name == "CanExplode" then
hasExplode = true
end
end
end

if modList:Flag(nil, "PassiveSkillHasOtherEffect") then
for i, mod in ipairs(modList:List(skillCfg, "NodeModifier")) do
if i == 1 then wipeTable(modList) end
modList:AddMod(mod.mod)
if hasOtherEffect and modList:Flag(nil, "PassiveSkillHasOtherEffect") then
local newMods = modList:List(nil, "NodeModifier")
for i = 1, #newMods do
local mod = newMods[i].mod
if i == 1 then
wipeTable(modList)
hasExtraSkill = nil
hasExplode = nil
end
if mod.name == "ExtraSkill" then
hasExtraSkill = true
elseif mod.name == "CanExplode" then
hasExplode = true
end
modList:AddMod(mod)
end
end

node.grantedSkills = { }
for _, skill in ipairs(modList:List(nil, "ExtraSkill")) do
if skill.name ~= "Unknown" then
t_insert(node.grantedSkills, {
skillId = skill.skillId,
level = skill.level,
noSupports = true,
source = "Tree:"..node.id
})
node.grantedSkills = wipeTable(node.grantedSkills)
if hasExtraSkill then
local list = modList:List(nil, "ExtraSkill")
for i = 1, #list do
local skill = list[i]
if skill.name ~= "Unknown" then
t_insert(node.grantedSkills, {
skillId = skill.skillId,
level = skill.level,
noSupports = true,
source = "Tree:" .. node.id
})
end
end
end

return modList, modList:Flag(nil, "CanExplode") and node
if hasExplode then
return modList, modList:Flag(nil, "CanExplode") and node
else
return modList
end
end

-- Build list of modifiers from the listed tree nodes
Expand All @@ -181,8 +261,12 @@ function calcs.buildModListForNodeList(env, nodeList, finishJewels)
-- Add node modifiers
local modList = new("ModList"):ModList()
local explodeSources = {}
-- Outside MAIN mode the per-node list is merged into modList and then
-- dropped, so a single list can be recycled for every node instead of
-- allocating one each time.
local scratch = env.mode ~= "MAIN" and new("ModList"):ModList() or nil
for _, node in pairs(nodeList) do
local nodeModList, explode = calcs.buildModListForNode(env, node)
local nodeModList, explode = calcs.buildModListForNode(env, node, scratch)
t_insert(explodeSources, explode)
modList:AddList(nodeModList)
if env.mode == "MAIN" then
Expand All @@ -193,7 +277,7 @@ function calcs.buildModListForNodeList(env, nodeList, finishJewels)
if finishJewels then
-- Process extra radius nodes; these are unallocated nodes near conversion or threshold jewels that need to be processed
for _, node in pairs(env.extraRadiusNodeList) do
local nodeModList = calcs.buildModListForNode(env, node)
local nodeModList = calcs.buildModListForNode(env, node, scratch)
if env.mode == "MAIN" then
node.finalModList = nodeModList
end
Expand Down
Loading
Loading