diff --git a/spec/System/TestSkills_spec.lua b/spec/System/TestSkills_spec.lua index 6b832ed95f..ed51fb7f3f 100644 --- a/spec/System/TestSkills_spec.lua +++ b/spec/System/TestSkills_spec.lua @@ -228,4 +228,25 @@ describe("TestAttacks", function() local finalCost = build.calcsTab.mainOutput.ManaCost assert.are.equals(7, round(finalCost)) end) + + it("evaluates BaseFlag tags using PoB 1 skill data", function() + build.skillsTab:PasteSocketGroup("Absolution 20/0 1\n") + runCallback("OnFrame") + + local durationSkill = build.calcsTab.mainEnv.player.mainSkill + durationSkill.skillModList:NewMod("BaseFlagTest", "BASE", 1, "Test", { type = "BaseFlag", baseFlag = "duration" }) + durationSkill.skillModList:NewMod("NegatedBaseFlagTest", "BASE", 1, "Test", { type = "BaseFlag", baseFlag = "duration", neg = true }) + assert.are.equals(1, durationSkill.skillModList:Sum("BASE", durationSkill.skillCfg, "BaseFlagTest")) + assert.are.equals(0, durationSkill.skillModList:Sum("BASE", durationSkill.skillCfg, "NegatedBaseFlagTest")) + + newBuild() + build.skillsTab:PasteSocketGroup("Fireball 20/0 1\n") + runCallback("OnFrame") + + local nonDurationSkill = build.calcsTab.mainEnv.player.mainSkill + nonDurationSkill.skillModList:NewMod("BaseFlagTest", "BASE", 1, "Test", { type = "BaseFlag", baseFlag = "duration" }) + nonDurationSkill.skillModList:NewMod("NegatedBaseFlagTest", "BASE", 1, "Test", { type = "BaseFlag", baseFlag = "duration", neg = true }) + assert.are.equals(0, nonDurationSkill.skillModList:Sum("BASE", nonDurationSkill.skillCfg, "BaseFlagTest")) + assert.are.equals(1, nonDurationSkill.skillModList:Sum("BASE", nonDurationSkill.skillCfg, "NegatedBaseFlagTest")) + end) end) diff --git a/src/Classes/CalcBreakdownControl.lua b/src/Classes/CalcBreakdownControl.lua index 9b11440c4f..1255ae9856 100644 --- a/src/Classes/CalcBreakdownControl.lua +++ b/src/Classes/CalcBreakdownControl.lua @@ -474,6 +474,8 @@ function CalcBreakdownClass:AddModSection(sectionData, modList) if not desc then desc = "Skill type: "..(tag.neg and "Not " or "").."?" end + elseif tag.type == "BaseFlag" then + desc = "Base flag: "..(tag.neg and "Not " or "")..self:FormatModName(tostring(tag.baseFlag)) elseif tag.type == "SlotNumber" then desc = "When in slot #"..tag.num elseif tag.type == "GlobalEffect" then diff --git a/src/Classes/ModStore.lua b/src/Classes/ModStore.lua index e7fa805a61..fdf09770bc 100644 --- a/src/Classes/ModStore.lua +++ b/src/Classes/ModStore.lua @@ -34,6 +34,14 @@ local ModStoreClass = newClass("ModStore", function(self, parent) self.conditions = { } end) +local function getActor(self, actorType) + if actorType == "player" then + return self.actor.player or (self.actor.parent and self.actor.parent.player) or (self.actor.enemy and self.actor.enemy.player) + else + return self.actor[actorType] + end +end + function ModStoreClass:ScaleAddMod(mod, scale, replace) local unscalable = false for _, effects in ipairs(mod) do @@ -323,15 +331,17 @@ function ModStoreClass:EvalMod(mod, cfg, globalLimits) -- This explicit target is necessary because even though the GetMultiplier method does call self.parent.GetMultiplier, it does so with noMod = true, -- disabling the summation (3rd part): (not noMod and self:Sum("BASE", cfg, multiplierName[var]) or 0) if tag.limitActor then - if self.actor[tag.limitActor] then - limitTarget = self.actor[tag.limitActor].modDB + local limitActor = getActor(self, tag.limitActor) + if limitActor then + limitTarget = limitActor.modDB else return end end if tag.actor then - if self.actor[tag.actor] then - target = self.actor[tag.actor].modDB + local actor = getActor(self, tag.actor) + if actor then + target = actor.modDB else return end @@ -398,15 +408,17 @@ function ModStoreClass:EvalMod(mod, cfg, globalLimits) local target = self local thresholdTarget = self if tag.thresholdActor then - if self.actor[tag.thresholdActor] then - thresholdTarget = self.actor[tag.thresholdActor].modDB + local thresholdActor = getActor(self, tag.thresholdActor) + if thresholdActor then + thresholdTarget = thresholdActor.modDB else return end end if tag.actor then - if self.actor[tag.actor] then - target = self.actor[tag.actor].modDB + local actor = getActor(self, tag.actor) + if actor then + target = actor.modDB else return end @@ -428,8 +440,9 @@ function ModStoreClass:EvalMod(mod, cfg, globalLimits) local target = self -- This functions similar to the above tagTypes in regard to which actor to use, but for PerStat -- if the actor is 'parent', we don't want to return if we're already using 'parent', just keep using 'self' - if tag.actor and self.actor[tag.actor] then - target = self.actor[tag.actor].modDB + local actor = getActor(self, tag.actor) + if actor then + target = actor.modDB end if tag.statList then base = 0 @@ -450,7 +463,7 @@ function ModStoreClass:EvalMod(mod, cfg, globalLimits) limitTotal = limit else mult = m_min(mult, limit) - end + end end if type(value) == "table" then value = copyTable(value) @@ -476,8 +489,9 @@ function ModStoreClass:EvalMod(mod, cfg, globalLimits) local target = self -- This functions similar to the above tagTypes in regard to which actor to use, but for PercentStat -- if the actor is 'parent', we don't want to return if we're already using 'parent', just keep using 'self' - if tag.actor and self.actor[tag.actor] then - target = self.actor[tag.actor].modDB + local actor = getActor(self, tag.actor) + if actor then + target = actor.modDB end if tag.statList then base = 0 @@ -499,7 +513,7 @@ function ModStoreClass:EvalMod(mod, cfg, globalLimits) limitTotal = limit else mult = m_min(mult, limit) - end + end end if type(value) == "table" then value = copyTable(value) @@ -555,9 +569,9 @@ function ModStoreClass:EvalMod(mod, cfg, globalLimits) end end end - -- Syntax: { type = "MeleeProximity", ramp = {MaxBonusPct,MinBonusPct} } - -- Both MaxBonusPct and MinBonusPct are percent in decimal form (1.0 = 100%) - -- Example: { type = "MeleeProximity", ramp = {1,0} } ## Duelist-Slayer: Impact + -- Syntax: { type = "MeleeProximity", ramp = {MaxBonusPct,MinBonusPct} } + -- Both MaxBonusPct and MinBonusPct are percent in decimal form (1.0 = 100%) + -- Example: { type = "MeleeProximity", ramp = {1,0} } ## Duelist-Slayer: Impact elseif tag.type == "MeleeProximity" then if not cfg or not cfg.skillDist then return @@ -565,7 +579,7 @@ function ModStoreClass:EvalMod(mod, cfg, globalLimits) -- Max potency is 0-15 units of distance if cfg.skillDist <= 15 then value = value * tag.ramp[1] - -- Reduced potency (linear) until 40 units + -- Reduced potency (linear) until 40 units elseif cfg.skillDist >= 16 and cfg.skillDist <= 39 then value = value * (tag.ramp[1] - ((tag.ramp[1] / 25) * (cfg.skillDist - 15))) elseif cfg.skillDist >= 40 then @@ -608,7 +622,8 @@ function ModStoreClass:EvalMod(mod, cfg, globalLimits) local match = false local target = self if tag.actor then - target = self.actor[tag.actor] and self.actor[tag.actor].modDB + local actor = getActor(self, tag.actor) + target = actor and actor.modDB end if target and (tag.var or tag.varList) then if tag.varList then @@ -713,7 +728,7 @@ function ModStoreClass:EvalMod(mod, cfg, globalLimits) end return false end - + local match = {} if tag.slotName then match["slotName"] = (tag.slotName == cfg.slotName) or false @@ -820,6 +835,15 @@ function ModStoreClass:EvalMod(mod, cfg, globalLimits) if not match then return end + elseif tag.type == "BaseFlag" then + local baseFlags = cfg and ((cfg.skillGrantedEffect and cfg.skillGrantedEffect.baseFlags) or cfg.baseFlags) + local match = baseFlags and baseFlags[tag.baseFlag] or false + if tag.neg then + match = not match + end + if not match then + return + end elseif tag.type == "SlotName" then if not cfg then return @@ -901,4 +925,4 @@ function ModStoreClass:EvalMod(mod, cfg, globalLimits) end end return value -end \ No newline at end of file +end