diff --git a/spec/System/TestMinionRage_spec.lua b/spec/System/TestMinionRage_spec.lua new file mode 100644 index 0000000000..d7b5951fdc --- /dev/null +++ b/spec/System/TestMinionRage_spec.lua @@ -0,0 +1,217 @@ +describe("Minion Rage", function() + before_each(function() + newBuild() + end) + + local function setupMinionSkill(gemId, supportGemId, quality) + local gemList = { + { + gemId = gemId, + level = 20, + quality = quality or 0, + enabled = true, + count = 1, + enableGlobal1 = true, + enableGlobal2 = true, + }, + } + + if supportGemId then + table.insert(gemList, { + gemId = supportGemId, + level = 1, + quality = 0, + enabled = true, + count = 1, + enableGlobal1 = true, + enableGlobal2 = true, + }) + end + + local socketGroup = { + enabled = true, + gemList = gemList, + } + + table.insert(build.skillsTab.socketGroupList, socketGroup) + build.skillsTab:ProcessSocketGroup(socketGroup) + + local groupIndex = #build.skillsTab.socketGroupList + build.mainSocketGroup = groupIndex + build.calcsTab.input.skill_number = groupIndex + socketGroup.mainActiveSkill = 1 + socketGroup.mainActiveSkillCalcs = 1 + + build.buildFlag = true + build.modFlag = true + runCallback("OnFrame") + build.calcsTab:BuildOutput() + runCallback("OnFrame") + end + + local function setMinionRage(rage) + build.configTab.input.multiplierMinionRage = rage + build.configTab:BuildModList() + + build.modFlag = true + build.buildFlag = true + runCallback("OnFrame") + build.calcsTab:BuildOutput() + runCallback("OnFrame") + + return build.calcsTab.mainOutput.Minion, build.calcsTab.mainEnv.minion + end + + it("hides Minion Rage for an unsupported non-Reaver minion", function() + setupMinionSkill("Metadata/Items/Gems/SkillGemSkeletalBrute") + + local control = build.configTab.varControls.multiplierMinionRage + assert.is_not_nil(control) + assert.is_false(control.shown()) + end) + + it("shows Minion Rage for a minion supported by Rage III", function() + setupMinionSkill( + "Metadata/Items/Gems/SkillGemSkeletalBrute", + "Metadata/Items/Gems/SkillGemRageSupportThree" + ) + + local control = build.configTab.varControls.multiplierMinionRage + assert.is_true(control.shown()) + end) + + it("shows Minion Rage for Skeletal Reavers without Rage support", function() + setupMinionSkill("Metadata/Items/Gems/SkillGemSkeletalReaver") + + local control = build.configTab.varControls.multiplierMinionRage + assert.is_true(control.shown()) + end) + + it("clamps configured Minion Rage to Maximum Rage", function() + setupMinionSkill("Metadata/Items/Gems/SkillGemSkeletalReaver") + + local output = setMinionRage(40) + + assert.are.equals(30, output.Rage) + assert.are.equals(30, output.MaximumRage) + end) + + it("grants Skeletal Reavers 3% increased Attack Speed per Rage", function() + setupMinionSkill("Metadata/Items/Gems/SkillGemSkeletalReaver") + + local output, minion = setMinionRage(20) + local speedIncrease = minion.mainSkill.skillModList:Sum( + "INC", + minion.mainSkill.skillCfg, + "Speed" + ) + + assert.are.equals(20, output.RageEffect) + assert.are.equals(60, speedIncrease) + end) + + it("applies Skeletal Reaver quality to Rage effect", function() + setupMinionSkill( + "Metadata/Items/Gems/SkillGemSkeletalReaver", + nil, + 20 + ) + + local output, minion = setMinionRage(20) + local speedIncrease = minion.mainSkill.skillModList:Sum( + "INC", + minion.mainSkill.skillCfg, + "Speed" + ) + + assert.are.equals(24, output.RageEffect) + assert.are.equals(72, speedIncrease) + end) + + it("applies Rage III Attack Speed only below Maximum Rage", function() + setupMinionSkill( + "Metadata/Items/Gems/SkillGemSkeletalBrute", + "Metadata/Items/Gems/SkillGemRageSupportThree" + ) + + local outputAt29, minionAt29 = setMinionRage(29) + local speedAt29 = minionAt29.mainSkill.skillModList:Sum( + "INC", + minionAt29.mainSkill.skillCfg, + "Speed" + ) + + local outputAt30, minionAt30 = setMinionRage(30) + local speedAt30 = minionAt30.mainSkill.skillModList:Sum( + "INC", + minionAt30.mainSkill.skillCfg, + "Speed" + ) + + assert.are.equals(29, outputAt29.Rage) + assert.are.equals(15, speedAt29) + assert.are.equals(30, outputAt30.Rage) + assert.are.equals(0, speedAt30) + end) + + it("applies Rage as more Attack Damage to minions", function() + setupMinionSkill( + "Metadata/Items/Gems/SkillGemSkeletalBrute", + "Metadata/Items/Gems/SkillGemRageSupportThree" + ) + + local _, minionAtZero = setMinionRage(0) + local damageAtZero = minionAtZero.mainSkill.skillModList:Sum( + "MORE", + minionAtZero.mainSkill.skillCfg, + "Damage" + ) + + local outputAt20, minionAt20 = setMinionRage(20) + local damageAt20 = minionAt20.mainSkill.skillModList:Sum( + "MORE", + minionAt20.mainSkill.skillCfg, + "Damage" + ) + + assert.are.equals(20, outputAt20.RageEffect) + assert.are.equals(damageAtZero + 20, damageAt20) + end) + + it("uses Maximum Rage from a weapon copied by Manifest Weapon", function() + build.itemsTab:CreateDisplayItemFromRaw([[ + New Item + Rabid Talisman + Implicits: 1 + +10 to Maximum Rage + ]]) + build.itemsTab:AddDisplayItem() + runCallback("OnFrame") + + setupMinionSkill( + "Metadata/Items/Gems/SkillGemManifestWeapon", + "Metadata/Items/Gems/SkillGemRageSupportThree" + ) + + local outputAt30, minionAt30 = setMinionRage(30) + local speedAt30 = minionAt30.mainSkill.skillModList:Sum( + "INC", + minionAt30.mainSkill.skillCfg, + "Speed" + ) + + assert.are.equals(40, outputAt30.MaximumRage) + assert.are.equals(30, outputAt30.Rage) + + local outputAt40, minionAt40 = setMinionRage(40) + local speedAt40 = minionAt40.mainSkill.skillModList:Sum( + "INC", + minionAt40.mainSkill.skillCfg, + "Speed" + ) + + assert.are.equals(40, outputAt40.MaximumRage) + assert.are.equals(40, outputAt40.Rage) + assert.are.equals(15, speedAt30 - speedAt40) + end) +end) \ No newline at end of file diff --git a/src/Classes/ConfigTab.lua b/src/Classes/ConfigTab.lua index 83c649b476..728c8352e9 100644 --- a/src/Classes/ConfigTab.lua +++ b/src/Classes/ConfigTab.lua @@ -458,6 +458,12 @@ local ConfigTabClass = newClass("ConfigTab", "UndoHandler", "ControlHost", "Cont end end)) end + if varData.ifMinionFlag then + t_insert(shownFuncs, listOrSingleIfOption(varData.ifMinionFlag, function(ifOption) + local minion = self.build.calcsTab.mainEnv.minion + return minion and minion.modDB and minion.modDB:Flag(nil, ifOption) + end)) + end if varData.ifMod then t_insert(shownFuncs, listOrSingleIfOption(varData.ifMod, function(ifOption) if implyCond(varData) then diff --git a/src/Data/Minions.lua b/src/Data/Minions.lua index d5dfec4ed5..783c80acd5 100644 --- a/src/Data/Minions.lua +++ b/src/Data/Minions.lua @@ -361,6 +361,7 @@ minions["RaisedSkeletonReaver"] = { -- set_item_drop_slots [set_item_drop_slots = 0] -- set_action_attack_or_cast_time_uses_animation_length [set_action_attack_or_cast_time_uses_animation_length = 0] -- is_skeleton_minion [is_skeleton_minion = 1] + mod("Condition:CanGainRage", "FLAG", true), }, } diff --git a/src/Data/SkillStatMap.lua b/src/Data/SkillStatMap.lua index 6e5decaed8..85cad54a8b 100644 --- a/src/Data/SkillStatMap.lua +++ b/src/Data/SkillStatMap.lua @@ -2941,6 +2941,7 @@ return { }, ["gain_x_rage_on_melee_hit"] = { flag("Condition:CanGainRage", { type = "GlobalEffect", effectType = "Buff", effectName = "Rage" } ), + mod("MinionModifier", "LIST", { mod = flag("Condition:CanGainRage") }), }, ["gain_x%_of_maximum_rage_on_melee_hit"] = { flag("Condition:CanGainRage", { type = "GlobalEffect", effectType = "Buff", effectName = "Rage" } ), diff --git a/src/Data/Skills/act_int.lua b/src/Data/Skills/act_int.lua index c4db5175b9..2653c56234 100644 --- a/src/Data/Skills/act_int.lua +++ b/src/Data/Skills/act_int.lua @@ -18606,6 +18606,14 @@ skills["SummonSkeletalReaversPlayer"] = { baseEffectiveness = 0, incrementalEffectiveness = 0.092720001935959, statDescriptionScope = "skill_stat_descriptions", + statMap = { + ["attack_speed_+%_per_rage"] = { + mod("MinionModifier", "LIST", { mod = mod("Speed", "INC", nil, ModFlag.Attack, 0, { type = "Multiplier", var = "RageEffect" }) }), + }, + ["minion_rage_effect_+%"] = { + mod("MinionModifier", "LIST", { mod = mod("RageEffect", "INC", nil) }), + }, + }, baseFlags = { spell = true, minion = true, diff --git a/src/Export/Minions/Minions.txt b/src/Export/Minions/Minions.txt index 415612a709..ca67de1887 100644 --- a/src/Export/Minions/Minions.txt +++ b/src/Export/Minions/Minions.txt @@ -39,6 +39,7 @@ local minions, mod = ... #monster Metadata/Monsters/Skeletons/PlayerSummoned/SkeletonReaverPlayerSummoned RaisedSkeletonReaver #limit ActiveSkeletonLimit +#mod mod("Condition:CanGainRage", "FLAG", true) #emit #monster Metadata/Monsters/Skeletons/PlayerSummoned/SkeletonWarriorPlayerSummoned RaisedSkeletonWarriors diff --git a/src/Export/Skills/act_int.txt b/src/Export/Skills/act_int.txt index 85e8cdc010..bd468daec5 100644 --- a/src/Export/Skills/act_int.txt +++ b/src/Export/Skills/act_int.txt @@ -1285,6 +1285,14 @@ statMap = { #skill SummonSkeletalReaversPlayer #set SummonSkeletalReaversPlayer #flags spell minion duration +statMap = { + ["attack_speed_+%_per_rage"] = { + mod("MinionModifier", "LIST", { mod = mod("Speed", "INC", nil, ModFlag.Attack, 0, { type = "Multiplier", var = "RageEffect" }) }), + }, + ["minion_rage_effect_+%"] = { + mod("MinionModifier", "LIST", { mod = mod("RageEffect", "INC", nil) }), + }, +}, #mods #skillEnd diff --git a/src/Modules/CalcSetup.lua b/src/Modules/CalcSetup.lua index 5ba727778c..0266dbde62 100644 --- a/src/Modules/CalcSetup.lua +++ b/src/Modules/CalcSetup.lua @@ -38,6 +38,7 @@ function calcs.initModDB(env, modDB) modDB:NewMod("PowerChargesMax", "BASE", data.characterConstants["max_power_charges"], "Base") modDB:NewMod("FrenzyChargesMax", "BASE", data.characterConstants["max_frenzy_charges"], "Base") modDB:NewMod("EnduranceChargesMax", "BASE", data.characterConstants["max_endurance_charges"], "Base") + modDB:NewMod("MaximumRage", "BASE", data.gameConstants["BaseMaximumRage"], "Base") modDB:NewMod("SiphoningChargesMax", "BASE", 0, "Base") modDB:NewMod("ChallengerChargesMax", "BASE", 0, "Base") modDB:NewMod("BlitzChargesMax", "BASE", 0, "Base") @@ -686,7 +687,6 @@ function calcs.initEnv(build, mode, override, specEnv) modDB:NewMod("TotemColdResist", "BASE", 40, "Base") modDB:NewMod("TotemLightningResist", "BASE", 40, "Base") modDB:NewMod("TotemChaosResist", "BASE", 20, "Base") - modDB:NewMod("MaximumRage", "BASE", data.gameConstants["BaseMaximumRage"], "Base") modDB:NewMod("MaximumFortification", "BASE", data.characterConstants["base_max_fortification"], "Base") modDB:NewMod("MaximumValour", "BASE", 50, "Base") modDB:NewMod("SoulEaterMax", "BASE", 45, "Base") diff --git a/src/Modules/ConfigOptions.lua b/src/Modules/ConfigOptions.lua index 0437732526..c896992b32 100644 --- a/src/Modules/ConfigOptions.lua +++ b/src/Modules/ConfigOptions.lua @@ -964,6 +964,8 @@ Huge sets the radius to 11. { var = "multiplierRage", type = "count", label = "^xFF9922Rage:", ifFlag = "Condition:CanGainRage", tooltip = "Base Maximum ^xFF9922Rage ^7is 30, and inherently grants 1% More Attack Damage per 1 ^xFF9922Rage^7\nYou lose 5 ^xFF9922Rage ^7every second if you have not been Hit or gained ^xFF9922Rage ^7in the last 4 seconds.", apply = function(val, modList, enemyModList) modList:NewMod("Multiplier:RageStack", "BASE", val, "Config", { type = "IgnoreCond" }, { type = "Condition", var = "Combat" }, { type = "Condition", var = "CanGainRage" }) end }, + { var = "multiplierMinionRage", type = "count", label = "Minion ^xFF9922Rage:", ifMinionFlag = "Condition:CanGainRage", tooltip = "Sets the Minion's current Rage.\nRage is capped at Minion's Maximum Rage.", apply = function(val, modList, enemyModList) modList:NewMod("MinionModifier", "LIST", { mod = modLib.createMod("Multiplier:RageStack", "BASE", val, "Config", { type = "IgnoreCond" }, { type = "Condition", var = "Combat" }, { type = "Condition", var = "CanGainRage" }) }, "Config") + end }, { var = "multiplierCombo", type = "count", label = "Combo:", ifMult = "ComboStacks", tooltip = "Some skills and effects require a certain Combo count to use.\nCombo is built by successfully Striking Enemies.", apply = function(val, modList, enemyModList) modList:NewMod("Multiplier:ComboStacks", "BASE", val, "Config", { type = "IgnoreCond" }, { type = "Condition", var = "Combat" }) end },