Skip to content

Commit 9855e1e

Browse files
EtherealCarnivoreLocalIdentity
andauthored
Rename Otherworldly Appendages to Cryogenesis, add new stats (#9517)
* Rename Otherworldly Appendages to Cryogenesis, add new stats 3.28 replaces the old graft damage reduction stats with: - Int single highest attribute: all added damage treated as Lightning - Dex single highest attribute: all added damage treated as Cold Updated tree data (all 4 variants), added ModParser entries, single-highest-attribute conditions in CalcPerform, and flat added damage redirection in CalcOffence. * Revert tree changes * Add ConvertMod to ModStore/ModList/ModDB Like ReplaceMod but matches by oldName instead of the new mod's name, so it can change a mod's identity (e.g. FireMin -> ColdMin) rather than just updating its value. ModDB moves the mod between name buckets. * Rework Cryogenesis to use ConvertMod, match 3.28 tree wording ModParser entries now match the actual 3.28 tree text instead of guessed wording. Handles the "Lighting" typo with a pattern. CalcOffence uses ConvertMod to redirect added damage mods before the damage loop so breakdowns show the source properly. Base Elemental Hit is excluded per the node text. Added ConvertMod to ModStore/ModList/ModDB - like ReplaceMod but matches by oldName so it can change a mod's identity. * Update ModCache entries for Cryogenesis ascendancy mods The old cache had these 3.28 tree lines stored as parse failures from before the ModParser entries existed. Since the cache uses exact-case text as the key, the tree's mixed-case text always hit the stale entry and returned "unsupported" without reaching the new specialModList patterns. Replaced 3 individual entries with correct parsed results and removed 3 concatenated multi-line entries that were never going to match anyway. * Use offset mods for Cryogenesis redirect, label source Replaced ConvertMod approach with sum-and-offset: for each non-target damage type, add a negative mod to cancel the original and a positive mod on the target type. Both labeled "Cryogenesis Conversion" so the breakdown popup shows where the redirected damage comes from. Removed ConvertMod/ConvertModInternal from ModStore/ModList/ModDB since nothing uses them anymore. * Revert "Use offset mods for Cryogenesis redirect, label source" This reverts commit baee98d. * Fix Ele Hit handling and breakdown Only the base damage granted via ele hit is not meant to be converted. Mods from gear are meant to work still The breakdown now shows a note on the mods that are converted that the cryogenesis node is what is changing them --------- Co-authored-by: LocalIdentity <localidentity2@gmail.com>
1 parent d31d44d commit 9855e1e

7 files changed

Lines changed: 117 additions & 6 deletions

File tree

src/Classes/ModDB.lua

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ local ipairs = ipairs
77
local pairs = pairs
88
local select = select
99
local t_insert = table.insert
10+
local t_remove = table.remove
1011
local m_floor = math.floor
1112
local m_min = math.min
1213
local m_max = math.max
@@ -65,6 +66,45 @@ function ModDBClass:ReplaceModInternal(mod)
6566
return false
6667
end
6768

69+
---ConvertModInternal
70+
--- Converts an existing mod with oldName to a new mod with a different name.
71+
--- Moves the mod from the old name's bucket to the new name's bucket.
72+
--- If no matching mod exists, then the function returns false
73+
---@param oldName string @The name of the existing mod to find
74+
---@param mod table @The new mod to replace it with
75+
---@return boolean @Whether any mod was converted
76+
function ModDBClass:ConvertModInternal(oldName, mod)
77+
if not self.mods[oldName] then
78+
if self.parent then
79+
return self.parent:ConvertModInternal(oldName, mod)
80+
end
81+
return false
82+
end
83+
84+
local oldList = self.mods[oldName]
85+
for i = 1, #oldList do
86+
local curMod = oldList[i]
87+
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
88+
-- Remove from old name's bucket
89+
t_remove(oldList, i)
90+
-- Add to new name's bucket
91+
local newName = mod.name
92+
if not self.mods[newName] then
93+
self.mods[newName] = { }
94+
end
95+
mod.converted = true
96+
t_insert(self.mods[newName], mod)
97+
return true
98+
end
99+
end
100+
101+
if self.parent then
102+
return self.parent:ConvertModInternal(oldName, mod)
103+
end
104+
105+
return false
106+
end
107+
68108
function ModDBClass:AddList(modList)
69109
local mods = self.mods
70110
for i, mod in ipairs(modList) do

src/Classes/ModList.lua

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,27 @@ function ModListClass:ReplaceModInternal(mod)
4545
return false
4646
end
4747

48+
---ConvertModInternal
49+
--- Converts an existing mod with oldName to a new mod with a different name.
50+
--- If no matching mod exists, then the function returns false
51+
---@param oldName string @The name of the existing mod to find
52+
---@param mod table @The new mod to replace it with
53+
---@return boolean @Whether any mod was converted
54+
function ModListClass:ConvertModInternal(oldName, mod)
55+
for i, curMod in ipairs(self) do
56+
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
57+
self[i] = mod
58+
return true
59+
end
60+
end
61+
62+
if self.parent then
63+
return self.parent:ConvertModInternal(oldName, mod)
64+
end
65+
66+
return false
67+
end
68+
4869
function ModListClass:MergeMod(mod, skipNonAdditive)
4970
if mod.type == "BASE" or mod.type == "INC" or mod.type == "MORE" then
5071
for i = 1, #self do

src/Classes/ModStore.lua

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,19 @@ function ModStoreClass:ReplaceMod(...)
110110
end
111111
end
112112

113+
---ConvertMod
114+
--- Converts an existing mod to a new name, replacing it in the store.
115+
--- Finds a mod matching oldName with the same type, flags, keywordFlags, and source as the new mod.
116+
--- If no matching mod exists, the new mod is added instead.
117+
---@param oldName string @The name of the existing mod to convert
118+
---@param ... any @Parameters to be passed along to the modLib.createMod function (new name, type, value, source, ...)
119+
function ModStoreClass:ConvertMod(oldName, ...)
120+
local mod = mod_createMod(...)
121+
if not self:ConvertModInternal(oldName, mod) then
122+
self:AddMod(mod)
123+
end
124+
end
125+
113126
function ModStoreClass:Combine(modType, cfg, ...)
114127
if modType == "MORE" then
115128
return self:More(cfg, ...)

src/Data/ModCache.lua

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8413,7 +8413,7 @@ c["Elemental Ailments you inflict are Reflected to you Elemental Damage with Hit
84138413
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}
84148414
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}
84158415
c["Elemental Equilibrium"]={{[1]={flags=0,keywordFlags=0,name="Keystone",type="LIST",value="Elemental Equilibrium"}},nil}
8416-
c["Elemental Hit's Added Damage cannot be replaced this way"]={nil,"Elemental Hit's Added Damage cannot be replaced this way "}
8416+
c["Elemental Hit's Added Damage cannot be replaced this way"]={{},nil}
84178417
c["Elemental Overload"]={{[1]={flags=0,keywordFlags=0,name="Keystone",type="LIST",value="Elemental Overload"}},nil}
84188418
c["Elemental Resistance values as inverted"]={nil,"Elemental Resistance values as inverted "}
84198419
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 "}
@@ -12583,11 +12583,8 @@ c["You count as on Low Life while you are Cursed with Vulnerability"]={{[1]={[1]
1258312583
c["You do not inherently take less Damage for having Fortification"]={{[1]={flags=0,keywordFlags=0,name="Condition:NoFortificationMitigation",type="FLAG",value=true}},nil}
1258412584
c["You gain 3 Grasping Vines when you take a Critical Strike"]={{}," Grasping Vines when you take a Critical Strike "}
1258512585
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 "}
12586-
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 "}
12587-
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 "}
12588-
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 "}
12589-
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 "}
12590-
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 "}
12586+
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}
12587+
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}
1259112588
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}
1259212589
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}
1259312590
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 "}

src/Modules/CalcOffence.lua

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3048,6 +3048,35 @@ function calcs.offence(env, actor, activeSkill)
30483048

30493049
runSkillFunc("postCritFunc")
30503050

3051+
-- Added damage redirection (Cryogenesis)
3052+
-- Convert all added damage mods to the target type before the damage loop
3053+
-- so breakdowns show the redirected source correctly.
3054+
-- Base Elemental Hit is excluded per the node text.
3055+
local addedDamageRedirectType = nil
3056+
if skillModList:Flag(cfg, "AllAddedDamageAsLightning") then
3057+
addedDamageRedirectType = "Lightning"
3058+
elseif skillModList:Flag(cfg, "AllAddedDamageAsCold") then
3059+
addedDamageRedirectType = "Cold"
3060+
end
3061+
if addedDamageRedirectType then
3062+
for _, damageType in ipairs(dmgTypeList) do
3063+
if damageType ~= addedDamageRedirectType then
3064+
for _, value in ipairs(skillModList:Tabulate("BASE", cfg, damageType.."Min")) do
3065+
local mod = value.mod
3066+
if mod.source ~= "Skill:ElementalHit" then
3067+
skillModList:ConvertMod(damageType.."Min", addedDamageRedirectType.."Min", "BASE", mod.value, mod.source, mod.flags, mod.keywordFlags, { type = "Cryogenesis Added Damage" }, unpack(mod))
3068+
end
3069+
end
3070+
for _, value in ipairs(skillModList:Tabulate("BASE", cfg, damageType.."Max")) do
3071+
local mod = value.mod
3072+
if mod.source ~= "Skill:ElementalHit" then
3073+
skillModList:ConvertMod(damageType.."Max", addedDamageRedirectType.."Max", "BASE", mod.value, mod.source, mod.flags, mod.keywordFlags, { type = "Cryogenesis Added Damage" }, unpack(mod))
3074+
end
3075+
end
3076+
end
3077+
end
3078+
end
3079+
30513080
-- Calculate base hit damage
30523081
for _, damageType in ipairs(dmgTypeList) do
30533082
local damageTypeMin = damageType.."Min"

src/Modules/CalcPerform.lua

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,8 @@ local function doActorAttribsConditions(env, actor)
391391
condList["StrHighestAttribute"] = output.Str >= output.Dex and output.Str >= output.Int
392392
condList["IntHighestAttribute"] = output.Int >= output.Str and output.Int >= output.Dex
393393
condList["DexHighestAttribute"] = output.Dex >= output.Str and output.Dex >= output.Int
394+
condList["IntSingleHighestAttribute"] = output.Int > output.Str and output.Int > output.Dex
395+
condList["DexSingleHighestAttribute"] = output.Dex > output.Str and output.Dex > output.Int
394396
end
395397
end
396398

@@ -453,6 +455,8 @@ local function doActorAttribsConditions(env, actor)
453455
condList["StrHighestAttribute"] = output.Str >= output.Dex and output.Str >= output.Int
454456
condList["IntHighestAttribute"] = output.Int >= output.Str and output.Int >= output.Dex
455457
condList["DexHighestAttribute"] = output.Dex >= output.Str and output.Dex >= output.Int
458+
condList["IntSingleHighestAttribute"] = output.Int > output.Str and output.Int > output.Dex
459+
condList["DexSingleHighestAttribute"] = output.Dex > output.Str and output.Dex > output.Int
456460
end
457461
end
458462

src/Modules/ModParser.lua

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5290,6 +5290,13 @@ local specialModList = {
52905290
["consecrated path and purifying flame create profane ground instead of consecrated ground"] = {
52915291
flag("Condition:CreateProfaneGround"),
52925292
},
5293+
["you gain added cold damage instead of added damage of other types if dexterity exceeds both other attributes"] = {
5294+
flag("AllAddedDamageAsCold", { type = "Condition", var = "DexSingleHighestAttribute" }),
5295+
},
5296+
["you gain added lightn?ing damage instead of added damage of other types if intelligence exceeds both other attributes"] = {
5297+
flag("AllAddedDamageAsLightning", { type = "Condition", var = "IntSingleHighestAttribute" }),
5298+
},
5299+
["elemental hit's added damage cannot be replaced this way"] = { },
52935300
["you have consecrated ground around you while stationary if strength is your highest attribute"] = {
52945301
flag("Condition:OnConsecratedGround", { type = "Condition", var = "StrHighestAttribute" }, { type = "Condition", var = "Stationary" }),
52955302
},

0 commit comments

Comments
 (0)