diff --git a/src/Classes/ModDB.lua b/src/Classes/ModDB.lua index 3ff5c16a3..ca864aaa9 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 @@ -64,6 +65,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 00ae37856..7bbadba7d 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 6470f1b21..7e665e923 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.rej b/src/Data/ModCache.lua.rej new file mode 100644 index 000000000..150c3ec1c --- /dev/null +++ b/src/Data/ModCache.lua.rej @@ -0,0 +1,24 @@ +diff a/src/Data/ModCache.lua b/src/Data/ModCache.lua (rejected hunks) +@@ -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.rej b/src/Modules/CalcOffence.lua.rej new file mode 100644 index 000000000..f6cb055fa --- /dev/null +++ b/src/Modules/CalcOffence.lua.rej @@ -0,0 +1,30 @@ +diff a/src/Modules/CalcOffence.lua b/src/Modules/CalcOffence.lua (rejected hunks) +@@ -3059,17 +3059,18 @@ function calcs.offence(env, actor, activeSkill) + addedDamageRedirectType = "Cold" + end + if addedDamageRedirectType then +- local skipRedirect = activeSkill.activeEffect.grantedEffect.name == "Elemental Hit" +- if not skipRedirect 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 +- skillModList:ConvertMod(damageType.."Min", addedDamageRedirectType.."Min", "BASE", mod.value, mod.source, mod.flags, mod.keywordFlags, unpack(mod)) ++ 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 +- for _, value in ipairs(skillModList:Tabulate("BASE", cfg, damageType.."Max")) do +- local mod = value.mod +- skillModList:ConvertMod(damageType.."Max", addedDamageRedirectType.."Max", "BASE", mod.value, mod.source, mod.flags, mod.keywordFlags, unpack(mod)) ++ 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 diff --git a/src/Modules/CalcPerform.lua b/src/Modules/CalcPerform.lua index cd5a2bc12..ba8b6a094 100644 --- a/src/Modules/CalcPerform.lua +++ b/src/Modules/CalcPerform.lua @@ -376,6 +376,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/CalcPerform.lua.rej b/src/Modules/CalcPerform.lua.rej new file mode 100644 index 000000000..0a310bcc7 --- /dev/null +++ b/src/Modules/CalcPerform.lua.rej @@ -0,0 +1,10 @@ +diff a/src/Modules/CalcPerform.lua b/src/Modules/CalcPerform.lua (rejected hunks) +@@ -457,6 +459,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 3feb3402a..aa7b4de8d 100644 --- a/src/Modules/ModParser.lua +++ b/src/Modules/ModParser.lua @@ -5711,6 +5711,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" }), },