diff --git a/src/Classes/ModDB.lua b/src/Classes/ModDB.lua index f60ac59e49..3b7ae60922 100644 --- a/src/Classes/ModDB.lua +++ b/src/Classes/ModDB.lua @@ -7,6 +7,7 @@ local ipairs = ipairs local pairs = pairs local select = select 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 @@ -65,6 +66,45 @@ function ModDBClass:ReplaceModInternal(mod) return false end +---ConvertModInternal +--- Converts an existing mod with oldName to a new mod with a different name. +--- Moves the mod from the old name's bucket to the new name's bucket. +--- If no matching mod exists, then the function returns false +---@param oldName string @The name of the existing mod to find +---@param mod table @The new mod to replace it with +---@return boolean @Whether any mod was converted +function ModDBClass:ConvertModInternal(oldName, mod) + if not self.mods[oldName] then + if self.parent then + return self.parent:ConvertModInternal(oldName, mod) + end + return false + end + + local oldList = self.mods[oldName] + for i = 1, #oldList do + local curMod = oldList[i] + if oldName == curMod.name and mod.type == curMod.type and mod.flags == curMod.flags and mod.keywordFlags == curMod.keywordFlags and mod.source == curMod.source and not curMod.converted then + -- Remove from old name's bucket + t_remove(oldList, i) + -- Add to new name's bucket + local newName = mod.name + if not self.mods[newName] then + self.mods[newName] = { } + end + mod.converted = true + t_insert(self.mods[newName], mod) + return true + end + end + + if self.parent then + return self.parent:ConvertModInternal(oldName, mod) + end + + return false +end + function ModDBClass:AddList(modList) local mods = self.mods for i, mod in ipairs(modList) do diff --git a/src/Classes/ModList.lua b/src/Classes/ModList.lua index b27393279a..7bb6e2aba8 100644 --- a/src/Classes/ModList.lua +++ b/src/Classes/ModList.lua @@ -45,6 +45,27 @@ function ModListClass:ReplaceModInternal(mod) return false end +---ConvertModInternal +--- Converts an existing mod with oldName to a new mod with a different name. +--- If no matching mod exists, then the function returns false +---@param oldName string @The name of the existing mod to find +---@param mod table @The new mod to replace it with +---@return boolean @Whether any mod was converted +function ModListClass:ConvertModInternal(oldName, mod) + for i, curMod in ipairs(self) do + if oldName == curMod.name and mod.type == curMod.type and mod.flags == curMod.flags and mod.keywordFlags == curMod.keywordFlags and mod.source == curMod.source then + self[i] = mod + return true + end + end + + if self.parent then + return self.parent:ConvertModInternal(oldName, mod) + end + + return false +end + function ModListClass:MergeMod(mod, skipNonAdditive) if mod.type == "BASE" or mod.type == "INC" or mod.type == "MORE" then for i = 1, #self do diff --git a/src/Classes/ModStore.lua b/src/Classes/ModStore.lua index ff0130c481..ec61927e90 100644 --- a/src/Classes/ModStore.lua +++ b/src/Classes/ModStore.lua @@ -110,6 +110,19 @@ function ModStoreClass:ReplaceMod(...) end end +---ConvertMod +--- Converts an existing mod to a new name, replacing it in the store. +--- Finds a mod matching oldName with the same type, flags, keywordFlags, and source as the new mod. +--- If no matching mod exists, the new mod is added instead. +---@param oldName string @The name of the existing mod to convert +---@param ... any @Parameters to be passed along to the modLib.createMod function (new name, type, value, source, ...) +function ModStoreClass:ConvertMod(oldName, ...) + local mod = mod_createMod(...) + if not self:ConvertModInternal(oldName, mod) then + self:AddMod(mod) + end +end + function ModStoreClass:Combine(modType, cfg, ...) if modType == "MORE" then return self:More(cfg, ...) diff --git a/src/Data/ModCache.lua b/src/Data/ModCache.lua index b807a765e8..dbabd5d4cf 100755 --- a/src/Data/ModCache.lua +++ b/src/Data/ModCache.lua @@ -8413,7 +8413,7 @@ c["Elemental Ailments you inflict are Reflected to you Elemental Damage with Hit c["Elemental Damage with Hits is Lucky while you are Shocked"]={{[1]={[1]={type="Condition",var="Shocked"},flags=0,keywordFlags=0,name="ElementalLuckHits",type="FLAG",value=true}},nil} c["Elemental Damage you Deal with Hits is Resisted by lowest Elemental Resistance instead"]={{[1]={flags=0,keywordFlags=0,name="ElementalDamageUsesLowestResistance",type="FLAG",value=true}},nil} c["Elemental Equilibrium"]={{[1]={flags=0,keywordFlags=0,name="Keystone",type="LIST",value="Elemental Equilibrium"}},nil} -c["Elemental Hit's Added Damage cannot be replaced this way"]={nil,"Elemental Hit's Added Damage cannot be replaced this way "} +c["Elemental Hit's Added Damage cannot be replaced this way"]={{},nil} c["Elemental Overload"]={{[1]={flags=0,keywordFlags=0,name="Keystone",type="LIST",value="Elemental Overload"}},nil} c["Elemental Resistance values as inverted"]={nil,"Elemental Resistance values as inverted "} c["Elemental Resistance values as inverted Limited to 1 Runegraft of the Gauche"]={nil,"Elemental Resistance values as inverted Limited to 1 Runegraft of the Gauche "} @@ -12581,11 +12581,8 @@ c["You count as on Low Life while you are Cursed with Vulnerability"]={{[1]={[1] c["You do not inherently take less Damage for having Fortification"]={{[1]={flags=0,keywordFlags=0,name="Condition:NoFortificationMitigation",type="FLAG",value=true}},nil} c["You gain 3 Grasping Vines when you take a Critical Strike"]={{}," Grasping Vines when you take a Critical Strike "} c["You gain 3 Grasping Vines when you take a Critical Strike Nearby stationary Enemies gain a Grasping Vine every 0.5 seconds"]={{}," Grasping Vines when you take a Critical Strike Nearby stationary Enemies gain a Grasping Vine every 0.5 seconds "} -c["You gain Added Cold Damage instead of Added Damage of other types if Dexterity exceeds both other Attributes"]={nil,"Added Cold Damage instead of Added Damage of other types if Dexterity exceeds both other Attributes "} -c["You gain Added Cold Damage instead of Added Damage of other types if Dexterity exceeds both other Attributes You gain Added Lighting Damage instead of Added Damage of other types if Intelligence exceeds both other Attributes"]={nil,"Added Cold Damage instead of Added Damage of other types if Dexterity exceeds both other Attributes You gain Added Lighting Damage instead of Added Damage of other types if Intelligence exceeds both other Attributes "} -c["You gain Added Cold Damage instead of Added Damage of other types if Dexterity exceeds both other Attributes You gain Added Lighting Damage instead of Added Damage of other types if Intelligence exceeds both other Attributes Elemental Hit's Added Damage cannot be replaced this way"]={nil,"Added Cold Damage instead of Added Damage of other types if Dexterity exceeds both other Attributes You gain Added Lighting Damage instead of Added Damage of other types if Intelligence exceeds both other Attributes Elemental Hit's Added Damage cannot be replaced this way "} -c["You gain Added Lighting Damage instead of Added Damage of other types if Intelligence exceeds both other Attributes"]={nil,"Added Lighting Damage instead of Added Damage of other types if Intelligence exceeds both other Attributes "} -c["You gain Added Lighting Damage instead of Added Damage of other types if Intelligence exceeds both other Attributes Elemental Hit's Added Damage cannot be replaced this way"]={nil,"Added Lighting Damage instead of Added Damage of other types if Intelligence exceeds both other Attributes Elemental Hit's Added Damage cannot be replaced this way "} +c["You gain Added Cold Damage instead of Added Damage of other types if Dexterity exceeds both other Attributes"]={{[1]={[1]={type="Condition",var="DexSingleHighestAttribute"},flags=0,keywordFlags=0,name="AllAddedDamageAsCold",type="FLAG",value=true}},nil} +c["You gain Added Lighting Damage instead of Added Damage of other types if Intelligence exceeds both other Attributes"]={{[1]={[1]={type="Condition",var="IntSingleHighestAttribute"},flags=0,keywordFlags=0,name="AllAddedDamageAsLightning",type="FLAG",value=true}},nil} c["You gain Divinity for 10 seconds on reaching maximum Divine Charges"]={{[1]={[1]={type="Condition",var="Divinity"},flags=0,keywordFlags=0,name="ElementalDamage",type="MORE",value=75},[2]={[1]={type="Condition",var="Divinity"},flags=0,keywordFlags=0,name="ElementalDamageTaken",type="MORE",value=-25}},nil} c["You gain Onslaught for 1 seconds on Killing Taunted Enemies"]={{[1]={[1]={type="Condition",var="KilledTauntedEnemyRecently"},flags=0,keywordFlags=0,name="Condition:Onslaught",type="FLAG",value=true}},nil} c["You gain Onslaught for 1 seconds per Endurance Charge when Hit"]={{[1]={[1]={type="Multiplier",var="EnduranceCharge"},flags=0,keywordFlags=0,name="Condition:Onslaught",type="FLAG",value=true}}," when Hit "} diff --git a/src/Modules/CalcOffence.lua b/src/Modules/CalcOffence.lua index fefdfbbbad..65265f745c 100644 --- a/src/Modules/CalcOffence.lua +++ b/src/Modules/CalcOffence.lua @@ -3048,6 +3048,35 @@ function calcs.offence(env, actor, activeSkill) runSkillFunc("postCritFunc") + -- Added damage redirection (Cryogenesis) + -- Convert all added damage mods to the target type before the damage loop + -- so breakdowns show the redirected source correctly. + -- Base Elemental Hit is excluded per the node text. + local addedDamageRedirectType = nil + if skillModList:Flag(cfg, "AllAddedDamageAsLightning") then + addedDamageRedirectType = "Lightning" + elseif skillModList:Flag(cfg, "AllAddedDamageAsCold") then + addedDamageRedirectType = "Cold" + end + if addedDamageRedirectType then + for _, damageType in ipairs(dmgTypeList) do + if damageType ~= addedDamageRedirectType then + for _, value in ipairs(skillModList:Tabulate("BASE", cfg, damageType.."Min")) do + local mod = value.mod + if mod.source ~= "Skill:ElementalHit" then + skillModList:ConvertMod(damageType.."Min", addedDamageRedirectType.."Min", "BASE", mod.value, mod.source, mod.flags, mod.keywordFlags, { type = "Cryogenesis Added Damage" }, unpack(mod)) + end + end + for _, value in ipairs(skillModList:Tabulate("BASE", cfg, damageType.."Max")) do + local mod = value.mod + if mod.source ~= "Skill:ElementalHit" then + skillModList:ConvertMod(damageType.."Max", addedDamageRedirectType.."Max", "BASE", mod.value, mod.source, mod.flags, mod.keywordFlags, { type = "Cryogenesis Added Damage" }, unpack(mod)) + end + end + end + end + end + -- Calculate base hit damage for _, damageType in ipairs(dmgTypeList) do local damageTypeMin = damageType.."Min" diff --git a/src/Modules/CalcPerform.lua b/src/Modules/CalcPerform.lua index 5ce0829317..4568886b53 100644 --- a/src/Modules/CalcPerform.lua +++ b/src/Modules/CalcPerform.lua @@ -391,6 +391,8 @@ local function doActorAttribsConditions(env, actor) condList["StrHighestAttribute"] = output.Str >= output.Dex and output.Str >= output.Int condList["IntHighestAttribute"] = output.Int >= output.Str and output.Int >= output.Dex condList["DexHighestAttribute"] = output.Dex >= output.Str and output.Dex >= output.Int + condList["IntSingleHighestAttribute"] = output.Int > output.Str and output.Int > output.Dex + condList["DexSingleHighestAttribute"] = output.Dex > output.Str and output.Dex > output.Int end end @@ -453,6 +455,8 @@ local function doActorAttribsConditions(env, actor) condList["StrHighestAttribute"] = output.Str >= output.Dex and output.Str >= output.Int condList["IntHighestAttribute"] = output.Int >= output.Str and output.Int >= output.Dex condList["DexHighestAttribute"] = output.Dex >= output.Str and output.Dex >= output.Int + condList["IntSingleHighestAttribute"] = output.Int > output.Str and output.Int > output.Dex + condList["DexSingleHighestAttribute"] = output.Dex > output.Str and output.Dex > output.Int end end diff --git a/src/Modules/ModParser.lua b/src/Modules/ModParser.lua index 64980f42fd..44d8098e48 100644 --- a/src/Modules/ModParser.lua +++ b/src/Modules/ModParser.lua @@ -5286,6 +5286,13 @@ local specialModList = { ["consecrated path and purifying flame create profane ground instead of consecrated ground"] = { flag("Condition:CreateProfaneGround"), }, + ["you gain added cold damage instead of added damage of other types if dexterity exceeds both other attributes"] = { + flag("AllAddedDamageAsCold", { type = "Condition", var = "DexSingleHighestAttribute" }), + }, + ["you gain added lightn?ing damage instead of added damage of other types if intelligence exceeds both other attributes"] = { + flag("AllAddedDamageAsLightning", { type = "Condition", var = "IntSingleHighestAttribute" }), + }, + ["elemental hit's added damage cannot be replaced this way"] = { }, ["you have consecrated ground around you while stationary if strength is your highest attribute"] = { flag("Condition:OnConsecratedGround", { type = "Condition", var = "StrHighestAttribute" }, { type = "Condition", var = "Stationary" }), },