-- Path of Building
--
-- Active Intelligence skill gems
-- Skill data (c) Grinding Gear Games
--
local skills, mod, flag, skill = ...

#skill Arc
#flags spell chaining
	statMap = {
		["arc_damage_+%_final_for_each_remaining_chain"] = {
			mod("Damage", "MORE", nil, 0, bit.bor(KeywordFlag.Hit, KeywordFlag.Ailment), { type = "PerStat", stat = "ChainRemaining" }),
		},
	},
#mods

#skill ArcAltX
#flags spell chaining
#mods

#skill ArcAltY
#flags spell chaining
	statMap = {
		["arc_damage_+%_final_for_each_remaining_chain"] = {
			mod("Damage", "MORE", nil, 0, bit.bor(KeywordFlag.Hit, KeywordFlag.Ailment), { type = "PerStat", stat = "ChainRemaining" }),
		},
	},
#mods

#skill VaalArc
#flags spell chaining
	statMap = {
		["arc_damage_+%_final_for_each_remaining_chain"] = {
			mod("Damage", "MORE", nil, 0, bit.bor(KeywordFlag.Hit, KeywordFlag.Ailment), { type = "PerStat", stat = "ChainRemaining" }),
		},
	},
#baseMod flag("Condition:CanBeLucky", { type = "GlobalEffect", effectType = "Buff" })
#mods

#skill ArcaneCloak
#flags spell duration
	statMap = {
		["arcane_cloak_damage_absorbed_%"] = {
			mod("GuardAbsorbRate", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Guard", unscalable = true }),
		},
		["arcane_cloak_consume_%_of_mana"] = {
			mod("Multiplier:ArcaneCloakConsumedMana", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Guard", unscalable = true }),
			div = 100,
		},
		["arcane_cloak_gain_%_of_consumed_mana_as_lightning_damage"] = {
			mod("LightningMin", "BASE", nil, 0, 0, { type = "PercentStat", stat = "ManaUnreserved", percentVar = "ArcaneCloakConsumedMana" }, { type = "GlobalEffect", effectType = "Buff" }),
			mod("LightningMax", "BASE", nil, 0, 0, { type = "PercentStat", stat = "ManaUnreserved", percentVar = "ArcaneCloakConsumedMana" }, { type = "GlobalEffect", effectType = "Buff" }),
		},
	},
#baseMod mod("GuardAbsorbLimit", "BASE", 100, 0, 0, { type = "PercentStat", stat = "ManaUnreserved", percentVar = "ArcaneCloakConsumedMana" }, { type = "GlobalEffect", effectType = "Guard", unscalable = true })
#baseMod mod("Multiplier:ManaSpentRecently", "BASE", 100, 0, 0, { type = "PercentStat", stat = "ManaUnreserved", percentVar = "ArcaneCloakConsumedMana" }, { type = "Condition", var = "ArcaneCloakUsedRecently"}, { type = "GlobalEffect", effectType = "Buff", unscalable = true})
#mods

#skill Automation
#flags spell
	statMap = {
		["automation_behaviour"] = {
			-- Display only
		},
	},
#mods

#skill SupportAutomation
#mods

#skill BrandSupport
#flags spell duration brand
	preDamageFunc = function(activeSkill, output)
		activeSkill.skillData.hitTimeOverride = activeSkill.skillData.repeatFrequency / (1 + activeSkill.skillModList:Sum("INC", activeSkill.skillCfg, "Speed", "BrandActivationFrequency") / 100) / activeSkill.skillModList:More(activeSkill.skillCfg, "BrandActivationFrequency")
	end,
#baseMod skill("debuff", true)
#mods

#skill SupportBrandSupport
	addFlags = {
		brand = true,
	},
	statMap = {
		["support_brand_damage_+%_final"] = {
			mod("TriggeredDamage", "MORE", nil),
		},
		["support_brand_area_of_effect_+%_final"] = {
			mod("AreaOfEffect", "MORE", nil),
		},
		["trigger_brand_support_hit_damage_+%_final_vs_branded_enemy"] = {
			mod("TriggeredDamage", "MORE", nil, 0, 0, { type = "Condition", var = "TargetingBrandedEnemy"}),
		},
	},
#baseMod skill("triggeredByBrand", true)
#mods

#skill CreepingFrost
#flags spell area projectile duration
#baseMod skill("dotIsArea", true)
#baseMod skill("radiusLabel", "Projectile Impact:")
#baseMod skill("radiusSecondaryLabel", "DoT Area:")
#mods

#skill ArmageddonBrand
#flags spell area duration brand
	preDamageFunc = function(activeSkill, output)
		activeSkill.skillData.hitTimeOverride = activeSkill.skillData.repeatFrequency / (1 + activeSkill.skillModList:Sum("INC", activeSkill.skillCfg, "Speed", "BrandActivationFrequency") / 100) / activeSkill.skillModList:More(activeSkill.skillCfg, "BrandActivationFrequency")
	end,
	statMap = {
		["base_skill_show_average_damage_instead_of_dps"] = {
		},
	},
#baseMod skill("radiusSecondary", 8)
#baseMod skill("debuff", true)
#mods

#skill ArmageddonBrandAltX
#flags spell area duration brand
	statMap = {
		["base_skill_show_average_damage_instead_of_dps"] = {
		},
	},
#mods

#skill ArmageddonBrandAltY
#flags spell area duration brand
	preDamageFunc = function(activeSkill, output)
		activeSkill.skillData.hitTimeOverride = activeSkill.skillData.repeatFrequency / (1 + activeSkill.skillModList:Sum("INC", activeSkill.skillCfg, "Speed", "BrandActivationFrequency") / 100) / activeSkill.skillModList:More(activeSkill.skillCfg, "BrandActivationFrequency")
	end,
	statMap = {
		["base_skill_show_average_damage_instead_of_dps"] = {
		},
	},
#baseMod skill("debuff", true)
#mods

#skill AssassinsMark
#flags spell curse duration mark
	statMap = {
		["enemy_additional_critical_strike_multiplier_against_self"] = {
			mod("SelfCritMultiplier", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Curse" }),
		},
		["enemy_additional_critical_strike_chance_against_self"] = {
			mod("SelfCritChance", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Curse" }),
			div = 100,
		},
		["life_granted_when_killed"] = {
			mod("SelfLifeOnKill", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Curse" }),
		},
		["mana_granted_when_killed"] = {
			mod("SelfManaOnKill", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Curse" }),
		},
	},
#baseMod skill("debuff", true)
#mods

#skill BallLightning
#flags spell projectile
	parts = {
		{
			name = "One Bolt",
		},
		{
			name = "All Bolts in Range",
		}
	},
	preDamageFunc = function(activeSkill, output, breakdown)
		local skillCfg = activeSkill.skillCfg
		local skillData = activeSkill.skillData
		local skillFlags = activeSkill.skillFlags
		local skillModList = activeSkill.skillModList
		local t_insert = table.insert
		local s_format = string.format

		local dpsMultiplier = 0
		if activeSkill.skillPart == 1 then
			-- Compute DPS changes as if we get exactly 1 strike per ball.
			dpsMultiplier = 1
		elseif activeSkill.skillPart == 2 then
			-- Compute DPS changes accounting for all strikes in range.

			-- What's the bolt strike proc rate? Note that the interval is not
			-- considered to be a cooldown, so it is unaffected by CDR mods.
			local secsPerStrike = skillData.strikeInterval
			-- How many total bolt strikes proc per ball, ignoring whether the
			-- enemy is in range? We assume that the first strike is at the end
			-- of the first interval, based on Kitava self-poison testing (no
			-- recorded examples of getting 14 self poison stacks with multiple
			-- people testing).
			local durationSecs = skillData.duration
			local maxStrikes = math.floor(durationSecs / secsPerStrike)
			-- How fast does the ball travel?
			local baseBallDistPerSec = skillData.projectileSpeed
			local incSpeedMult, moreSpeedMult = calcLib.mods(skillModList, skillCfg, "ProjectileSpeed")
			local netSpeedMult = incSpeedMult * moreSpeedMult
			local ballDistPerSec = baseBallDistPerSec * netSpeedMult
			local ballDistPerStrike = ballDistPerSec * secsPerStrike
			-- How many times does the ball proc a bolt strike while it is in
			-- range of the enemy?
			local enemyRadius = 0 -- for now, we will be conservative and assume no enemy radius
			local baseStrikeRadius = output.AreaOfEffectRadius
			local strikeRadius = baseStrikeRadius
			local castDist = 0
			if skillCfg.skillDist then
				-- Advanced users can specify exactly the standoff distance
				-- they'll use against single-target bosses.
				castDist = skillCfg.skillDist
			elseif skillFlags.triggered then
				-- Cyclone is the most common trigger skill, and players who
				-- aren't min-maxing their playstyle will tend to just
				-- cyclone back and forth across the boss instead of
				-- hovering at the optimal range. For simplicity, let's
				-- assume they tend to be an average of 1 normal bolt strike
				-- radius away as they do this.
				castDist = math.floor(baseStrikeRadius / 2)
			else
				-- Be nice and assume hand-casters are at the optimal
				-- distance for normal bolt strikes.
				castDist = baseStrikeRadius
			end
			local firstStrikeIdxThatHits =
				math.max(1,  -- 1 not 0 here: strike seems to happen at the end of the interval, not start
						 math.ceil((castDist - strikeRadius) / ballDistPerStrike))
			local lastStrikeIdxThatHits = math.floor(math.min(data.misc.ProjectileDistanceCap, castDist + strikeRadius) / ballDistPerStrike)
			local numStrikes = math.max(0, math.min(maxStrikes, lastStrikeIdxThatHits + 1 - firstStrikeIdxThatHits))
			lastStrikeIdxThatHits = firstStrikeIdxThatHits + numStrikes - 1

			dpsMultiplier = numStrikes

			if breakdown then
				local breakdownHits = {}
				t_insert(breakdownHits, s_format("^8Balls travel at^7 %.2f^8 units/sec.", ballDistPerSec))
				t_insert(breakdownHits, s_format("^8Lightning bolts strike all nearby enemies every^7 %.2f^8 seconds (^7%.2f^8 strikes/sec).", secsPerStrike, 1 / secsPerStrike))
				t_insert(breakdownHits, s_format("^8Balls travel^7 %.2f^8 units between each bolt strike.", ballDistPerStrike))
				t_insert(breakdownHits, s_format("^8Assumes balls are cast^7 %d^8 units from the enemy.", castDist))
				t_insert(breakdownHits, s_format("^8Balls can strike enemies up to^7 %d^8 units away from themselves.", strikeRadius))
				t_insert(breakdownHits, s_format("^8The first strike is at^7 %.2f^8 seconds after it is cast, when the ball is^7 %d^8 units from the cast point.", firstStrikeIdxThatHits * secsPerStrike, firstStrikeIdxThatHits * ballDistPerStrike))
				t_insert(breakdownHits, s_format("^8The last strike is at^7 %.2f^8 seconds after it is cast, when the ball is^7 %d^8 units from the cast point.", lastStrikeIdxThatHits * secsPerStrike, lastStrikeIdxThatHits * ballDistPerStrike))
				output.NormalHitsPerCast = numStrikes
				breakdown.NormalHitsPerCast = breakdownHits
			end
		end
		if dpsMultiplier ~= 1 then
			skillData.dpsMultiplier = (skillData.dpsMultiplier or 1) * dpsMultiplier
			output.SkillDPSMultiplier = (output.SkillDPSMultiplier or 1) * dpsMultiplier
		end
	end,
	statMap = {
		["ball_lightning_projectile_speed_and_hit_frequency_+%_final"] = {
			mod("ProjectileSpeed", "MORE", nil)
		},
	},
#baseMod skill("strikeInterval", 0.15)
#baseMod skill("projectileSpeed", 40)
#baseMod skill("duration", 2)
#baseMod flag("CannotSplit")
#mods

#skill BallLightningAltX
#flags spell projectile
	preDamageFunc = function(activeSkill, output, breakdown)
		local s_format = string.format
		local dpsMultiplier = 1
		if activeSkill.skillPart == 2 or activeSkill.skillPart == 3 then

			local skillData = activeSkill.skillData
			local secsPerStrike = skillData.strikeInterval
			local durationSecs = skillData.duration
			local numStrikes = math.floor(durationSecs / secsPerStrike)

			dpsMultiplier = numStrikes
			if dpsMultiplier ~= 1 then
				skillData.dpsMultiplier = (skillData.dpsMultiplier or 1) * dpsMultiplier
				output.SkillDPSMultiplier = (output.SkillDPSMultiplier or 1) * dpsMultiplier
			end
			output.NormalHitsPerCast = numStrikes
			if breakdown then
				breakdown.NormalHitsPerCast = {
					s_format("^8Lightning bolts strike all nearby enemies every^7 %.2f^8 seconds (^7%.2f^8 strikes/sec).", secsPerStrike, 1 / secsPerStrike),
					s_format("^8Balls lasts for ^7%d^8 seconds for a total of ^7%d^8 strikes.", durationSecs, numStrikes),
				}
			end
		end
	end,
	parts = {
		{
			name = "One Bolt",
		},
		{
			name = "Half Bolts Hitting",
		},
		{
			name = "All Bolts Hitting",
		},
	},
	statMap = {
		["ball_lightning_projectile_speed_and_hit_frequency_+%_final"] = {
			mod("ProjectileSpeed", "MORE", nil)
		},
	},
#baseMod skill("strikeInterval", 0.3, { type = "SkillPart", skillPart = 2 })
#baseMod skill("strikeInterval", 0.15, { type = "SkillPart", skillPart = 3 })
#baseMod skill("projectileSpeed", 40)
#baseMod skill("duration", 2)
#baseMod flag("CannotSplit")
#mods

#skill BallLightningAltY
#flags spell
	preDamageFunc = function(activeSkill, output, breakdown)
		local s_format = string.format

		local skillData = activeSkill.skillData
		local secsPerStrike = skillData.strikeInterval
		local durationSecs = skillData.duration
		local numStrikes = math.floor(durationSecs / secsPerStrike)

		skillData.dpsMultiplier = (skillData.dpsMultiplier or 1) * numStrikes
		output.NormalHitsPerCast = numStrikes
		output.SkillDPSMultiplier = (output.SkillDPSMultiplier or 1) * numStrikes

		if breakdown then
			breakdown.NormalHitsPerCast = {
				s_format("^8Lightning bolts strike all nearby enemies every^7 %.2f^8 seconds (^7%.2f^8 strikes/sec).", secsPerStrike, 1 / secsPerStrike),
				s_format("^8Balls lasts for ^7%d^8 seconds for a total of ^7%d^8 strikes.", durationSecs, numStrikes),
			}
		end
	end,
#baseMod skill("strikeInterval", 0.15)
#baseMod skill("duration", 2)
#mods

#skill Bane
#flags spell duration area
	preSkillTypeFunc = function(activeSkill, output)
		local curseCount = 0
		for _, skill in ipairs(activeSkill.actor.activeSkillList) do
			if skill.socketGroup == activeSkill.socketGroup and skill.skillModList:GetCondition("AppliedByBane") then
				curseCount = curseCount + 1
				if curseCount == output.EnemyCurseLimit then
					break
				end
			end
		end
		activeSkill.skillModList:NewMod("Multiplier:CurseApplied", "BASE", curseCount, "Base")
	end,
	statMap = {
		["dark_ritual_damage_+%_final_per_curse_applied"] = {
			mod("Damage", "MORE", nil, 0, 0, { type = "Multiplier", var = "CurseApplied" }),
		},
		["dark_ritual_skill_effect_duration_+%_per_curse_applied"] = {
			mod("Duration", "INC", nil, 0, 0, { type = "Multiplier", var = "CurseApplied" }),
		},
		["apply_linked_curses_with_dark_ritual"] = {
		},
		["cannot_cast_curses"] = {
		},
		["display_linked_curse_effect_+%_final"] = {
		},
		["display_linked_curse_effect_+%"] = {
		},
		["support_bane_curse_effect_+%_final"] = {
		},
	},
#baseMod skill("debuff", true)
#mods

#skill SupportDarkRitual Bane
	statMap = {
		["apply_linked_curses_with_dark_ritual"] = {
			flag("Condition:AppliedByBane"),
		},
		["support_bane_curse_effect_+%_final"] = {
			mod("CurseEffect", "MORE", nil),
		},
	},
#mods

#skill BaneAltX
#flags spell duration area
	preSkillTypeFunc = function(activeSkill, output)
		local curseCount = 0
		for _, skill in ipairs(activeSkill.actor.activeSkillList) do
			if skill.socketGroup == activeSkill.socketGroup and skill.skillModList:GetCondition("AppliedByBane") then
				curseCount = curseCount + 1
				if curseCount == output.EnemyCurseLimit then
					break
				end
			end
		end
		activeSkill.skillModList:NewMod("Multiplier:CurseApplied", "BASE", curseCount, "Base")
	end,
	statMap = {
		["dark_ritual_damage_+%_final_per_curse_applied"] = {
			mod("Damage", "MORE", nil, 0, 0, { type = "Multiplier", var = "CurseApplied" }),
		},
		["dark_ritual_skill_effect_duration_+%_per_curse_applied"] = {
			mod("Duration", "INC", nil, 0, 0, { type = "Multiplier", var = "CurseApplied" }),
		},
		["apply_linked_curses_with_dark_ritual"] = {
		},
		["cannot_cast_curses"] = {
		},
		["support_bane_curse_effect_+%_final"] = {
		},
	},
#baseMod skill("debuff", true)
#mods

#skill SupportDarkRitualAltX Bane of Condemnation
	statMap = {
		["apply_linked_curses_with_dark_ritual"] = {
			flag("Condition:AppliedByBane"),
		},
		["support_bane_curse_effect_+%_final"] = {
			mod("CurseEffect", "MORE", nil),
		},
	},
#mods

#skill BlazingSalvo
#flags spell area projectile
	parts = {
		{
			name = "1 Projectile",
		},
		{
			name = "All Projectiles",
		},
	},
	preDamageFunc = function(activeSkill, output)
		if activeSkill.skillPart == 2 then
			activeSkill.skillData.dpsMultiplier = (activeSkill.skillData.dpsMultiplier or 1) * output.ProjectileCount
		end
	end,
#baseMod skill("radius", 16)
#baseMod skill("radiusLabel", "Minimum Range:")
#baseMod skill("radiusSecondary", 22)
#baseMod skill("radiusSecondaryLabel", "Maximum Range:")
#baseMod flag("CannotSplit")
#mods

#skill Blight
#flags spell duration area
	parts = {
		{
			name = "Manual Stacks",
			stages = true,
		},
		{
			name = "Maximum Sustainable Stacks",
		},
	},
	statMap = {
		["display_max_blight_stacks"] = {
			mod("Multiplier:BlightMaxStages", "BASE", nil, 0, 0, { type = "SkillPart", skillPart = 1 }),
			mod("BlightBaseMaxStages", "BASE", nil, 0, 0, { type = "SkillPart", skillPart = 2 }),
		},
	},
#baseMod mod("Damage", "MORE", 100, 0, 0, { type = "Multiplier", var = "BlightStageAfterFirst" })
#baseMod skill("debuff", true)
#baseMod skill("debuffSecondary", true)
#baseMod skill("radius", 26)
#mods

#skill BlightAltX
#flags spell duration area
	parts = {
		{
			name = "Manual Stacks",
			stages = true,
		},
		{
			name = "Maximum Sustainable Stacks",
		},
	},
	statMap = {
		["display_max_blight_stacks"] = {
			mod("Multiplier:BlightofContagionMaxStages", "BASE", nil, 0, 0, { type = "SkillPart", skillPart = 1 }),
			mod("BlightBaseMaxStages", "BASE", nil, 0, 0, { type = "SkillPart", skillPart = 2 }),
		},
	},
#baseMod mod("Damage", "MORE", 100, 0, 0, { type = "Multiplier", var = "BlightofContagionStageAfterFirst" })
#baseMod skill("debuff", true)
#baseMod skill("debuffSecondary", true)
#baseMod skill("radius", 26)
#mods

#skill BlightAltY
#flags spell duration area
	parts = {
		{
			name = "Manual Stacks",
			stages = true,
		},
		{
			name = "Maximum Sustainable Stacks",
		},
	},
	statMap = {
		["display_max_blight_stacks"] = {
			mod("Multiplier:BlightofAtrophyMaxStages", "BASE", nil, 0, 0, { type = "SkillPart", skillPart = 1 }),
			mod("BlightBaseMaxStages", "BASE", nil, 0, 0, { type = "SkillPart", skillPart = 2 }),
		},
	},
#baseMod mod("Damage", "MORE", 100, 0, 0, { type = "Multiplier", var = "BlightofAtrophyStageAfterFirst" })
#baseMod skill("debuff", true)
#baseMod skill("debuffSecondary", true)
#baseMod skill("radius", 26)
#mods

#skill VaalBlight
#flags spell duration area
	statMap = {
		["hinder_enemy_chaos_damage_taken_+%"] = {
			mod("ChaosDamageTaken", "INC", nil, 0, 0, { type = "GlobalEffect", effectType = "Debuff", effectName = "Hinder" }),
		},
		["display_max_blight_stacks"] = {
			mod("Multiplier:BlightMaxStages", "BASE", nil, 0, 0, { type = "SkillPart", skillPart = 1 }),
		},
	},
#baseMod skill("radius", 20)
#mods

#skill Bodyswap
#flags spell area
	parts = {
		{
			name = "Self Explosion",
			spell = true,
			cast = false,
		},
		{
			name = "Corpse Explosion",
			spell = false,
			cast =  true,
		},
	},
	preDamageFunc = function(activeSkill, output)
		if activeSkill.skillPart == 1 then
			local skillData = activeSkill.skillData
			if activeSkill.skillFlags.totem then
				skillData.FireBonusMin = output.TotemLife * skillData.selfFireExplosionLifeMultiplier
				skillData.FireBonusMax = output.TotemLife * skillData.selfFireExplosionLifeMultiplier
			else
				skillData.FireBonusMin = output.Life * skillData.selfFireExplosionLifeMultiplier
				skillData.FireBonusMax = output.Life * skillData.selfFireExplosionLifeMultiplier
			end
		end
	end,
	statMap = {
		["spell_minimum_base_fire_damage"] = {
			skill("FireMin", nil, { type = "SkillPart", skillPart = 1 }),
		},
		["spell_maximum_base_fire_damage"] = {
			skill("FireMax", nil, { type = "SkillPart", skillPart = 1 }),
		},
		["corpse_warp_area_of_effect_+%_final_when_consuming_corpse"] = {
			mod("AreaOfEffect", "MORE", nil, 0, 0, { type = "SkillPart", skillPart = 2 }),
		},
	},
#baseMod skill("explodeCorpse", true, { type = "SkillPart", skillPart = 2 })
#baseMod skill("radius", 14)
#mods

#skill BodyswapAltX
#flags spell area
	parts = {
		{
			name = "Self Explosion",
			spell = true,
			cast = false,
		},
		{
			name = "Minion Explosion",
			spell = false,
			cast =  true,
		},
	},
	preDamageFunc = function(activeSkill, output)
		if activeSkill.skillPart == 1 then
			local skillData = activeSkill.skillData
			if activeSkill.skillFlags.totem then
				skillData.FireBonusMin = output.TotemLife * skillData.selfFireExplosionLifeMultiplier
				skillData.FireBonusMax = output.TotemLife * skillData.selfFireExplosionLifeMultiplier
			else
				skillData.FireBonusMin = output.Life * skillData.selfFireExplosionLifeMultiplier
				skillData.FireBonusMax = output.Life * skillData.selfFireExplosionLifeMultiplier
			end
		end
	end,
	statMap = {
		["spell_minimum_base_fire_damage"] = {
			skill("FireMin", nil, { type = "SkillPart", skillPart = 1 }),
		},
		["spell_maximum_base_fire_damage"] = {
			skill("FireMax", nil, { type = "SkillPart", skillPart = 1 }),
		},
		["spell_base_fire_damage_%_maximum_life"] = {
			skill("selfFireExplosionLifeMultiplier", nil, { type = "SkillPart", skillPart = 1 }),
			div = 100,
		},
		["skill_minion_explosion_life_%"] = {
			skill("selfFireExplosionLifeMultiplier", nil, { type = "SkillPart", skillPart = 2 }),
			div = 100,
		},
		["corpse_warp_area_of_effect_+%_final_when_consuming_minion"] = {
			mod("AreaOfEffect", "MORE", nil, 0, 0, { type = "SkillPart", skillPart = 2 }),
		},
	},
#baseMod skill("explodeCorpse", true, { type = "SkillPart", skillPart = 2 })
#baseMod skill("radius", 14)
#mods

#skill BoneOffering
#flags spell duration
	statMap = {
		["monster_base_block_%"] = {
			mod("BlockChance", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Buff" }),
		},
		["base_spell_block_%"] = {
			mod("SpellBlockChance", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Buff" }),
		},
	},
#baseMod skill("buffMinions", true)
#baseMod skill("buffNotPlayer", true)
#mods

#skill BrandRecall
#flags spell
	statMap = {
		["recall_sigil_target_search_range_+%"] = {
			mod("BrandAttachmentRange", "INC", nil, 0, 0,{ type = "Condition", var = "CannotRecallBrand", neg = true }, { type = "GlobalEffect", effectType = "Buff" }),
		},
	},
#mods

#skill Clarity
#flags spell aura area
	statMap = {
		["base_mana_regeneration_rate_per_minute"] = {
			mod("ManaRegen", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Aura" }),
			div = 60,
		},
	},
#baseMod skill("radius", 40)
#mods

#skill VaalClarity
#flags spell aura area duration
	statMap = {
		["no_mana_cost"] = {
			mod("ManaCost", "MORE", nil, 0, 0, { type = "GlobalEffect", effectType = "Aura", unscalable = true}),
			value = -100,
		},
	},
#baseMod skill("radius", 40)
#mods

#skill ColdSnap
#flags spell area
#baseMod skill("dotIsArea", true)
#baseMod skill("radiusLabel", "Initial Burst:")
#baseMod skill("radiusSecondaryLabel", "Initial Chilled Ground:")
#baseMod skill("radiusTertiaryLabel", "Final Chilled Ground:")
#mods

#skill ColdSnapAltX
#flags spell area
#mods

#skill VaalColdSnap
#flags spell area duration
#baseMod skill("dotIsArea", true)
#baseMod skill("radiusLabel", "Initial Burst:")
#baseMod skill("radiusSecondaryLabel", "Initial Chilled Ground:")
#baseMod skill("radiusTertiaryLabel", "Final Chilled Ground:")
#mods

#skill Conductivity
#flags spell curse area duration hex
	statMap = {
		["base_lightning_damage_resistance_%"] = {
			mod("LightningResist", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Curse" }),
		},
		["chance_to_be_shocked_%"] = {
			mod("SelfShockChance", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Curse" }),
		},
		["base_self_shock_duration_-%"] = {
			mod("SelfShockDuration", "INC", nil, 0, 0, { type = "GlobalEffect", effectType = "Curse" }),
			div = -1,
		},
	},
#baseMod skill("debuff", true)
#baseMod skill("radius", 22)
#mods

#skill Contagion
#flags spell area duration
#baseMod skill("debuff", true)
#mods

#skill ContagionAltX
#flags spell area duration
#baseMod skill("debuff", true)
#mods

#skill ContagionAltY
#flags spell area duration
#baseMod skill("debuff", true)
#mods

#skill ConversionTrap
#flags spell duration trap
#mods

#skill Convocation
#flags spell duration
	statMap = {
		["base_life_regeneration_rate_per_minute"] = {
			mod("LifeRegen", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Buff" }),
			div = 60,
		},
	},
#baseMod skill("buffMinions", true)
#baseMod skill("buffNotPlayer", true)
#mods

#skill CracklingLance
#flags spell area
	statMap = {
		["disintegrate_damage_+%_final_per_intensity"] = {
			mod("Damage", "MORE", nil, 0, 0, { type = "Multiplier", var = "Intensity", limitVar = "IntensityLimit" }),
		},
		["disintegrate_base_radius_+_per_intensify"] = {
			skill("radiusExtra", nil, { type = "Multiplier", var = "Intensity", limitVar = "IntensityLimit" }),
		},
		["quality_display_disintegrate_is_gem"] = {
			-- Display only
		},
	},
#baseMod skill("radius", 10)
#mods

#skill CracklingLanceAltX
#flags spell area
#baseMod skill("radius", 10)
#mods

#skill CracklingLanceAltY
#flags spell area
#baseMod skill("radius", 10)
#mods

#skill DarkPact
#flags spell area chaining
	parts = {
		{
			name = "Cast on Player",
		},
		{
			name = "Cast on Skeleton",
		},
	},
	preDamageFunc = function(activeSkill, output)
		local life
		if activeSkill.skillPart == 1 then
			if activeSkill.skillFlags.totem then
				life = output.TotemLife
			else
				life = output.Life
			end
		else
			life = activeSkill.skillData.skeletonLife or 0
		end
		local add = life * activeSkill.skillData.lifeDealtAsChaos / 100
		activeSkill.skillData.ChaosMin = activeSkill.skillData.ChaosMin + add
		activeSkill.skillData.ChaosMax = activeSkill.skillData.ChaosMax + add
	end,
	statMap = {
		["skeletal_chains_aoe_%_health_dealt_as_chaos_damage"] = {
			skill("lifeDealtAsChaos", nil),
		},
		["skeletal_chains_no_minions_radius_+"] = {
			skill("radiusExtra", nil, { type = "SkillPart", skillPart = 1 }),
		},
		["skeletal_chains_no_minions_damage_+%_final"] = {
			mod("Damage", "MORE", nil, 0, bit.bor(KeywordFlag.Hit, KeywordFlag.Ailment), { type = "SkillPart", skillPart = 1 }),
		},
		["quality_display_dark_pact_is_gem"] = {
			-- Display only
		},
	},
#baseMod skill("radius", 26)
#mods

#skill DarkPactAltX
#flags spell area
	parts = {
		{
			name = "Average",
		},
		{
			name = "Max Ruin",
		},
	},
	preDamageFunc = function(activeSkill, output)
		local life
		if activeSkill.skillFlags.totem then
			life = output.TotemLife
		else
			life = output.Life
		end
		local add = life * activeSkill.skillData.percentageLifeSacrificed / 100 * (activeSkill.skillData.percentageSacrificedDealtAsChaos / 100)
		if activeSkill.skillPart == 2 then
			activeSkill.skillData.ChaosMin = activeSkill.skillData.ChaosMin + math.floor(add)
			activeSkill.skillData.ChaosMax = activeSkill.skillData.ChaosMax + math.floor(add)
		else
			local avgCastsTillRuin = 7 / (1 + math.min(100, activeSkill.skillData.additionalRuinChance or 0) / 100)
			activeSkill.skillData.ChaosMin = activeSkill.skillData.ChaosMin + math.floor(add / avgCastsTillRuin)
			activeSkill.skillData.ChaosMax = activeSkill.skillData.ChaosMax + math.floor(add / avgCastsTillRuin)
		end
	end,
	statMap = {
		["dark_pact_sacrifice_%_life_from_max_ruin"] = {
			skill("percentageLifeSacrificed", nil),
		},
		["dark_pact_chaos_damage_added_from_sacrifice_+%"] = {
			skill("percentageSacrificedDealtAsChaos", nil),
		},
		["dark_pact_chance_to_gain_an_additional_ruin_%"] = {
			skill("additionalRuinChance"),
		},
	},
#baseMod skill("radius", 26)
#baseMod skill("showAverage", true, { type = "SkillPart", skillPart = 2 })
#mods

#skill Despair
#flags spell curse area duration hex
	statMap = {
		["base_chaos_damage_resistance_%"] = {
			mod("ChaosResist", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Curse" }),
		},
		["minimum_added_chaos_damage_taken"] = {
			mod("SelfChaosMin", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Curse" }),
		},
		["maximum_added_chaos_damage_taken"] = {
			mod("SelfChaosMax", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Curse" }),
		},
	},
#baseMod skill("debuff", true)
#baseMod skill("radius", 22)
#mods

#skill DestructiveLink
#flags spell duration
	statMap = {
		["critical_link_grants_base_critical_strike_multiplier_+"] = {
			mod("CritMultiplier", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Link" }),
		},
		["critical_link_grants_accuracy_rating_+%"] = {
			mod("Accuracy", "INC", nil, 0, 0, { type = "GlobalEffect", effectType = "Link" }),
		},
		["display_critical_link_overrides_main_hand_critical_strike_chance"] = {
			flag("MainHandCritIsEqualToParent", { type = "GlobalEffect", effectType = "Link" }, { type = "Condition", var = "MainHandAttack" }),
		},
	},
#mods

#skill Discharge
#flags spell area
#mods

#skill DischargeAltX
#flags spell area
#mods

#skill Discipline
#flags spell aura area
	statMap = {
		["energy_shield_recharge_rate_+%"] = {
			mod("EnergyShieldRecharge", "INC", nil, 0, 0, { type = "GlobalEffect", effectType = "Aura" }),
		},
		["base_maximum_energy_shield"] = {
			mod("EnergyShield", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Aura" }),
		},
	},
#baseMod skill("radius", 40)
#mods

#skill VaalDiscipline
#flags spell aura area duration
	statMap = {
		["energy_shield_recharge_not_delayed_by_damage"] = {
			mod("EnergyShieldRechargeNotDelayedByDamage", "DUMMY", nil, 0, 0, { type = "GlobalEffect", effectType = "Aura" }),
		},
	},
#baseMod skill("radius", 40)
#mods

#skill DivineIre
#flags spell area
	preDamageFunc = function(activeSkill, output)
		if activeSkill.skillPart == 2 then
			activeSkill.skillData.hitTimeMultiplier = activeSkill.skillModList:Sum("BASE", activeSkill.skillCfg, "Multiplier:DivineIreStage")
		end
	end,
	parts = {
		{
			name = "Channelling",
			area = false,
		},
		{
			name = "Release",
			area = true,
			stages = true,
			channelRelease = true,
		},
	},
	statMap = {
		["divine_tempest_damage_+%_final_while_channelling"] = {
			mod("Damage", "MORE", nil, 0, 0, { type = "SkillPart", skillPart = 1 }),
		},
		["divine_tempest_hit_damage_+%_final_per_stage"] = {
			mod("Damage", "MORE", nil, ModFlag.Hit, 0, { type = "Multiplier", var = "DivineIreStageAfterFirst" }),
		},
		["divine_tempest_ailment_damage_+%_final_per_stage"] = {
			mod("Damage", "MORE", nil, 0, KeywordFlag.Ailment, { type = "Multiplier", var = "DivineIreStageAfterFirst" }),
		},
	},
#baseMod mod("Multiplier:DivineIreMaxStages", "BASE", 10, 0, 0, { type = "SkillPart", skillPart = 2 })
#baseMod skill("radius", 38)
#mods

#skill DivineIreAltX
#flags spell area
	preDamageFunc = function(activeSkill, output)
		if activeSkill.skillPart == 2 then
			activeSkill.skillData.hitTimeMultiplier = activeSkill.skillModList:Sum("BASE", activeSkill.skillCfg, "Multiplier:DivineIreofHolyLightningStage")
		end
	end,
	parts = {
		{
			name = "Channelling",
			area = false,
		},
		{
			name = "Release",
			area = true,
			stages = true,
			channelRelease = true,
		},
	},
	statMap = {
		["divine_tempest_hit_damage_+%_final_per_stage"] = {
			mod("Damage", "MORE", nil, ModFlag.Hit, 0, { type = "Multiplier", var = "DivineIreofHolyLightningStageAfterFirst" }),
		},
		["divine_tempest_ailment_damage_+%_final_per_stage"] = {
			mod("Damage", "MORE", nil, 0, KeywordFlag.Ailment, { type = "Multiplier", var = "DivineIreofHolyLightningStageAfterFirst" }),
		},
		["divine_tempest_no_beam"] = {
			-- Display only
		},
	},
#baseMod mod("Multiplier:DivineIreofHolyLightningMaxStages", "BASE", 10, 0, 0, { type = "SkillPart", skillPart = 2 })
#baseMod skill("radius", 38)
#mods

#skill DivineIreAltY
#flags spell area channelRelease
	preDamageFunc = function(activeSkill, output)
		activeSkill.skillData.hitTimeMultiplier = activeSkill.skillModList:Sum("BASE", activeSkill.skillCfg, "Multiplier:DivineIreofDisintegrationStage")
	end,
	statMap = {
		["divine_tempest_hit_damage_+%_final_per_stage"] = {
			mod("Damage", "MORE", nil, ModFlag.Hit, 0, { type = "Multiplier", var = "DivineIreofDisintegrationStageAfterFirst" }),
		},
		["divine_tempest_ailment_damage_+%_final_per_stage"] = {
			mod("Damage", "MORE", nil, 0, KeywordFlag.Ailment, { type = "Multiplier", var = "DivineIreofDisintegrationStageAfterFirst" }),
		},
	},
#baseMod mod("Multiplier:DivineIreofDisintegrationMaxStages", "BASE", 10, 0, 0)
#mods

#skill DivineRetribution
#flags spell area
#mods

#skill ElementalWeakness
#flags spell curse area duration hex
	statMap = {
		["base_resist_all_elements_%"] = {
			mod("ElementalResist", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Curse" }),
		},
		["self_elemental_status_duration_-%"] = {
			mod("SelfElementalAilmentDuration", "INC", nil, 0, 0, { type = "GlobalEffect", effectType = "Curse" }),
			mult = -1
		}
	},
#baseMod skill("debuff", true)
#baseMod skill("radius", 22)
#mods

#skill EnergyBlade
#flags spell
	statMap = {
		["storm_blade_energy_shield_+%_final"] = {
			mod("EnergyShield", "MORE", nil, 0, 0, { type = "GlobalEffect", effectType = "Buff" }),
		},
		["storm_blade_minimum_lightning_damage_from_es_%"] = {
			mod("EnergyBladeMinLightning", "BASE", nil, 0, 0, { type = "PercentStat", stat = "EnergyShield", percent = 1 }, { type = "GlobalEffect", effectType = "Buff", unscalable = true  }),
		},
		["storm_blade_maximum_lightning_damage_from_es_%"] = {
			mod("EnergyBladeMaxLightning", "BASE", nil, 0, 0, { type = "PercentStat", stat = "EnergyShield", percent = 1 }, { type = "GlobalEffect", effectType = "Buff", unscalable = true  }),
		},
		["storm_blade_damage_+%_final_with_two_hand_weapon"] = {
			mod("EnergyBladeDamage", "MORE", nil, 0, 0, { type = "Condition", var = "UsingTwoHandedWeapon" }, { type = "GlobalEffect", effectType = "Buff", unscalable = true  }),
		},
		["storm_blade_minimum_lightning_damage"] = {
			mod("EnergyBladeMinLightning", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Buff", unscalable = true  }),
		},
		["storm_blade_maximum_lightning_damage"] = {
			mod("EnergyBladeMaxLightning", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Buff", unscalable = true  }),
		},
	},
#mods

#skill Enfeeble
#flags spell curse area duration hex
	statMap = {
		["enfeeble_damage_+%_final"] = {
			mod("Damage", "MORE", nil, 0, 0, { type = "GlobalEffect", effectType = "Curse" }, { type = "Condition", var = "RareOrUnique", neg = true }),
		},
		["enfeeble_damage_+%_vs_rare_or_unique_final"] = {
			mod("Damage", "MORE", nil, 0, 0, { type = "GlobalEffect", effectType = "Curse" }, { type = "Condition", var = "RareOrUnique" }),
		},
		["accuracy_rating_+%"] = {
			mod("Accuracy", "INC", nil, 0, 0, { type = "GlobalEffect", effectType = "Curse" }),
		},
	},
#baseMod skill("debuff", true)
#baseMod skill("radius", 22)
#mods

#skill EssenceDrain
#flags spell projectile duration
#baseMod skill("debuff", true)
#baseMod skill("showAverage", true)
#baseMod skill("radius", 8)
#mods

#skill EssenceDrainAltX
#flags spell projectile duration
#baseMod skill("debuff", true)
#baseMod skill("showAverage", true)
#baseMod skill("radius", 8)
#mods

#skill EssenceDrainAltY
#flags spell projectile duration
#baseMod skill("debuff", true)
#baseMod skill("showAverage", true)
#baseMod skill("radius", 8)
#mods

#skill EyeOfWinter
#flags spell projectile
	preDamageFunc = function(activeSkill, output)
		activeSkill.skillModList:NewMod("Damage", "MORE", activeSkill.skillModList:Sum("BASE", activeSkill.skillCfg, "EyeOfWinterRamp"), "Skill:EyeOfWinter", { type = "DistanceRamp", ramp = {{0,0},{60*output.ProjectileSpeedMod,1}} })
	end,
	statMap = {
		["freezing_pulse_damage_+%_final_at_long_range"] = {
			mod("EyeOfWinterRamp", "BASE", nil)
		},
		["quality_display_eye_of_winter_is_gem"] = {
			-- Display only
		},
		["quality_display_freezing_pulse_damage_at_long_range_is_gem"] = {
			-- Display only
		},
	},
#mods

#skill EyeOfWinterAltX
#flags spell projectile
	preDamageFunc = function(activeSkill, output)
		activeSkill.skillModList:NewMod("Damage", "MORE", activeSkill.skillModList:Sum("BASE", activeSkill.skillCfg, "EyeOfWinterRamp"), "Skill:EyeOfWinterAltX", { type = "DistanceRamp", ramp = {{0,0},{60*output.ProjectileSpeedMod,1}} })
	end,
	statMap = {
		["freezing_pulse_damage_+%_final_at_long_range"] = {
			mod("EyeOfWinterRamp", "BASE", nil)
		},
		["quality_display_eye_of_winter_is_gem"] = {
			-- Display only
		},
		["quality_display_freezing_pulse_damage_at_long_range_is_gem"] = {
			-- Display only
		},
	},
#mods

#skill EyeOfWinterAltY
#flags spell projectile
	preDamageFunc = function(activeSkill, output)
		activeSkill.skillModList:NewMod("Damage", "MORE", activeSkill.skillModList:Sum("BASE", activeSkill.skillCfg, "EyeOfWinterRamp"), "Skill:EyeOfWinterAltY", { type = "DistanceRamp", ramp = {{0,0},{60*output.ProjectileSpeedMod,1}} })
	end,
	statMap = {
		["freezing_pulse_damage_+%_final_at_long_range"] = {
			mod("EyeOfWinterRamp", "BASE", nil)
		},
		["quality_display_eye_of_winter_is_gem"] = {
			-- Display only
		},
		["quality_display_freezing_pulse_damage_at_long_range_is_gem"] = {
			-- Display only
		},
	},
#mods

#skill Fireball
#flags spell projectile
	parts = {
		{
			name = "Projectile",
			area = false,
		},
		{
			name = "Explosion",
			area = true,
		},
	},
#mods

#skill VaalFireball
#flags spell projectile
	parts = {
		{
			name = "Projectile",
			area = false,
		},
		{
			name = "Explosion",
			area = true,
		},
	},
#mods

#skill Firestorm
#flags spell area duration
	parts = {
		{
			name = "First Impact",
		},
		{
			name = "Subsequent Impacts",
		},
	},
	statMap = {
		["firestorm_initial_impact_damage_+%_final"] = {
			mod("Damage", "MORE", nil, 0, bit.bor(KeywordFlag.Hit, KeywordFlag.Ailment), { type = "SkillPart", skillPart = 1 })
		},
		["firestorm_initial_impact_area_of_effect_+%_final"] = {
			mod("AreaOfEffect", "MORE", nil, 0, 0, { type = "SkillPart", skillPart = 1 })
		},
	},
#baseMod skill("showAverage", false, { type = "SkillPart", skillPart = 1 })
#baseMod skill("radiusLabel", "Fireball explosion:")
#baseMod skill("radiusSecondary", 25)
#baseMod skill("radiusSecondaryLabel", "Area in which fireballs fall:")
#mods

#skill FirestormAltX
#flags spell area
#mods

#skill FirestormAltY
#flags spell area duration
#baseMod skill("radiusSecondary", 22)
#baseMod skill("radiusLabel", "Fire projectile:")
#baseMod skill("radiusSecondaryLabel", "Target area:")
#mods

#skill VaalFirestorm
#flags spell area duration
	statMap = {
		["vaal_firestorm_gem_explosion_area_of_effect_+%_final"] = {
			mod("AreaOfEffectSecondary", "MORE", nil, 0, 0)
		},
	},
#baseMod skill("dotIsArea", true)
#baseMod flag("dotIsBurningGround")
#baseMod skill("radiusLabel", "Area of fireball explosion:")
#baseMod skill("radiusSecondary", 20)
#baseMod skill("radiusSecondaryLabel", "Area in which fireballs fall:")
#mods

#skill FlameDash
#flags spell area duration
#baseMod skill("dotIsArea", true)
#baseMod flag("dotIsBurningGround")
#mods

#skill FlameDashAltY
#flags spell area duration
#baseMod skill("dotIsArea", true)
#baseMod flag("dotIsBurningGround")
#mods

#skill FlameWall
#flags spell area duration
	parts = {
		{
			name = "Primary Debuff",
			area = true,
		},
		{
			name = "Secondary Debuff",
			area = false,
		},
	},
	statMap = {
		["base_fire_damage_to_deal_per_minute"] = {
			skill("FireDot", nil, { type = "SkillPart", skillPart = 1 }),
			div = 60,
		},
		["secondary_base_fire_damage_to_deal_per_minute"] = {
			skill("FireDot", nil, { type = "SkillPart", skillPart = 2 }),
			div = 60,
		},
		["wall_maximum_length"] = {
			skill("radius", nil),
		},
		["flame_wall_minimum_added_fire_damage"] = {
			mod("FireMin", "BASE", nil, ModFlag.Projectile, 0, { type = "GlobalEffect", effectType = "Buff", effectName = "Flame Wall", effectCond = "FlameWallAddedDamage" }),
		},
		["flame_wall_maximum_added_fire_damage"] = {
			mod("FireMax", "BASE", nil, ModFlag.Projectile, 0, { type = "GlobalEffect", effectType = "Buff", effectName = "Flame Wall", effectCond = "FlameWallAddedDamage" }),
		},
		["quality_display_firewall_is_gem"] = {
			-- Display only
		},
		["quality_display_wall_length_is_gem"] = {
			-- Display only
		},
	},
#baseMod skill("radiusLabel", "Flame Wall Length:")
#baseMod skill("dotIsArea", true)
#baseMod skill("buffAllies", true)
#mods

#skill FlameSurge
#flags spell area duration
	statMap = {
		["flame_whip_damage_+%_final_vs_burning_enemies"] = {
			mod("Damage", "MORE", nil, ModFlag.Hit, 0, { type = "ActorCondition", actor = "enemy", var = "Burning" }),
		},
		["active_skill_base_area_length_+"] = {
			mod("AreaOfEffect", "BASE", nil),
		},
		["flame_surge_ignite_damage_as_burning_ground_damage_%"] = {
			mod("IgniteDpsAsBurningGround", "MAX", nil, 0, 0, { type = "GlobalEffect", effectType = "Buff", unscalable = true }),
		},
		["quality_display_flame_whip_is_gem"] = {
			-- Display only
		},
	},
#baseMod skill("radius", 30)
#mods

#skill FlameSurgeAltX
#flags spell area duration
	statMap = {
		["active_skill_base_area_length_+"] = {
			mod("AreaOfEffect", "BASE", nil),
		},
		["flame_surge_burning_ground_on_ignite_damage_%"] = {
			mod("IgniteDpsAsBurningGround", "MAX", nil),
		},
		["quality_display_alt_flame_whip_is_gem"] = {
			-- Display only
		},
	},
#baseMod skill("radius", 30)
#mods

#skill Flameblast
#flags spell area channelRelease
	preDamageFunc = function(activeSkill, output)
		activeSkill.skillData.hitTimeMultiplier = math.max(activeSkill.skillModList:Sum("BASE", activeSkill.skillCfg, "Multiplier:FlameblastStage") - activeSkill.skillModList:Sum("BASE", activeSkill.skillCfg, "Multiplier:FlameblastMinimumStage"), 1)
	end,
	statMap = {
		["charged_blast_spell_damage_+%_final_per_stack"] = {
			mod("Damage", "MORE", nil, ModFlag.Hit, 0, { type = "Multiplier", var = "FlameblastStage" }),
		},
		["flameblast_ailment_damage_+%_final_per_stack"] = {
			mod("Damage", "MORE", nil, 0, KeywordFlag.Ailment, { type = "Multiplier", var = "FlameblastStage" }),
		},
		["base_skill_show_average_damage_instead_of_dps"] = {
		},
		["flameblast_maximum_stages"] = {
			mod("Multiplier:FlameblastMaxStages", "BASE", nil),
		},
		["vaal_flameblast_radius_+_per_stage"] = {
			mod("AreaOfEffect", "BASE", nil, 0, 0, { type = "Multiplier", var = "FlameblastStageAfterFirst" }),
		},
		["quality_display_flameblast_is_gem"] = {
			-- Display only
		},
	},
#baseMod skill("radius", 2)
#baseMod mod("PvpTvalueMultiplier", "MORE", 100, 0, 0, { type = "Multiplier", var = "FlameblastStageAfterFirst" })
#mods

#skill FlameblastAltX
#flags spell area channelRelease
	preDamageFunc = function(activeSkill, output)
		activeSkill.skillData.hitTimeMultiplier = math.max(activeSkill.skillModList:Sum("BASE", activeSkill.skillCfg, "Multiplier:FlameblastofCelerityStage") - activeSkill.skillModList:Sum("BASE", activeSkill.skillCfg, "Multiplier:FlameblastMinimumStage"), 1)
	end,
	statMap = {
		["charged_blast_spell_damage_+%_final_per_stack"] = {
			mod("Damage", "MORE", nil, ModFlag.Hit, 0, { type = "Multiplier", var = "FlameblastofCelerityStage" }),
		},
		["base_skill_show_average_damage_instead_of_dps"] = {
		},
		["flameblast_maximum_stages"] = {
			mod("Multiplier:FlameblastofCelerityMaxStages", "BASE", nil),
		},
		["vaal_flameblast_radius_+_per_stage"] = {
			mod("AreaOfEffect", "BASE", nil, 0, 0, { type = "Multiplier", var = "FlameblastofCelerityStageAfterFirst" }),
		},
		["quality_display_flameblast_is_gem"] = {
			-- Display only
		},
		["flameblast_base_radius_override"] = {
			skill("radius", nil),
		},
	},
#baseMod mod("PvpTvalueMultiplier", "MORE", 100, 0, 0, { type = "Multiplier", var = "FlameblastofCelerityStageAfterFirst" })
#mods

#skill FlameblastAltY
#flags spell area channelRelease
	preDamageFunc = function(activeSkill, output)
		activeSkill.skillData.hitTimeMultiplier = math.max(activeSkill.skillModList:Sum("BASE", activeSkill.skillCfg, "Multiplier:FlameblastofContractionStage") - activeSkill.skillModList:Sum("BASE", activeSkill.skillCfg, "Multiplier:FlameblastMinimumStage"), 1)
	end,
	statMap = {
		["charged_blast_spell_damage_+%_final_per_stack"] = {
			mod("Damage", "MORE", nil, ModFlag.Hit, 0, { type = "Multiplier", var = "FlameblastofContractionStage" }),
		},
		["flameblast_ailment_damage_+%_final_per_stack"] = {
			mod("Damage", "MORE", nil, 0, KeywordFlag.Ailment, { type = "Multiplier", var = "FlameblastofContractionStage" }),
		},
		["base_skill_show_average_damage_instead_of_dps"] = {
		},
		["flameblast_maximum_stages"] = {
			mod("Multiplier:FlameblastofContractionMaxStages", "BASE", nil),
		},
		["vaal_flameblast_radius_+_per_stage"] = {
			mod("AreaOfEffect", "BASE", nil, 0, 0, { type = "Multiplier", var = "FlameblastofContractionStageAfterFirst" }),
		},
		["quality_display_flameblast_is_gem"] = {
			-- Display only
		},
		["flameblast_base_radius_override"] = {
			skill("radius", nil),
		},
	},
#baseMod mod("PvpTvalueMultiplier", "MORE", 100, 0, 0, { type = "Multiplier", var = "FlameblastofContractionStageAfterFirst" })
#mods

#skill VaalFlameblast
#flags spell area
	statMap = {
		["charged_blast_spell_damage_+%_final_per_stack"] = {
			mod("Damage", "MORE", nil, ModFlag.Hit, 0, { type = "Multiplier", var = "VaalFlameblastStage" }),
		},
		["flameblast_ailment_damage_+%_final_per_stack"] = {
			mod("Damage", "MORE", nil, 0, KeywordFlag.Ailment, { type = "Multiplier", var = "VaalFlameblastStage" }),
		},
		["vaal_flameblast_radius_+_per_stage"] = {
			mod("AreaOfEffect", "BASE", nil, 0, 0, { type = "Multiplier", var = "FlameblastStageAfterFirst" }),
		},
	},
#baseMod mod("Multiplier:VaalFlameblastMaxStages", "BASE", 15)
#baseMod skill("radius", 35)
#mods

#skill Flammability
#flags spell curse area duration hex
	statMap = {
		["base_fire_damage_resistance_%"] = {
			mod("FireResist", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Curse" }),
		},
		["chance_to_be_ignited_%"] = {
			mod("SelfIgniteChance", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Curse" }),
		},
		["base_self_ignite_duration_-%"] = {
			mod("SelfIgniteDuration", "INC", nil, 0, 0, { type = "GlobalEffect", effectType = "Curse" }),
			mult = -1,
		},
	},
#baseMod skill("debuff", true)
#baseMod skill("radius", 22)
#mods

#skill FleshOffering
#flags spell duration
	statMap = {
		["attack_speed_+%_granted_from_skill"] = {
			mod("Speed", "INC", nil, ModFlag.Attack, 0, { type = "GlobalEffect", effectType = "Buff" }),
		},
		["base_movement_velocity_+%"] = {
			mod("MovementSpeed", "INC", nil, 0, 0, { type = "GlobalEffect", effectType = "Buff" }),
		},
		["cast_speed_+%_granted_from_skill"] = {
			mod("Speed", "INC", nil, ModFlag.Cast, 0, { type = "GlobalEffect", effectType = "Buff" }),
		},
	},
#baseMod skill("buffMinions", true)
#baseMod skill("buffNotPlayer", true)
#mods

#skill ForbiddenRite
#flags spell projectile area
	parts = {
		{
			name = "1 Projectile",
		},
		{
			name = "All Primary Projectiles",
		},
	},
	preDamageFunc = function(activeSkill, output, breakdown)
		local add
		local t_insert = table.insert
		local s_format = string.format
		local basetakenFlat = activeSkill.skillModList:Sum("BASE", nil, "DamageTaken", "ChaosDamageTaken", "DamageTakenWhenHit", "ChaosDamageTakenWhenHit")
		local baseTakenInc = activeSkill.skillModList:Sum("INC", nil, "DamageTaken", "ChaosDamageTaken", "DamageTakenWhenHit", "ChaosDamageTakenWhenHit")
		local baseTakenMore = activeSkill.skillModList:More(nil, "DamageTaken", "ChaosDamageTaken","DamageTakenWhenHit", "ChaosDamageTakenWhenHit")
		local chaosDamageTaken = math.max((1 + baseTakenInc / 100) * baseTakenMore, 0)
		local chaosFlat = floor(round(basetakenFlat * chaosDamageTaken), 0)
		local life, energyShield, chaosResistance, SelfDamageTakenLife, SelfDamageTakenES
		if activeSkill.skillFlags.totem then
			life = output.TotemLife
			energyShield = output.TotemEnergyShield
			chaosResistance = output.TotemChaosResist
		else
			life = output.Life
			energyShield = output.EnergyShield
			chaosResistance = output.ChaosResist
		end
		add = life * activeSkill.skillData.lifeDealtAsChaos + energyShield * activeSkill.skillData.energyShieldDealtAsChaos
		SelfDamageTakenLife = floor(round(life * activeSkill.skillData.SelfDamageTakenLife) * (100 - chaosResistance) / 100 * chaosDamageTaken)
		SelfDamageTakenES = floor(round(energyShield * activeSkill.skillData.SelfDamageTakenES) * (100 - chaosResistance) / 100 * chaosDamageTaken)
		activeSkill.skillData.ChaosMin = activeSkill.skillData.ChaosMin + add
		activeSkill.skillData.ChaosMax = activeSkill.skillData.ChaosMax + add
		if activeSkill.skillPart == 2 then
			activeSkill.skillData.dpsMultiplier = (activeSkill.skillData.dpsMultiplier or 1) * (output.ProjectileCount + 1)
		end
		output.FRDamageTaken = SelfDamageTakenLife + SelfDamageTakenES + chaosFlat
		if breakdown then
			local FRDamageTaken = {}
			t_insert(FRDamageTaken, s_format("Damage Taken per cast from Forbidden Rite: %d", output.FRDamageTaken))
			t_insert(FRDamageTaken, s_format("^8=^7 %d^8 (Life) *^7 %d%%^8 (of Life taken as Chaos Damage)", life, activeSkill.skillData.SelfDamageTakenLife * 100))
			if energyShield ~= 0 then
				t_insert(FRDamageTaken, s_format("^8+^7 %d^8 (ES) *^7 %d%%^8 (of ES taken as Chaos Damage)", energyShield, activeSkill.skillData.SelfDamageTakenES * 100))
			end
			t_insert(FRDamageTaken, s_format("^8=^7 %d^8 (Chaos Damage) *^7 %d%%^8 (Chaos Resistance)", life * activeSkill.skillData.SelfDamageTakenLife + energyShield * activeSkill.skillData.SelfDamageTakenES, chaosResistance))
			if chaosFlat ~= 0 then
				t_insert(FRDamageTaken, s_format("^8 -^7 %d^8 (Flat Damage reduction)", -basetakenFlat))
			end
			if chaosDamageTaken ~= 1 then
				t_insert(FRDamageTaken, s_format("^8 *^7 %.2f^8 (Damage taken Multiplier)", chaosDamageTaken))
			end
			breakdown.FRDamageTaken = FRDamageTaken
		end
	end,
	statMap = {
		["skill_base_chaos_damage_%_maximum_life"] = {
			skill("lifeDealtAsChaos", nil),
			div = 100,
		},
		["skill_base_chaos_damage_%_maximum_energy_shield"] = {
			skill("energyShieldDealtAsChaos", nil),
			div = 100,
		},
		["base_skill_area_of_effect_+%"] = {
			mod("AreaOfEffect", "INC", nil),
		},
		["soulfeast_take_%_maximum_life_as_chaos_damage"] = {
			skill("SelfDamageTakenLife", nil),
			div = 100,
		},
		["soulfeast_take_%_maximum_energy_shield_as_chaos_damage"] = {
			skill("SelfDamageTakenES", nil),
			div = 100,
		},
	},
#baseMod skill("radius", 8)
#mods

#skill ForbiddenRiteAltX
#flags spell projectile area
	parts = {
		{
			name = "1 Projectile",
		},
		{
			name = "All Primary Projectiles",
		},
	},
	preDamageFunc = function(activeSkill, output, breakdown)
		local add
		local t_insert = table.insert
		local s_format = string.format
		local basetakenFlat = activeSkill.skillModList:Sum("BASE", nil, "DamageTaken", "ChaosDamageTaken", "DamageTakenWhenHit", "ChaosDamageTakenWhenHit")
		local baseTakenInc = activeSkill.skillModList:Sum("INC", nil, "DamageTaken", "ChaosDamageTaken", "DamageTakenWhenHit", "ChaosDamageTakenWhenHit")
		local baseTakenMore = activeSkill.skillModList:More(nil, "DamageTaken", "ChaosDamageTaken","DamageTakenWhenHit", "ChaosDamageTakenWhenHit")
		local chaosDamageTaken = math.max((1 + baseTakenInc / 100) * baseTakenMore, 0)
		local chaosFlat = floor(round(basetakenFlat * chaosDamageTaken), 0)
		local energyShield, chaosResistance, SelfDamageTakenES
		if activeSkill.skillFlags.totem then
			energyShield = output.TotemEnergyShield
			chaosResistance = output.TotemChaosResist
		else
			energyShield = output.EnergyShield
			chaosResistance = output.ChaosResist
		end
		add = energyShield * activeSkill.skillData.energyShieldDealtAsChaos
		SelfDamageTakenES = floor(round(energyShield * activeSkill.skillData.SelfDamageTakenES) * (100 - chaosResistance) / 100 * chaosDamageTaken)
		activeSkill.skillData.ChaosMin = activeSkill.skillData.ChaosMin + add
		activeSkill.skillData.ChaosMax = activeSkill.skillData.ChaosMax + add
		if activeSkill.skillPart == 2 then
			activeSkill.skillData.dpsMultiplier = (activeSkill.skillData.dpsMultiplier or 1) * (output.ProjectileCount + 1)
		end
		output.FRDamageTaken = SelfDamageTakenES + chaosFlat
		if breakdown then
			local FRDamageTaken = {}
			t_insert(FRDamageTaken, s_format("Damage Taken per cast from Forbidden Rite: %d", output.FRDamageTaken))
			if energyShield ~= 0 then
				t_insert(FRDamageTaken, s_format("^8+^7 %d^8 (ES) *^7 %d%%^8 (of ES taken as Chaos Damage)", energyShield, activeSkill.skillData.SelfDamageTakenES * 100))
			end
			t_insert(FRDamageTaken, s_format("^8=^7 %d^8 (Chaos Damage) *^7 %d%%^8 (Chaos Resistance)", energyShield * activeSkill.skillData.SelfDamageTakenES, chaosResistance))
			if chaosFlat ~= 0 then
				t_insert(FRDamageTaken, s_format("^8 -^7 %d^8 (Flat Damage reduction)", -basetakenFlat))
			end
			if chaosDamageTaken ~= 1 then
				t_insert(FRDamageTaken, s_format("^8 *^7 %.2f^8 (Damage taken Multiplier)", chaosDamageTaken))
			end
			breakdown.FRDamageTaken = FRDamageTaken
		end
	end,
	statMap = {
		["skill_base_chaos_damage_%_maximum_energy_shield"] = {
			skill("energyShieldDealtAsChaos", nil),
			div = 100,
		},
		["base_skill_area_of_effect_+%"] = {
			mod("AreaOfEffect", "INC", nil),
		},
		["soulfeast_take_%_maximum_energy_shield_as_chaos_damage"] = {
			skill("SelfDamageTakenES", nil),
			div = 100,
		},
	},
#baseMod skill("radius", 8)
#mods

#skill FreezingPulse
#flags spell projectile
	preDamageFunc = function(activeSkill, output)
		activeSkill.skillModList:NewMod("Damage", "MORE", -50, "Skill:FreezingPulse", { type = "DistanceRamp", ramp = {{0,0},{60*output.ProjectileSpeedMod,1}} })
		activeSkill.skillModList:NewMod("EnemyFreezeChance", "BASE", 25, "Skill:FreezingPulse", { type = "DistanceRamp", ramp = {{0,1},{15*output.ProjectileSpeedMod,0}} })
	end,
	statMap = {
		["display_what_freezing_pulse_does"] = {
		},
	},
#mods

#skill FrostBomb
#flags spell area duration
	statMap = {
		["base_cold_damage_resistance_%"] = {
			mod("ColdExposure", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Debuff", effectName = "Frost Bomb" }),
		},
		["life_regeneration_rate_+%"] = {
			mod("LifeRegen", "INC", nil, 0, 0, { type = "GlobalEffect", effectType = "Debuff", effectName = "Frost Bomb" }),
		},
	},
#baseMod skill("debuffSecondary", true)
#mods

#skill FrostBombAltX
#flags spell area duration
#mods

#skill FrostBombAltY
#flags spell area duration
	preDamageFunc = function(activeSkill, output)
		local duration = math.floor(activeSkill.skillData.duration * output.DurationMod * 10)
		activeSkill.skillModList:NewMod("Multiplier:100msFrostBombDuration", "BASE", duration, "Skill:FrostBombAltY")
	end,
	statMap = {
		["active_skill_hit_damage_+%_final_per_100ms_duration"] = {
			mod("Damage", "MORE", nil, 0, KeywordFlag.Hit, { type = "Multiplier", var = "100msFrostBombDuration"} ),
		},
		["active_skill_ailment_damage_+%_final_per_100ms_duration"] = {
			mod("Damage", "MORE", nil, 0, KeywordFlag.Ailment, { type = "Multiplier", var = "100msFrostBombDuration"} ),
		},
	},
#mods

#skill FrostShield
#flags spell area duration
	statMap = {
		["frost_globe_additional_spell_base_critical_strike_chance_per_stage"] = {
			mod("CritChance", "BASE", nil, ModFlag.Spell, 0, { type = "Multiplier", var = "FrostShieldStage", limitVar = "FrostShieldMaxStages" }, { type = "GlobalEffect", effectType = "Buff", effectName = "Frost Shield" }),
			div = 100,
		},
		["energy_shield_lost_per_minute"] = {
			mod("EnergyShieldDegen", "BASE", nil, 0, 0, { type = "MultiplierThreshold", var = "FrostShieldStage", threshold = 1 }, { type = "GlobalEffect", effectType = "Buff", effectName = "Frost Shield" }),
			div = 60,
		},
		["frost_globe_absorb_damage_%_enemy_in_bubble"] = {
			mod("FrostGlobeDamageMitigation", "BASE", nil, 0, 0, { type = "ActorCondition", actor = "enemy", var = "EnemyInFrostGlobe" }, { type = "GlobalEffect", effectType = "Buff", effectName = "Frost Shield" }),
		},
		["frost_globe_absorb_damage_%_enemy_outside_bubble"] = {
			mod("FrostGlobeDamageMitigation", "BASE", nil, 0, 0, { type = "ActorCondition", actor = "enemy", var = "EnemyInFrostGlobe", neg = true }, { type = "GlobalEffect", effectType = "Buff", effectName = "Frost Shield" }),
		},
		["frost_globe_health_per_stage"] = {
			mod("FrostGlobeHealth", "BASE", nil, 0, 0, { type = "Multiplier", var = "FrostShieldStage", limitVar = "FrostShieldMaxStages" }, { type = "GlobalEffect", effectType = "Buff", effectName = "Frost Shield" }),
		},
		["frost_globe_max_stages"] = {
			mod("Multiplier:FrostShieldMaxStages", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Buff", unscalable = true }),
		},
	},
#baseMod skill("radius", 22)
#mods

#skill FrostWall
#flags spell duration
	statMap = {
		["quality_display_wall_length_is_gem"] = {
			-- Display only
		},
	},
#mods

#skill Frostbite
#flags spell curse area duration hex
	statMap = {
		["base_cold_damage_resistance_%"] = {
			mod("ColdResist", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Curse" }),
		},
		["chance_to_be_frozen_%"] = {
			mod("SelfFreezeChance", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Curse" }),
		},
		["base_self_freeze_duration_-%"] = {
			mod("SelfFreezeDuration", "INC", nil, 0, 0, { type = "GlobalEffect", effectType = "Curse" }),
			mult = -1,
		},
	},
#baseMod skill("debuff", true)
#baseMod skill("radius", 22)
#mods

#skill Frostblink
#flags spell area hit
	statMap = {
		["ice_dash_cooldown_recovery_per_nearby_normal_or_magic_enemy"] = {
			mod("CooldownRecovery", "INC", nil, 0, 0, { type = "ActorCondition", actor = "enemy", var = "RareOrUnique", neg = true }),
		},
		["ice_dash_cooldown_recovery_per_nearby_rare_or_unique_enemy"] = {
			mod("CooldownRecovery", "INC", nil, 0, 0, { type = "ActorCondition", actor = "enemy", var = "RareOrUnique" }),
		},
	},
#baseMod skill("showAverage", true)
#baseMod skill("radius", 20)
#baseMod skill("radiusLabel", "Area of initial explosion:")
#baseMod skill("radiusSecondary", 16)
#baseMod skill("radiusSecondaryLabel", "Area of Chilled Ground:")
#mods

#skill FrostblinkAltX
#flags spell area hit
	statMap = {
		["frostblink_damage_+%_final_per_5%_chill_effect_on_target"] = {
			mod("Damage", "MORE", nil, ModFlag.Hit, 0, { type = "Multiplier", var = "ChillEffect", div = 5, actor = "enemy" }),
		},
	},
#baseMod skill("radius", 20)
#mods

#skill FrostBolt
#flags spell projectile
#mods

#skill GalvanicField
#flags spell duration chaining
	preDamageFunc = function(activeSkill, output)
		activeSkill.skillData.hitTimeOverride = activeSkill.skillData.repeatFrequency
	end,
	parts = {
		{
			name = "Single Target",
		},
		{
			name = "Multi Target",
		}
	},
	statMap = {
		["galvanic_field_damage_+%_final_per_5%_increased_damage_taken_from_shock"] = {
			mod("Damage", "MORE", nil, ModFlag.Hit, 0, { type = "Multiplier", var = "ShockEffect", div = 5, actor = "enemy" }),
		},
		["galvanic_field_radius_+_per_10%_increased_damage_taken_from_shock"] = {
			skill("radiusExtra", nil, { type = "Multiplier", var = "ShockEffect", div = 10, actor = "enemy" }),
		},
		["galvanic_field_retargeting_delay_ms"] = {
			skill("repeatFrequency", nil, { type = "SkillPart", skillPart = 1 }),
			div = 1000,
		},
		["base_galvanic_field_beam_delay_ms"] = {
			skill("repeatFrequency", nil, { type = "SkillPart", skillPart = 2 }),
			div = 1000,
		},
		["base_chance_to_shock_%_from_skill"] = {
			mod("EnemyShockChance", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Buff", effectName = "Galvanic Field" }),
		},
		["base_skill_show_average_damage_instead_of_dps"] = {
		},
		["quality_display_shock_chance_from_skill_is_gem"] = {
			-- Display only
		},
	},
#baseMod skill("radius", 19)
#mods

#skill GalvanicFieldAltX
#flags spell duration chaining
	preDamageFunc = function(activeSkill, output)
		activeSkill.skillData.hitTimeOverride = activeSkill.skillData.repeatFrequency
	end,
	parts = {
		{
			name = "Single Target",
		},
		{
			name = "Multi Target",
		}
	},
	statMap = {
		["galvanic_field_retargeting_delay_ms"] = {
			skill("repeatFrequency", nil, { type = "SkillPart", skillPart = 1 }),
			div = 1000,
		},
		["base_galvanic_field_beam_delay_ms"] = {
			skill("repeatFrequency", nil, { type = "SkillPart", skillPart = 2 }),
			div = 1000,
		},
		["base_chance_to_shock_%_from_skill"] = {
			mod("EnemyShockChance", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Buff", effectName = "Galvanic Field of Intensity" }),
		},
		["base_skill_show_average_damage_instead_of_dps"] = {
		},
		["quality_display_shock_chance_from_skill_is_gem"] = {
			-- Display only
		},
	},
#baseMod skill("radius", 19)
#mods

#skill GlacialCascade
#flags spell area
	parts = {
		{
			name = "Initial Bursts",
		},
		{
			name = "Final Burst",
		},
	},
	statMap = {
		["glacial_cascade_final_spike_damage_+%_final"] = {
			mod("Damage", "MORE", nil, 0, 0, { type = "SkillPart", skillPart = 2 }),
		},
		["quality_display_glacial_cascade_is_gem"] = {
			-- Display Only
		},
	},
#baseMod skill("radius", 12)
#baseMod skill("radiusExtra", 12, { type = "SkillPart", skillPart = 2 })
#mods

#skill GlacialCascadeAltX
#flags spell area
#baseMod skill("radius", 12)
#mods

#skill Hydrosphere
#flags spell duration area
	parts = {
			{
				name = "Autopulse (Frozen)",
			},
			{
				name = "Autopulse (Shocked)",
			},
			{
				name = "Autopulse (Frozen & Shocked)",
			},
			{
				name = "Cast (No Ailment)",
			},
			{
				name = "Cast (Frozen)",
			},
			{
				name = "Cast (Shocked)",
			},
			{
				name = "Cast (Frozen & Shocked)",
			}
	},
	preDamageFunc = function(activeSkill, output)
		if activeSkill.skillPart == 1 or activeSkill.skillPart == 2 or activeSkill.skillPart == 3 then
			activeSkill.skillData.hitTimeOverride = activeSkill.skillData.repeatFrequency / (1 + activeSkill.skillModList:Sum("INC", activeSkill.skillCfg, "HydroSphereFrequency") / 100)
		end
	end,
	statMap = {
		["skill_physical_damage_%_to_convert_to_cold"] = {
			mod("SkillPhysicalDamageConvertToCold", "BASE", nil, 0, 0, { type = "SkillPart", skillPart = 1 }),
			mod("SkillPhysicalDamageConvertToLightning", "BASE", nil, 0, 0, { type = "SkillPart", skillPart = 2 }),
			mod("SkillPhysicalDamageConvertToCold", "BASE", nil, 0, 0, { type = "SkillPart", skillPart = 3 }),
			mod("SkillPhysicalDamageConvertToLightning", "BASE", nil, 0, 0, { type = "SkillPart", skillPart = 3 }),
			mod("SkillPhysicalDamageConvertToCold", "BASE", nil, 0, 0, { type = "SkillPart", skillPart = 4 }),
			mod("SkillPhysicalDamageConvertToCold", "BASE", nil, 0, 0, { type = "SkillPart", skillPart = 5 }),
			mod("SkillPhysicalDamageConvertToLightning", "BASE", nil, 0, 0, { type = "SkillPart", skillPart = 6 }),
			mod("SkillPhysicalDamageConvertToCold", "BASE", nil, 0, 0, { type = "SkillPart", skillPart = 7 }),
			mod("SkillPhysicalDamageConvertToLightning", "BASE", nil, 0, 0, { type = "SkillPart", skillPart = 7 }),
		},
		["water_sphere_cold_lightning_exposure_%"] = {
			mod("ColdExposure", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Debuff" } ),
			mod("LightningExposure", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Debuff" } ),
		},
		["hydro_sphere_base_pulse_frequency_ms"] = {
			skill("repeatFrequency", nil),
			div = 1000,
		},
		["hydro_sphere_pulse_frequency_+%"] = {
			mod("HydroSphereFrequency", "INC", nil),
		},
		["water_sphere_does_weird_conversion_stuff"] = {
			-- Display Only
		},
		["quality_display_hydrosphere_is_gem"] = {
			-- Display Only
		},
	},
#baseMod skill("radius", 23)
#mods

#skill Hexblast
#flags spell area
	parts = {
		{
			name = "Target",
			area = false,
		},
		{
			name = "Explosion",
			area = true,
		},
	},
	statMap = {
		["hexblast_hit_damage_+%_final_if_hexed"] = {
			mod("Damage", "MORE", nil, ModFlag.Hit, 0, { type = "ActorCondition", actor = "enemy", var = "Cursed" })
		},
		["hexblast_ailment_damage_+%_final_if_hexed"] = {
			mod("Damage", "MORE", nil, 0, KeywordFlag.Ailment, { type = "ActorCondition", actor = "enemy", var = "Cursed" })
		},
		["chaos_damage_resisted_by_lowest_resistance"] = {
			flag("ChaosDamageUsesLowestResistance"),
		},
		["hexblast_display_innate_remove_hex_100%_chance"] = {
			-- Display Only
		},
	},
#baseMod skill("showAverage", true)
#mods

#skill HexblastAltX
#flags spell area
	parts = {
		{
			name = "Target",
			area = false,
		},
		{
			name = "Explosion",
			area = true,
		},
	},
	statMap = {
		["hexblast_hit_damage_+%_final_if_hexed"] = {
			mod("Damage", "MORE", nil, ModFlag.Hit, 0, { type = "ActorCondition", actor = "enemy", var = "Cursed" })
		},
		["hexblast_ailment_damage_+%_final_if_hexed"] = {
			mod("Damage", "MORE", nil, 0, KeywordFlag.Ailment, { type = "ActorCondition", actor = "enemy", var = "Cursed" })
		},
		["chaos_damage_resisted_by_highest_resistance"] = {
			flag("ChaosDamageUsesHighestResistance"),
		},
		["hexblast_display_innate_remove_hex_100%_chance"] = {
			-- Display Only
		},
	},
#baseMod skill("showAverage", true)
#mods

#skill HexblastAltY
#flags spell area
	parts = {
		{
			name = "Target",
			area = false,
		},
		{
			name = "Explosion",
			area = true,
		},
	},
#mods

#skill HeraldOfThunder
#flags cast duration
	preDamageFunc = function(activeSkill, output)
		activeSkill.skillData.hitTimeOverride = activeSkill.skillData.repeatFrequency / (1 + activeSkill.skillModList:Sum("INC", activeSkill.skillCfg, "HeraldStormFrequency") / 100)
	end,
	statMap = {
		["spell_minimum_added_lightning_damage"] = {
			mod("LightningMin", "BASE", nil, 0, KeywordFlag.Spell, { type = "GlobalEffect", effectType = "Buff" }),
		},
		["spell_maximum_added_lightning_damage"] = {
			mod("LightningMax", "BASE", nil, 0, KeywordFlag.Spell, { type = "GlobalEffect", effectType = "Buff" }),
		},
		["attack_minimum_added_lightning_damage"] = {
			mod("LightningMin", "BASE", nil, 0, KeywordFlag.Attack, { type = "GlobalEffect", effectType = "Buff" }),
		},
		["attack_maximum_added_lightning_damage"] = {
			mod("LightningMax", "BASE", nil, 0, KeywordFlag.Attack, { type = "GlobalEffect", effectType = "Buff" }),
		},
		["herald_of_thunder_bolt_base_frequency"] = {
			skill("repeatFrequency", nil),
			div = 1000,
		},
		["herald_of_thunder_bolt_frequency_+%"] = {
			mod("HeraldStormFrequency", "INC", nil),
		},
	},
#baseMod skill("radius", 32)
#baseMod skill("showAverage", false)
#mods

#skill IceNova
#flags spell area
#mods

#skill IceNovaAltX
#flags spell area
	statMap = {
		["ice_nova_damage_when_cast_on_frostbolt_+%_final"] = {
			mod("Damage", "MORE", nil, 0, bit.bor(KeywordFlag.Hit, KeywordFlag.Ailment), { type = "Condition", var = "CastOnFrostbolt" }),
		},
	},
#mods

#skill IceNovaAltY
#flags spell area
	statMap = {
		["ice_nova_freeze_as_though_damage_+%_final"] = {
			mod("FreezeAsThoughDealing", "MORE", nil)
		},
	},
#mods

#skill VaalIceNova
#flags spell area
#mods

#skill IceSpear
#flags spell projectile
	preDamageFunc = function(activeSkill, output)
		if activeSkill.skillPart == 3 or activeSkill.skillPart == 4 then
			activeSkill.skillData.dpsMultiplier = (activeSkill.skillData.dpsMultiplier or 1) * output.ProjectileCount
		end
	end,
	parts = {
		{
			name = "First Form, 1 Projectile",
		},
		{
			name = "Second Form, 1 Projectile",
		},
		{
			name = "First Form, All Projectiles",
		},
		{
			name = "Second Form, All Projectiles",
		},
	},
	statMap = {
		["ice_spear_second_form_projectile_speed_+%_final"] = {
			mod("ProjectileSpeed", "MORE", nil, 0, 0, { type = "SkillPart", skillPartList = { 2, 4 } }),
		},
		["ice_spear_second_form_critical_strike_chance_+%"] = {
			mod("CritChance", "INC", nil, 0, 0, { type = "SkillPart", skillPartList = { 2, 4 } }),
		},
		["ice_spear_second_form_critical_strike_multiplier_+"] = {
			mod("CritMultiplier", "BASE", nil, 0, 0, { type = "SkillPart", skillPartList = { 2, 4 } }),
		},
	},
#baseMod mod("PierceChance", "BASE", 100, 0, 0, { type = "SkillPart", skillPart = 1 })
#mods

#skill IceSpearAltX
#flags spell projectile
	preDamageFunc = function(activeSkill, output)
		if activeSkill.skillPart == 3 or activeSkill.skillPart == 4 then
			activeSkill.skillData.dpsMultiplier = (activeSkill.skillData.dpsMultiplier or 1) * output.ProjectileCount
		end
	end,
	parts = {
		{
			name = "First Form, 1 Projectile",
		},
		{
			name = "Second Form, 1 Projectile",
		},
		{
			name = "First Form, All Projectiles",
		},
		{
			name = "Second Form, All Projectiles",
		},
	},
	statMap = {
		["ice_spear_second_form_projectile_speed_+%_final"] = {
			mod("ProjectileSpeed", "MORE", nil, 0, 0, { type = "SkillPart", skillPartList = { 2, 4 } }),
		},
	},
#baseMod mod("PierceChance", "BASE", 100, 0, 0, { type = "SkillPart", skillPart = 1 })
#mods

#skill IcicleMine
#flags spell projectile mine
	statMap = {
		["cold_projectile_mine_enemy_critical_strike_chance_+%_against_self"] = {
			mod("SelfCritChance", "INC", nil, 0, 0, { type = "Limit", limit = 500 }, { type = "GlobalEffect", effectType = "AuraDebuff", effectStackVar = "ActiveMineCount" }),
		},
	},
#mods

#skill IcicleMineAltX
#flags spell projectile mine
	statMap = {
		["cold_projectile_mine_enemy_critical_strike_chance_+%_against_self"] = {
			mod("SelfCritChance", "INC", nil, 0, 0, { type = "Limit", limit = 500 }, { type = "GlobalEffect", effectType = "AuraDebuff", effectStackVar = "ActiveMineCount" }),
		},
	},
#mods

#skill IcicleMineAltY
#flags spell projectile mine
	statMap = {
		["cold_projectile_mine_enemy_critical_strike_chance_+%_against_self"] = {
			mod("SelfCritChance", "INC", nil, 0, 0, { type = "Limit", limit = 500 }, { type = "GlobalEffect", effectType = "AuraDebuff", effectStackVar = "ActiveMineCount" }),
		},
	},
#mods

#skill Incinerate
#flags spell area
	preDamageFunc = function(activeSkill, output)
		if activeSkill.skillPart == 2 then
			activeSkill.skillData.hitTimeMultiplier = math.max(activeSkill.skillModList:Sum("BASE", activeSkill.skillCfg, "Multiplier:IncinerateStage") - activeSkill.skillModList:Sum("BASE", activeSkill.skillCfg, "Multiplier:IncinerateMinimumStage") - 0.4175, 0.5825) --First stage takes 0.5825x time to channel compared to subsequent stages
		end
	end,
	parts = {
		{
			name = "Channelling",
			stages = true,
		},
		{
			name = "Release",
			stages = true,
			channelRelease = true,
		},
	},
	statMap = {
		["grant_expanding_fire_cone_release_ignite_damage_+%_final"] = {
			mod("Damage", "MORE", nil, 0, KeywordFlag.Ignite, { type = "SkillPart", skillPart = 2 }),
		},
		["expanding_fire_cone_release_hit_damage_+%_final"] = {
			mod("Damage", "MORE", nil, ModFlag.Hit, 0, { type = "SkillPart", skillPart = 2 }),
		},
		["flamethrower_damage_+%_per_stage_final"] = {
			mod("Damage", "MORE", nil, 0, bit.bor(KeywordFlag.Hit, KeywordFlag.Ailment), { type = "Multiplier", var = "IncinerateStage" }),
		},
		["expanding_fire_cone_radius_+_per_stage"] = {
			skill("radiusExtra", nil, { type = "Multiplier", var = "IncinerateStage", limitVar = "IncinerateRadiusLimit", limitTotal = true }),
		},
		["expanding_fire_cone_final_wave_always_ignite"] = {
			mod("EnemyIgniteChance", "BASE", nil, 0, 0, { type = "SkillPart", skillPart = 2 }),
			value = 100,
		},
		["expanding_fire_cone_radius_limit"] = {
			mod("Multiplier:IncinerateRadiusLimit", "BASE", nil),
		},
		["expanding_fire_cone_maximum_number_of_stages"] = {
			mod("Multiplier:IncinerateMaxStages", "BASE", nil),
		},
		["quality_display_incinerate_is_gem_hit"] = {
			--Display Only
		},
		["quality_display_incinerate_is_gem_ingite"] = {
			--Display Only
		},
		["quality_display_incinerate_is_gem_stages"] = {
			--Display Only
		},
	},
#baseMod skill("radius", 25)
#baseMod skill("radiusLabel", "Flame Length:")
#baseMod skill("radiusSecondary", 20)
#baseMod skill("radiusSecondaryLabel", "Flame Width:")
#mods

#skill IncinerateAltX
#flags spell area
	preDamageFunc = function(activeSkill, output)
		if activeSkill.skillPart == 2 then
			activeSkill.skillData.hitTimeMultiplier = math.max(activeSkill.skillModList:Sum("BASE", activeSkill.skillCfg, "Multiplier:IncinerateofExpanseStage") - activeSkill.skillModList:Sum("BASE", activeSkill.skillCfg, "Multiplier:IncinerateMinimumStage") - 0.4175, 0.5825) --First stage takes 0.5825x time to channel compared to subsequent stages
		end
	end,
	parts = {
		{
			name = "Channelling",
			stages = true,
		},
		{
			name = "Release",
			stages = true,
			channelRelease = true,
		},
	},
	statMap = {
		["grant_expanding_fire_cone_release_ignite_damage_+%_final"] = {
			mod("Damage", "MORE", nil, 0, KeywordFlag.Ignite, { type = "SkillPart", skillPart = 2 }),
		},
		["expanding_fire_cone_release_hit_damage_+%_final"] = {
			mod("Damage", "MORE", nil, ModFlag.Hit, 0, { type = "SkillPart", skillPart = 2 }),
		},
		["flamethrower_damage_+%_per_stage_final"] = {
			mod("Damage", "MORE", nil, 0, bit.bor(KeywordFlag.Hit, KeywordFlag.Ailment), { type = "Multiplier", var = "IncinerateofExpanseStage" }),
		},
		["expanding_fire_cone_radius_+_per_stage"] = {
			skill("radiusExtra", nil, { type = "Multiplier", var = "IncinerateofExpanseStage", limitVar = "IncinerateofExpanseRadiusLimit", limitTotal = true }),
		},
		["expanding_fire_cone_final_wave_always_ignite"] = {
			mod("EnemyIgniteChance", "BASE", nil, 0, 0, { type = "SkillPart", skillPart = 2 }),
			value = 100,
		},
		["expanding_fire_cone_radius_limit"] = {
			mod("Multiplier:IncinerateofExpanseRadiusLimit", "BASE", nil),
		},
		["expanding_fire_cone_maximum_number_of_stages"] = {
			mod("Multiplier:IncinerateofExpanseMaxStages", "BASE", nil),
		},
		["quality_display_incinerate_is_gem_hit"] = {
			--Display Only
		},
		["quality_display_incinerate_is_gem_ingite"] = {
			--Display Only
		},
		["quality_display_incinerate_is_gem_stages"] = {
			--Display Only
		},
	},
#baseMod skill("radius", 25)
#baseMod skill("radiusLabel", "Flame Length:")
#baseMod skill("radiusSecondary", 20)
#baseMod skill("radiusSecondaryLabel", "Flame Width:")
#mods

#skill IncinerateAltY
#flags spell area
	preDamageFunc = function(activeSkill, output)
		if activeSkill.skillPart == 2 then
			activeSkill.skillData.hitTimeMultiplier = math.max(activeSkill.skillModList:Sum("BASE", activeSkill.skillCfg, "Multiplier:IncinerateofVentingStage") - activeSkill.skillModList:Sum("BASE", activeSkill.skillCfg, "Multiplier:IncinerateMinimumStage") - 0.4175, 0.5825) --First stage takes 0.5825x time to channel compared to subsequent stages
		end
	end,
	statMap = {
		["flamethrower_damage_+%_per_stage_final"] = {
			mod("Damage", "MORE", nil, 0, bit.bor(KeywordFlag.Hit, KeywordFlag.Ailment), { type = "Multiplier", var = "IncinerateofVentingStage" }),
		},
		["expanding_fire_cone_radius_+_per_stage"] = {
			skill("radiusExtra", nil, { type = "Multiplier", var = "IncinerateofVentingStage", limitVar = "IncinerateofVentingRadiusLimit", limitTotal = true }),
		},
		["expanding_fire_cone_radius_limit"] = {
			mod("Multiplier:IncinerateofVentingRadiusLimit", "BASE", nil),
		},
		["expanding_fire_cone_maximum_number_of_stages"] = {
			mod("Multiplier:IncinerateofVentingMaxStages", "BASE", nil),
		},
		["quality_display_incinerate_is_gem_hit"] = {
			--Display Only
		},
		["quality_display_incinerate_is_gem_ingite"] = {
			--Display Only
		},
		["quality_display_incinerate_is_gem_stages"] = {
			--Display Only
		},
	},
#baseMod skill("radius", 25)
#baseMod skill("radiusLabel", "Flame Length:")
#baseMod skill("radiusSecondary", 20)
#baseMod skill("radiusSecondaryLabel", "Flame Width:")
#mods

#skill KineticBlast
#flags attack projectile area
	parts = {
		{
			name = "Projectile",
			area = false,
		},
		{
			name = "Explosions",
			area = true,
		},
	},
#mods

#skill KineticBlastAltX
#flags attack projectile area
	parts = {
		{
			name = "Projectile",
			area = false,
		},
		{
			name = "Explosions",
			area = true,
		},
	},
	statMap = {
		["added_physical_damage_to_attacks_equal_to_%_maximum_mana"] = {
			mod("PhysicalMin", "BASE", nil, ModFlag.Attack, 0, { type = "PercentStat", stat = "Mana", percent = 1 }),
			mod("PhysicalMax", "BASE", nil, ModFlag.Attack, 0, { type = "PercentStat", stat = "Mana", percent = 1 }),
		},
		["kinetic_blast_modifiers_to_number_of_projectiles_instead_apply_to_number_of_clusters"] = {
			flag("NoAdditionalProjectiles"),
		},
	},
#mods

#skill KineticBolt
#flags attack projectile
#mods

#skill KineticBoltAltX
#flags attack projectile
#mods

#skill LightningSpireTrap
#flags spell trap duration area
	preDamageFunc = function(activeSkill, output, breakdown)
		local skillCfg = activeSkill.skillCfg
		local skillData = activeSkill.skillData
		local skillPart = activeSkill.skillPart
		local skillModList = activeSkill.skillModList
		local t_insert = table.insert
		local s_format = string.format

		-- seemingly the only mechanical difference with seismic trap - this one does not scale it's total radius with AoE modifiers
		output.AreaOfEffectRadius = skillData.radius
		if breakdown then
			breakdown.AreaOfEffectRadius = {"Targeting area of this skill is not affected by Area of Effect modifiers."}
		end

		local baseInterval = skillData.repeatInterval
		local incFrequency = (1 + skillModList:Sum("INC", skillCfg, "TrapThrowingSpeed") / 100)
		local moreFrequency = skillModList:More(skillCfg, "TrapThrowingSpeed")
		local wavePulseRate = incFrequency * moreFrequency / baseInterval
		skillData.hitTimeOverride = 1 / wavePulseRate
		output.WavePulseRate = wavePulseRate
		local incDuration = (1 + skillModList:Sum("INC", skillCfg, "Duration") / 100)
		local moreDuration = skillModList:More(skillCfg, "Duration")
		local duration = skillData.duration * incDuration * moreDuration
		local pulses = math.floor(duration * wavePulseRate)
		output.PulsesPerTrap = pulses
		local effectiveDuration = pulses / wavePulseRate
		local cooldown = output.TrapCooldown
		local averageActiveTraps = effectiveDuration / cooldown
		output.AverageActiveTraps = averageActiveTraps
		local function hitChance(enemyRadius, areaDamageRadius, areaSpreadRadius) -- not to be confused with attack hit chance
			local damagingAreaRadius = areaDamageRadius + enemyRadius - 1	-- radius where area damage can land to hit the enemy;
			-- -1 because of two assumptions: PoE coordinates are integers and damage is not registered if the two areas only share a point or vertex. If either is not correct, then -1 is not needed.
			return math.min(damagingAreaRadius * damagingAreaRadius / (areaSpreadRadius * areaSpreadRadius), 1)
		end
		local enemyRadius = skillModList:Override(skillCfg, "EnemyRadius") or skillModList:Sum("BASE", skillCfg, "EnemyRadius")
		local waveRadius = output.AreaOfEffectRadiusSecondary
		local fullRadius = output.AreaOfEffectRadius
		local overlapChance = hitChance(enemyRadius, waveRadius, fullRadius)
		output.OverlapChance = overlapChance * 100
		if breakdown then
			breakdown.OverlapChance = { }
			t_insert(breakdown.OverlapChance, "Chance for individual wave to land within range to damage enemy:")
			t_insert(breakdown.OverlapChance, "^8= (area where wave can spawn to damage enemy) / (total area)")
			t_insert(breakdown.OverlapChance, "^8= (^7secondary radius^8 + ^7enemy radius^8 - 1) ^ 2 / ^7radius^8 ^ 2")
			t_insert(breakdown.OverlapChance, s_format("^8= (^7%d^8 +^7 %d^8 - 1) ^ 2 /^7 %d^8 ^ 2", waveRadius, enemyRadius, fullRadius))
			t_insert(breakdown.OverlapChance, s_format("^8=^7 %.3f^8%%", overlapChance * 100))
			breakdown.WavePulseRate = { }
			t_insert(breakdown.WavePulseRate, "Pulse rate:")
			t_insert(breakdown.WavePulseRate, s_format("%.2f ^8(base pulse rate)", 1 / baseInterval))
			t_insert(breakdown.WavePulseRate, s_format("* %.2f ^8(increased/reduced pulse frequency)", incFrequency))
			t_insert(breakdown.WavePulseRate, s_format("* %.2f ^8(more/less pulse frequency)", moreFrequency))
			t_insert(breakdown.WavePulseRate, s_format("= %.2f^8/s", wavePulseRate))
			breakdown.PulsesPerTrap = { }
			t_insert(breakdown.PulsesPerTrap, "Pulses per trap:")
			t_insert(breakdown.PulsesPerTrap, s_format("%.3f ^8(unrounded skill duration)", duration))
			t_insert(breakdown.PulsesPerTrap, s_format("* %.2f ^8(pulse rate)", wavePulseRate))
			t_insert(breakdown.PulsesPerTrap, s_format("= %.2f ^8pulses", duration * wavePulseRate))
			t_insert(breakdown.PulsesPerTrap, "^8rounded down")
			t_insert(breakdown.PulsesPerTrap, s_format("= %d ^8pulses", pulses))
			t_insert(breakdown.PulsesPerTrap, s_format("^8Next breakpoint: %d%% increased Trap Throwing Speed / %d%% increased Duration",
					math.ceil(100 * ((pulses + 1) * baseInterval / (duration * moreFrequency) - incFrequency)),
					math.ceil(100 * ((pulses + 1) / (wavePulseRate * skillData.duration * moreDuration) - incDuration))
			))
			t_insert(breakdown.PulsesPerTrap, s_format("^8Previous breakpoint: %d%% reduced Trap Throwing Speed / %d%% reduced Duration",
					-math.ceil(100 * (pulses * baseInterval / (duration * moreFrequency) - incFrequency) - 1),
					-math.ceil(100 * (pulses / (wavePulseRate * skillData.duration * moreDuration) - incDuration) - 1)
			))
			breakdown.AverageActiveTraps = { }
			t_insert(breakdown.AverageActiveTraps, "Average active traps, not considering stored cooldown uses:")
			t_insert(breakdown.AverageActiveTraps, s_format("%.2f^8 /^7 %.2f^8 (pulses / pulse rate = effective skill duration)", pulses, wavePulseRate))
			t_insert(breakdown.AverageActiveTraps, s_format("/ %.2f ^8(cooldown)", cooldown))
			t_insert(breakdown.AverageActiveTraps, s_format("= %.2f traps", averageActiveTraps))
		end
		local maxWaves = skillModList:Sum("BASE", skillCfg, "MaximumWaves")
		local dpsMultiplier = 1
		if skillPart == 2 then
			dpsMultiplier = maxWaves * overlapChance
			if breakdown then
				breakdown.SkillDPSMultiplier = {}
				t_insert(breakdown.SkillDPSMultiplier, "DPS multiplier")
				t_insert(breakdown.SkillDPSMultiplier, "^8= ^7maximum waves^8 * ^7overlap chance^8")
				t_insert(breakdown.SkillDPSMultiplier, s_format("^8=^7 %d^8 *^7 %.2f^8", maxWaves, overlapChance))
				t_insert(breakdown.SkillDPSMultiplier, s_format("^8=^7 %.3f", dpsMultiplier))
			end
		elseif skillPart == 3 then
			dpsMultiplier = maxWaves
			if breakdown then
				breakdown.SkillDPSMultiplier = {}
				t_insert(breakdown.SkillDPSMultiplier, "DPS multiplier")
				t_insert(breakdown.SkillDPSMultiplier, s_format("^8=^7 %d (maximum waves)", dpsMultiplier))
			end
		elseif skillPart == 4 then
			dpsMultiplier = averageActiveTraps
			if breakdown then
				breakdown.SkillDPSMultiplier = {}
				t_insert(breakdown.SkillDPSMultiplier, "DPS multiplier")
				t_insert(breakdown.SkillDPSMultiplier, s_format("^8=^7 %.2f (average active traps)", dpsMultiplier))
			end
		elseif skillPart == 5 then
			dpsMultiplier = averageActiveTraps * maxWaves * overlapChance
			if breakdown then
				breakdown.SkillDPSMultiplier = {}
				t_insert(breakdown.SkillDPSMultiplier, "DPS multiplier")
				t_insert(breakdown.SkillDPSMultiplier, "^8= ^7average active traps^8 * ^7maximum waves^8 * ^7overlap chance^8")
				t_insert(breakdown.SkillDPSMultiplier, s_format("^8=^7 %.2f^8 *^7 %d^8 *^7 %.2f", averageActiveTraps, maxWaves, overlapChance))
				t_insert(breakdown.SkillDPSMultiplier, s_format("^8=^7 %.3f", dpsMultiplier))
			end
		elseif skillPart == 6 then
			dpsMultiplier = averageActiveTraps * maxWaves
			if breakdown then
				breakdown.SkillDPSMultiplier = {}
				t_insert(breakdown.SkillDPSMultiplier, "DPS multiplier")
				t_insert(breakdown.SkillDPSMultiplier, "^8= ^7average active traps^8 * ^7maximum waves")
				t_insert(breakdown.SkillDPSMultiplier, s_format("^8=^7 %.2f^8 *^7 %d", averageActiveTraps, maxWaves))
				t_insert(breakdown.SkillDPSMultiplier, s_format("^8=^7 %.3f", dpsMultiplier))
			end
		end
		if dpsMultiplier ~= 1 then
			skillData.dpsMultiplier = (skillData.dpsMultiplier or 1) * dpsMultiplier
			output.SkillDPSMultiplier = (output.SkillDPSMultiplier or 1) * dpsMultiplier
		end
	end,
	parts = {
		{
			name = "One wave hitting",
		},
		{
			name = "Average waves hitting configured size enemy",
		},
		{
			name = "All waves hitting",
		},
		{
			name = "Average active traps, one wave",
		},
		{
			name = "Average active traps, average waves",
		},
		{
			name = "Average active traps, all waves",
		},
	},
	statMap = {
		["base_skill_show_average_damage_instead_of_dps"] = {},
		["lightning_tower_trap_base_interval_duration_ms"] = {
			skill("repeatInterval", nil),
			div = 1000,
		},
		["lightning_tower_trap_number_of_beams"] = {
			mod("MaximumWaves", "BASE", nil),
		},
		["quality_display_lightning_tower_trap_is_gem"] = {
			-- Display Only
		},
	},
#baseMod skill("radius", 24)
#baseMod skill("radiusLabel", "Targeting Area:")
#baseMod skill("radiusSecondary", 10)
#baseMod skill("radiusSecondaryLabel", "Impact Area:")
#mods

#skill LightningSpireTrapAltX
#flags spell trap duration area
	preDamageFunc = function(activeSkill, output, breakdown)
		local skillCfg = activeSkill.skillCfg
		local skillData = activeSkill.skillData
		local skillPart = activeSkill.skillPart
		local skillModList = activeSkill.skillModList
		local t_insert = table.insert
		local s_format = string.format

		-- seemingly the only mechanical difference with seismic trap - this one does not scale it's total radius with AoE modifiers
		output.AreaOfEffectRadius = skillData.radius
		if breakdown then
			breakdown.AreaOfEffectRadius = {"Targeting area of this skill is not affected by Area of Effect modifiers."}
		end

		local baseInterval = skillData.repeatInterval
		local wavePulseRate = 1 / baseInterval
		skillData.hitTimeOverride = 1 / wavePulseRate
		local incDuration = (1 + skillModList:Sum("INC", skillCfg, "Duration") / 100)
		local moreDuration = skillModList:More(skillCfg, "Duration")
		local duration = skillData.duration * incDuration * moreDuration
		local pulses = math.floor(duration * wavePulseRate)
		output.PulsesPerTrap = pulses
		local actionSpeedMod = 1 + skillModList:Sum("INC", skillCfg, "ActionSpeed") / 100
		local baseSpeed = 1 / skillModList:Sum("BASE", skillCfg, "TrapThrowingTime")
		local throwSpeed = baseSpeed * calcLib.mod(skillModList, skillCfg, "TrapThrowingSpeed") * actionSpeedMod
		throwSpeed = math.min(throwSpeed, data.misc.ServerTickRate)
		local throwTime = 1 / throwSpeed

		local effectiveDuration = pulses / wavePulseRate
		--local cooldown = output.TrapCooldown or 1
		local averageActiveTraps = effectiveDuration / throwTime
		output.AverageActiveTraps = averageActiveTraps
		local function hitChance(enemyRadius, areaDamageRadius, areaSpreadRadius) -- not to be confused with attack hit chance
			local damagingAreaRadius = areaDamageRadius + enemyRadius - 1	-- radius where area damage can land to hit the enemy;
			-- -1 because of two assumptions: PoE coordinates are integers and damage is not registered if the two areas only share a point or vertex. If either is not correct, then -1 is not needed.
			return math.min(damagingAreaRadius * damagingAreaRadius / (areaSpreadRadius * areaSpreadRadius), 1)
		end
		local enemyRadius = skillModList:Override(skillCfg, "EnemyRadius") or skillModList:Sum("BASE", skillCfg, "EnemyRadius")
		local waveRadius = output.AreaOfEffectRadiusSecondary
		local fullRadius = output.AreaOfEffectRadius
		local overlapChance = hitChance(enemyRadius, waveRadius, fullRadius)
		output.OverlapChance = overlapChance * 100
		if breakdown then
			breakdown.OverlapChance = { }
			t_insert(breakdown.OverlapChance, "Chance for individual wave to land within range to damage enemy:")
			t_insert(breakdown.OverlapChance, "^8= (area where wave can spawn to damage enemy) / (total area)")
			t_insert(breakdown.OverlapChance, "^8= (^7secondary radius^8 + ^7enemy radius^8 - 1) ^ 2 / ^7radius^8 ^ 2")
			t_insert(breakdown.OverlapChance, s_format("^8= (^7%d^8 +^7 %d^8 - 1) ^ 2 /^7 %d^8 ^ 2", waveRadius, enemyRadius, fullRadius))
			t_insert(breakdown.OverlapChance, s_format("^8=^7 %.3f^8%%", overlapChance * 100))
			breakdown.PulsesPerTrap = { }
			t_insert(breakdown.PulsesPerTrap, "Pulses per trap:")
			t_insert(breakdown.PulsesPerTrap, s_format("%.3f ^8(unrounded skill duration)", duration))
			t_insert(breakdown.PulsesPerTrap, s_format("* %.2f ^8(pulse rate)", wavePulseRate))
			t_insert(breakdown.PulsesPerTrap, s_format("= %.2f ^8pulses", duration * wavePulseRate))
			t_insert(breakdown.PulsesPerTrap, "^8rounded down")
			t_insert(breakdown.PulsesPerTrap, s_format("= %d ^8pulses", pulses))
			t_insert(breakdown.PulsesPerTrap, s_format("^8Next breakpoint: %d%% increased Duration",
					math.ceil(100 * ((pulses + 1) / (wavePulseRate * skillData.duration * moreDuration) - incDuration))
			))
			t_insert(breakdown.PulsesPerTrap, s_format("^8Previous breakpoint: %d%% reduced Duration",
					-math.ceil(100 * (pulses / (wavePulseRate * skillData.duration * moreDuration) - incDuration) - 1)
			))
			breakdown.AverageActiveTraps = { }
			t_insert(breakdown.AverageActiveTraps, "Average active traps:")
			t_insert(breakdown.AverageActiveTraps, s_format("%.2f^8 /^7 %.2f^8 (pulses / pulse rate = effective skill duration)", pulses, wavePulseRate))
			t_insert(breakdown.AverageActiveTraps, s_format("/ %.2f ^8(throw time)", throwTime))
			t_insert(breakdown.AverageActiveTraps, s_format("= %.2f traps", averageActiveTraps))
		end
		local maxWaves = skillModList:Sum("BASE", skillCfg, "MaximumWaves")
		local dpsMultiplier = 1
		if skillPart == 2 then
			dpsMultiplier = maxWaves * overlapChance
			if breakdown then
				breakdown.SkillDPSMultiplier = {}
				t_insert(breakdown.SkillDPSMultiplier, "DPS multiplier")
				t_insert(breakdown.SkillDPSMultiplier, "^8= ^7maximum waves^8 * ^7overlap chance^8")
				t_insert(breakdown.SkillDPSMultiplier, s_format("^8=^7 %d^8 *^7 %.2f^8", maxWaves, overlapChance))
				t_insert(breakdown.SkillDPSMultiplier, s_format("^8=^7 %.3f", dpsMultiplier))
			end
		elseif skillPart == 3 then
			dpsMultiplier = maxWaves
			if breakdown then
				breakdown.SkillDPSMultiplier = {}
				t_insert(breakdown.SkillDPSMultiplier, "DPS multiplier")
				t_insert(breakdown.SkillDPSMultiplier, s_format("^8=^7 %d (maximum waves)", dpsMultiplier))
			end
		elseif skillPart == 4 then
			dpsMultiplier = averageActiveTraps
			if breakdown then
				breakdown.SkillDPSMultiplier = {}
				t_insert(breakdown.SkillDPSMultiplier, "DPS multiplier")
				t_insert(breakdown.SkillDPSMultiplier, s_format("^8=^7 %.2f (average active traps)", dpsMultiplier))
			end
		elseif skillPart == 5 then
			dpsMultiplier = averageActiveTraps * maxWaves * overlapChance
			if breakdown then
				breakdown.SkillDPSMultiplier = {}
				t_insert(breakdown.SkillDPSMultiplier, "DPS multiplier")
				t_insert(breakdown.SkillDPSMultiplier, "^8= ^7average active traps^8 * ^7maximum waves^8 * ^7overlap chance^8")
				t_insert(breakdown.SkillDPSMultiplier, s_format("^8=^7 %.2f^8 *^7 %d^8 *^7 %.2f", averageActiveTraps, maxWaves, overlapChance))
				t_insert(breakdown.SkillDPSMultiplier, s_format("^8=^7 %.3f", dpsMultiplier))
			end
		elseif skillPart == 6 then
			dpsMultiplier = averageActiveTraps * maxWaves
			if breakdown then
				breakdown.SkillDPSMultiplier = {}
				t_insert(breakdown.SkillDPSMultiplier, "DPS multiplier")
				t_insert(breakdown.SkillDPSMultiplier, "^8= ^7average active traps^8 * ^7maximum waves")
				t_insert(breakdown.SkillDPSMultiplier, s_format("^8=^7 %.2f^8 *^7 %d", averageActiveTraps, maxWaves))
				t_insert(breakdown.SkillDPSMultiplier, s_format("^8=^7 %.3f", dpsMultiplier))
			end
		end
		if dpsMultiplier ~= 1 then
			skillData.dpsMultiplier = (skillData.dpsMultiplier or 1) * dpsMultiplier
			output.SkillDPSMultiplier = (output.SkillDPSMultiplier or 1) * dpsMultiplier
		end
	end,
	parts = {
		{
			name = "One wave hitting",
		},
		{
			name = "Average waves hitting configured size enemy",
		},
		{
			name = "All waves hitting",
		},
		{
			name = "Average active traps, one wave",
		},
		{
			name = "Average active traps, average waves",
		},
		{
			name = "Average active traps, all waves",
		},
	},
	statMap = {
		["base_skill_show_average_damage_instead_of_dps"] = {},
		["lightning_tower_trap_base_interval_duration_ms"] = {
			skill("repeatInterval", nil),
			div = 1000,
		},
		["lightning_tower_trap_number_of_beams"] = {
			mod("MaximumWaves", "BASE", nil),
		},
		["quality_display_lightning_tower_trap_is_gem"] = {
			-- Display Only
		},
	},
#baseMod skill("radius", 24)
#baseMod skill("radiusLabel", "Targeting Area:")
#baseMod skill("radiusSecondary", 10)
#baseMod skill("radiusSecondaryLabel", "Impact Area:")
#mods

#skill LightningSpireTrapAltY
#flags spell trap duration area
	preDamageFunc = function(activeSkill, output, breakdown)
		local skillCfg = activeSkill.skillCfg
		local skillData = activeSkill.skillData
		local skillPart = activeSkill.skillPart
		local skillModList = activeSkill.skillModList
		local t_insert = table.insert
		local s_format = string.format

		-- seemingly the only mechanical difference with seismic trap - this one does not scale it's total radius with AoE modifiers
		output.AreaOfEffectRadius = skillData.radius
		if breakdown then
			breakdown.AreaOfEffectRadius = {"Targeting area of this skill is not affected by Area of Effect modifiers."}
		end

		local baseInterval = skillData.repeatInterval
		local wavePulseRate = 1 / baseInterval
		skillData.hitTimeOverride = 1 / wavePulseRate
		local incDuration = (1 + skillModList:Sum("INC", skillCfg, "Duration") / 100)
		local moreDuration = skillModList:More(skillCfg, "Duration")
		local duration = skillData.duration * incDuration * moreDuration
		local pulses = math.floor(duration * wavePulseRate)
		output.PulsesPerTrap = pulses
		local effectiveDuration = pulses / wavePulseRate
		local cooldown = output.TrapCooldown
		local averageActiveTraps = effectiveDuration / cooldown
		output.AverageActiveTraps = averageActiveTraps
		local function hitChance(enemyRadius, areaDamageRadius, areaSpreadRadius) -- not to be confused with attack hit chance
			local damagingAreaRadius = areaDamageRadius + enemyRadius - 1	-- radius where area damage can land to hit the enemy;
			-- -1 because of two assumptions: PoE coordinates are integers and damage is not registered if the two areas only share a point or vertex. If either is not correct, then -1 is not needed.
			return math.min(damagingAreaRadius * damagingAreaRadius / (areaSpreadRadius * areaSpreadRadius), 1)
		end
		local enemyRadius = skillModList:Override(skillCfg, "EnemyRadius") or skillModList:Sum("BASE", skillCfg, "EnemyRadius")
		local waveRadius = output.AreaOfEffectRadiusSecondary
		local fullRadius = output.AreaOfEffectRadius
		local overlapChance = hitChance(enemyRadius, waveRadius, fullRadius)
		output.OverlapChance = overlapChance * 100
		if breakdown then
			breakdown.OverlapChance = { }
			t_insert(breakdown.OverlapChance, "Chance for individual wave to land within range to damage enemy:")
			t_insert(breakdown.OverlapChance, "^8= (area where wave can spawn to damage enemy) / (total area)")
			t_insert(breakdown.OverlapChance, "^8= (^7secondary radius^8 + ^7enemy radius^8 - 1) ^ 2 / ^7radius^8 ^ 2")
			t_insert(breakdown.OverlapChance, s_format("^8= (^7%d^8 +^7 %d^8 - 1) ^ 2 /^7 %d^8 ^ 2", waveRadius, enemyRadius, fullRadius))
			t_insert(breakdown.OverlapChance, s_format("^8=^7 %.3f^8%%", overlapChance * 100))
			breakdown.PulsesPerTrap = { }
			t_insert(breakdown.PulsesPerTrap, "Pulses per trap:")
			t_insert(breakdown.PulsesPerTrap, s_format("%.3f ^8(unrounded skill duration)", duration))
			t_insert(breakdown.PulsesPerTrap, s_format("* %.2f ^8(pulse rate)", wavePulseRate))
			t_insert(breakdown.PulsesPerTrap, s_format("= %.2f ^8pulses", duration * wavePulseRate))
			t_insert(breakdown.PulsesPerTrap, "^8rounded down")
			t_insert(breakdown.PulsesPerTrap, s_format("= %d ^8pulses", pulses))
			t_insert(breakdown.PulsesPerTrap, s_format("^8Next breakpoint: %d%% increased Duration",
					math.ceil(100 * ((pulses + 1) / (wavePulseRate * skillData.duration * moreDuration) - incDuration))
			))
			t_insert(breakdown.PulsesPerTrap, s_format("^8Previous breakpoint: %d%% reduced Duration",
					-math.ceil(100 * (pulses / (wavePulseRate * skillData.duration * moreDuration) - incDuration) - 1)
			))
			breakdown.AverageActiveTraps = { }
			t_insert(breakdown.AverageActiveTraps, "Average active traps, not considering stored cooldown uses:")
			t_insert(breakdown.AverageActiveTraps, s_format("%.2f^8 /^7 %.2f^8 (pulses / pulse rate = effective skill duration)", pulses, wavePulseRate))
			t_insert(breakdown.AverageActiveTraps, s_format("/ %.2f ^8(cooldown)", cooldown))
			t_insert(breakdown.AverageActiveTraps, s_format("= %.2f traps", averageActiveTraps))
		end
		local maxWaves = skillModList:Sum("BASE", skillCfg, "MaximumWaves")
		local dpsMultiplier = 1
		if skillPart == 2 then
			dpsMultiplier = maxWaves * overlapChance
			if breakdown then
				breakdown.SkillDPSMultiplier = {}
				t_insert(breakdown.SkillDPSMultiplier, "DPS multiplier")
				t_insert(breakdown.SkillDPSMultiplier, "^8= ^7maximum waves^8 * ^7overlap chance^8")
				t_insert(breakdown.SkillDPSMultiplier, s_format("^8=^7 %d^8 *^7 %.2f^8", maxWaves, overlapChance))
				t_insert(breakdown.SkillDPSMultiplier, s_format("^8=^7 %.3f", dpsMultiplier))
			end
		elseif skillPart == 3 then
			dpsMultiplier = maxWaves
			if breakdown then
				breakdown.SkillDPSMultiplier = {}
				t_insert(breakdown.SkillDPSMultiplier, "DPS multiplier")
				t_insert(breakdown.SkillDPSMultiplier, s_format("^8=^7 %d (maximum waves)", dpsMultiplier))
			end
		elseif skillPart == 4 then
			dpsMultiplier = averageActiveTraps
			if breakdown then
				breakdown.SkillDPSMultiplier = {}
				t_insert(breakdown.SkillDPSMultiplier, "DPS multiplier")
				t_insert(breakdown.SkillDPSMultiplier, s_format("^8=^7 %.2f (average active traps)", dpsMultiplier))
			end
		elseif skillPart == 5 then
			dpsMultiplier = averageActiveTraps * maxWaves * overlapChance
			if breakdown then
				breakdown.SkillDPSMultiplier = {}
				t_insert(breakdown.SkillDPSMultiplier, "DPS multiplier")
				t_insert(breakdown.SkillDPSMultiplier, "^8= ^7average active traps^8 * ^7maximum waves^8 * ^7overlap chance^8")
				t_insert(breakdown.SkillDPSMultiplier, s_format("^8=^7 %.2f^8 *^7 %d^8 *^7 %.2f", averageActiveTraps, maxWaves, overlapChance))
				t_insert(breakdown.SkillDPSMultiplier, s_format("^8=^7 %.3f", dpsMultiplier))
			end
		elseif skillPart == 6 then
			dpsMultiplier = averageActiveTraps * maxWaves
			if breakdown then
				breakdown.SkillDPSMultiplier = {}
				t_insert(breakdown.SkillDPSMultiplier, "DPS multiplier")
				t_insert(breakdown.SkillDPSMultiplier, "^8= ^7average active traps^8 * ^7maximum waves")
				t_insert(breakdown.SkillDPSMultiplier, s_format("^8=^7 %.2f^8 *^7 %d", averageActiveTraps, maxWaves))
				t_insert(breakdown.SkillDPSMultiplier, s_format("^8=^7 %.3f", dpsMultiplier))
			end
		end
		if dpsMultiplier ~= 1 then
			skillData.dpsMultiplier = (skillData.dpsMultiplier or 1) * dpsMultiplier
			output.SkillDPSMultiplier = (output.SkillDPSMultiplier or 1) * dpsMultiplier
		end
	end,
	parts = {
		{
			name = "One wave hitting",
		},
		{
			name = "Average waves hitting configured size enemy",
		},
		{
			name = "All waves hitting",
		},
		{
			name = "Average active traps, one wave",
		},
		{
			name = "Average active traps, average waves",
		},
		{
			name = "Average active traps, all waves",
		},
	},
	statMap = {
		["base_skill_show_average_damage_instead_of_dps"] = {},
		["lightning_tower_trap_base_interval_duration_ms"] = {
			skill("repeatInterval", nil),
			div = 1000,
		},
		["lightning_tower_trap_number_of_beams"] = {
			mod("MaximumWaves", "BASE", nil),
		},
		["quality_display_lightning_tower_trap_is_gem"] = {
			-- Display Only
		},
	},
#baseMod skill("radius", 24)
#baseMod skill("radiusLabel", "Targeting Area:")
#baseMod skill("radiusSecondary", 10)
#baseMod skill("radiusSecondaryLabel", "Impact Area:")
#mods

#skill LightningConduit
#flags spell
	statMap = {
		["energy_release_damage_+%_final_per_5%_increased_damage_taken_from_shock_on_target"] = {
			mod("Damage", "MORE", nil, 0, KeywordFlag.Hit, { type = "Multiplier", var = "ShockEffect", div = 5, actor = "enemy" }),
		},
		["quality_display_lightning_conduit_is_gem"] = {
			-- Display Only
		},
	},
#baseMod skill("radiusLabel", "Targeting range:")
#mods

#skill LightningConduitAltX
#flags spell
#mods

#skill LightningTendrils
#flags spell area
	parts = {
		{
			name = "Average DPS",
		},
		{
			name = "Normal pulse",
		},
		{
			name = "Stronger pulse",
		},
	},
	statMap = {
		["lightning_tendrils_channelled_larger_pulse_damage_+%_final"] = {
			mod("Damage", "MORE", nil, 0, bit.bor(KeywordFlag.Hit, KeywordFlag.Ailment), { type = "SkillPart", skillPart = 3 }),
		},
		["lightning_tendrils_channelled_larger_pulse_always_crit"] = {
			mod("CritChance", "OVERRIDE", nil, 0, 0, { type = "SkillPart", skillPart = 3 }),
			base = 100
		},
		["lightning_tendrils_channelled_larger_pulse_interval"] = {
			flag("Every3UseCrit", { type = "SkillPart", skillPart = 1 }),
		},
	},
#baseMod mod("DPS", "MORE", -2/3 * 100, 0, 0, { type = "SkillPart", skillPart = 2 })
#baseMod mod("DPS", "MORE", -1/3 * 100, 0, 0, { type = "SkillPart", skillPart = 3 })
#mods

#skill LightningTendrilsAltX
#flags spell area channelling
	preDamageFunc = function(activeSkill, output, breakdown)
		-- DPS multiplier applied to the skills to reflect the DPS from each skill part
		local interval = activeSkill.skillData.pulseInterval
		if activeSkill.skillPart == 2 then
			activeSkill.skillModList:NewMod("DPS", "MORE", -(1 / interval) * 100, "Normal pulse", 0, 0, { type = "SkillPart", skillPart = 2 })
		elseif activeSkill.skillPart == 3 then
			activeSkill.skillModList:NewMod("DPS", "MORE", -(interval - 1)/ interval * 100, "Stronger pulse", 0, 0, { type = "SkillPart", skillPart = 3 })
		end
	end,
	postCritFunc = function(activeSkill, output, breakdown)
		-- Formula to find a effective damage multiplier to take into account the 500% more damage on every 5th hit
		if activeSkill.skillPart == 1 then
			local interval = activeSkill.skillData.pulseInterval
			local pulseDamage = activeSkill.skillData.pulseDamage / 100
			local critChance = output.PreEffectiveCritChance / 100
			local effectiveCritChance = output.CritChance / 100
			local critMulti = output.CritMultiplier
			local averageMore = 100 * (((interval - 1) * (1 + critChance * (critMulti - 1)) + (1 + pulseDamage) * critMulti) / (interval * ((1 - effectiveCritChance) + critMulti * effectiveCritChance)) - 1)
			activeSkill.skillModList:NewMod("Damage", "MORE", averageMore, "Average Pulse Damage", nil, bit.bor(KeywordFlag.Hit, KeywordFlag.Ailment), { type = "SkillPart", skillPart = 1 })
		end
	end,
	parts = {
		{
			name = "Average DPS",
		},
		{
			name = "Normal pulse",
		},
		{
			name = "Stronger pulse",
		},
	},
	statMap = {
		["lightning_tendrils_channelled_larger_pulse_damage_+%_final"] = {
			mod("Damage", "MORE", nil, 0, bit.bor(KeywordFlag.Hit, KeywordFlag.Ailment), { type = "SkillPart", skillPart = 3 }),
			skill("pulseDamage", nil),
		},
		["lightning_tendrils_channelled_larger_pulse_area_of_effect_+%_final"] = {
			mod("AreaOfEffect", "MORE", nil, 0, 0, { type = "SkillPart", skillPart = 3 }),
		},
		["lightning_tendrils_channelled_larger_pulse_always_crit"] = {
			mod("CritChance", "OVERRIDE", nil, 0, 0, { type = "SkillPart", skillPart = 3 }),
			base = 100
		},
		["lightning_tendrils_channelled_larger_pulse_interval"] = {
			flag("Every5UseCrit", { type = "SkillPart", skillPart = 1 }),
			skill("pulseInterval", nil),
		},
	},
#mods

#skill LightningTendrilsAltY
#flags spell area channelling
	statMap = {
		["lightning_tendrils_channelled_base_radius_+_per_second_while_channelling"] = {
			skill("radiusExtra", nil, { type = "Multiplier", var = "ChannellingTime", limit = 2})
		},
	},
#mods

#skill LightningTrap
#flags spell trap projectile
#mods

#skill LightningTrapAltX
#flags spell trap projectile
#mods

#skill VaalLightningTrap
#flags spell trap projectile duration
	statMap = {
		["shocked_ground_base_magnitude_override"] = {
			mod("ShockedGroundEffect", "BASE", nil)
		},
	},
#mods

#skill LightningWarp
#flags spell area duration
#baseMod skill("radius", 16)
#mods

#skill VaalLightningWarpInstant
#flags spell area duration
#baseMod skill("radius", 16)
#mods

#skill RollingMagma
#flags spell projectile area chaining
#baseMod skill("radius", 14)
#baseMod flag("CannotSplit")
#mods

#skill Malevolence
#flags spell aura area
	statMap = {
		["delirium_aura_damage_over_time_+%_final"] = {
			mod("Damage", "MORE", nil, ModFlag.Dot, 0, { type = "GlobalEffect", effectType = "Aura" }),
		},
		["delirium_skill_effect_duration_+%"] = {
			mod("Duration", "INC", nil, 0, 0, { type = "GlobalEffect", effectType = "Aura" }),
		},
	},
#baseMod skill("radius", 40)
#mods

#skill Manabond
#flags spell area arcane
	preDamageFunc = function(activeSkill, output)
		local missingUnreservedManaPercentage = activeSkill.skillData.ManabondMissingUnreservedManaPercentage or 100
		local manaGainedAsBaseLightningDamage =  math.floor((activeSkill.skillData.ManabondMissingManaGainPercent / 100) * (missingUnreservedManaPercentage / 100) * (output.ManaUnreserved or 0))
		activeSkill.skillModList:NewMod("LightningMin", "BASE", manaGainedAsBaseLightningDamage, "Manabond gain % missing unreserved mana as base lightning damage")
		activeSkill.skillModList:NewMod("LightningMax", "BASE", manaGainedAsBaseLightningDamage, "Manabond gain % missing unreserved mana as base lightning damage")
	end,
	statMap = {
		["mana_void_gain_%_missing_unreserved_mana_as_base_lightning_damage"] = {
			skill("ManabondMissingManaGainPercent", nil),
		},
		["quality_display_manabond_is_gem"] = {
			-- Display Only
		},
	},
#baseMod skill("radius", 18)
#baseMod skill("radiusLabel", "Circle area:")
#baseMod skill("radiusSecondary", 23)
#baseMod skill("radiusSecondaryLabel", "Rectangle area:")
#mods

#skill OrbOfStorms
#flags spell chaining duration
	preDamageFunc = function(activeSkill, output)
		activeSkill.skillData.hitTimeOverride = activeSkill.skillData.hitFrequency / calcLib.mod(activeSkill.skillModList, activeSkill.skillCfg, "Speed")
	end,
	statMap = {
		["orb_of_storms_base_bolt_frequency_ms"] = {
			skill("hitFrequency", nil),
			div = 1000,
		},
	},
#baseMod skill("radius", 28)
#mods

#skill PenanceBrand
#flags spell area duration brand
	preDamageFunc = function(activeSkill, output)
		activeSkill.skillData.hitTimeOverride = activeSkill.skillData.repeatFrequency * activeSkill.skillModList:Sum("BASE", activeSkill.skillCfg, "Multiplier:PenanceBrandMaxEnergy") / (1 + activeSkill.skillModList:Sum("INC", activeSkill.skillCfg, "Speed", "BrandActivationFrequency") / 100) / activeSkill.skillModList:More(activeSkill.skillCfg, "BrandActivationFrequency")
	end,
	statMap = {
		["base_skill_show_average_damage_instead_of_dps"] = {
		},
	},
#baseMod skill("radius", 28)
#baseMod mod("Multiplier:PenanceBrandMaxEnergy", "BASE", 20)
#mods

#skill PenanceBrandAltX
#flags spell area duration brand
	preDamageFunc = function(activeSkill, output)
		activeSkill.skillData.hitTimeOverride = activeSkill.skillData.repeatFrequency / (1 + activeSkill.skillModList:Sum("INC", activeSkill.skillCfg, "Speed", "BrandActivationFrequency") / 100) / activeSkill.skillModList:More(activeSkill.skillCfg, "BrandActivationFrequency")
	end,
	parts = {
		{
			name = "Energy Pulse",
			stages = true,
		},
		{
			name = "Max Pulse per Brand",
		},
	},
	statMap = {
		["magma_brand_hit_damage_+%_final_per_additional_pustule"] = {
			mod("Damage", "MORE", nil, ModFlag.Hit, 0, { type = "Multiplier", var = "PenanceBrandofDissipationStageAfterFirst" }),
		},
		["magma_brand_ailment_damage_+%_final_per_additional_pustule"] = {
			mod("Damage", "MORE", nil, 0, KeywordFlag.Ailment, { type = "Multiplier", var = "PenanceBrandofDissipationStageAfterFirst" }),
		},
		["penance_brand_pulses_instead_of_explode"] = {
			skill("radiusExtra", nil, { type = "Multiplier", var = "PenanceBrandofDissipationStageAfterFirst"}),
			value = 1,
		},
	},
#baseMod skill("radius", 28)
#baseMod mod("Multiplier:PenanceBrandofDissipationMaxStages", "BASE", 20, 0, 0, { type = "SkillPart", skillPart = 1 })
#baseMod skill("debuff", true)
#mods

#skill PenanceBrandAltY
#flags spell area duration brand
	preDamageFunc = function(activeSkill, output)
		activeSkill.skillData.hitTimeOverride = activeSkill.skillData.repeatFrequency / (1 + activeSkill.skillModList:Sum("INC", activeSkill.skillCfg, "Speed", "BrandActivationFrequency") / 100) / activeSkill.skillModList:More(activeSkill.skillCfg, "BrandActivationFrequency")
	end,
#baseMod skill("radius", 28)
#baseMod skill("showAverage", true)
#baseMod skill("debuff", true)
#mods

#skill PowerSiphon
#flags attack projectile
	statMap = {
		["critical_strike_chance_+%_final_per_power_charge_from_power_siphon"] = {
			mod("CritChance", "MORE", nil, 0, 0, { type = "Multiplier", var = "PowerCharge" }),
		},
		["power_siphon_base_fire_at_x_targets"] = {
			flag("OneShotProj")
		},
	},
#mods

#skill PowerSiphonAltX
#flags attack projectile
	statMap = {
		["power_siphon_base_fire_at_x_targets"] = {
			flag("OneShotProj")
		},
	},
#mods

#skill VaalPowerSiphon
#flags attack projectile
	statMap = {
		["critical_strike_chance_+%_final_per_power_charge_from_power_siphon"] = {
			mod("CritChance", "MORE", nil, 0, 0, { type = "Multiplier", var = "PowerCharge" }),
		},
		["power_siphon_fire_at_all_targets"] = {
			-- Display Only
		},
	},
#baseMod flag("OneShotProj")
#mods

#skill PurifyingFlame
#flags spell area duration
	parts = {
		{
			name = "Initial hit",
		},
		{
			name = "Shockwave",
		},
	},
	statMap = {
		["sanctify_wave_damage_+%_final"] = {
			mod("Damage", "MORE", nil, 0, 0, { type = "SkillPart", skillPart = 2 }),
		},
		["quality_display_sanctify_is_gem"] = {
			-- Display Only
		},
	},
#baseMod skill("radius", 18)
#baseMod skill("radiusLabel", "Initial Hit:")
#baseMod skill("radiusSecondary", 50)
#baseMod skill("radiusSecondaryLabel", "Shockwave:")
#mods

#skill PurifyingFlameAltX
#flags spell area duration
	parts = {
		{
			name = "Initial hit",
		},
		{
			name = "Shockwave",
		},
	},
	statMap = {
		["sanctify_wave_damage_+%_final"] = {
			mod("Damage", "MORE", nil, 0, 0, { type = "SkillPart", skillPart = 2 }),
		},
		["quality_display_sanctify_is_gem"] = {
			-- Display Only
		},
	},
#baseMod skill("radius", 18)
#baseMod skill("radiusLabel", "Initial Hit:")
#baseMod skill("radiusSecondary", 50)
#baseMod skill("radiusSecondaryLabel", "Shockwave:")
#mods

#skill PurityOfElements
#flags spell aura area
	statMap = {
		["base_resist_all_elements_%"] = {
			mod("ElementalResist", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Aura" }),
		},
		["immune_to_status_ailments"] = {
			flag("ElementalAilmentImmune", { type = "GlobalEffect", effectType = "Aura"}),
		},
	},
#baseMod skill("radius", 40)
#mods

#skill PurityOfLightning
#flags spell aura area
	statMap = {
		["base_lightning_damage_resistance_%"] = {
			mod("LightningResist", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Aura" }),
		},
		["base_maximum_lightning_damage_resistance_%"] = {
			mod("LightningResistMax", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Aura" }),
		},
	},
#baseMod skill("radius", 40)
#mods

#skill VaalImpurityOfLightning
#flags spell aura area duration
	statMap = {
		["hits_ignore_my_lightning_resistance"] = {
			flag("SelfIgnoreLightningResistance", { type = "GlobalEffect", effectType = "AuraDebuff" })
		},
		["base_maximum_lightning_damage_resistance_%"] = {
			mod("LightningResistMax", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Aura" }),
		},
		["base_immune_to_shock"] = {
			flag("ShockImmune", { type = "GlobalEffect", effectType = "Aura"}),
		},
	},
#mods

#skill PyroclastMine
#flags spell area projectile mine
	statMap = {
		["mortar_barrage_mine_minimum_added_fire_damage_taken"] = {
			mod("SelfFireMin", "BASE", nil, 0, 0, { type = "Limit", limitVar = "PyroclastSelfFireMinLimit" }, { type = "GlobalEffect", effectType = "AuraDebuff", effectStackVar = "ActiveMineCount" }),
		},
		["mortar_barrage_mine_maximum_added_fire_damage_taken"] = {
			mod("SelfFireMax", "BASE", nil, 0, 0, { type = "Limit", limitVar = "PyroclastSelfFireMaxLimit" }, { type = "GlobalEffect", effectType = "AuraDebuff", effectStackVar = "ActiveMineCount" }),
		},
		["mortar_barrage_mine_minimum_added_fire_damage_taken_limit"] = {
			mod("Multiplier:PyroclastSelfFireMinLimit", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "AuraDebuff", unscalable = true, effectName = "Pyroclast Mine Limit" }),
		},
		["mortar_barrage_mine_maximum_added_fire_damage_taken_limit"] = {
			mod("Multiplier:PyroclastSelfFireMaxLimit", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "AuraDebuff", unscalable = true, effectName = "Pyroclast Mine Limit" }),
		},
	},
#baseMod skill("radius", 20)
#baseMod skill("radiusLabel", "Area of initial explosion:")
#baseMod skill("radiusSecondary", 26)
#baseMod skill("radiusSecondaryLabel", "Area in which projectiles will land:")
#baseMod skill("radiusTertiary", 12)
#baseMod skill("radiusTertiaryLabel", "Area of projectile explosions:")
#mods

#skill PyroclastMineAltX
#flags spell area projectile mine
	statMap = {
		["mortar_barrage_mine_minimum_added_fire_damage_taken"] = {
			mod("SelfFireMin", "BASE", nil, 0, 0, { type = "Limit", limitVar = "PyroclastSelfFireMinLimit" }, { type = "GlobalEffect", effectType = "AuraDebuff", effectStackVar = "ActiveMineCount" }),
		},
		["mortar_barrage_mine_maximum_added_fire_damage_taken"] = {
			mod("SelfFireMax", "BASE", nil, 0, 0, { type = "Limit", limitVar = "PyroclastSelfFireMaxLimit" }, { type = "GlobalEffect", effectType = "AuraDebuff", effectStackVar = "ActiveMineCount" }),
		},
		["mortar_barrage_mine_minimum_added_fire_damage_taken_limit"] = {
			mod("Multiplier:PyroclastSelfFireMinLimit", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "AuraDebuff", unscalable = true, effectName = "Pyroclast Mine Limit" }),
		},
		["mortar_barrage_mine_maximum_added_fire_damage_taken_limit"] = {
			mod("Multiplier:PyroclastSelfFireMaxLimit", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "AuraDebuff", unscalable = true, effectName = "Pyroclast Mine Limit" }),
		},
	},
#baseMod skill("radius", 20)
#baseMod skill("radiusLabel", "Area of initial explosion:")
#baseMod skill("radiusSecondary", 26)
#baseMod skill("radiusSecondaryLabel", "Area in which projectiles will land:")
#baseMod skill("radiusTertiary", 12)
#baseMod skill("radiusTertiaryLabel", "Area of projectile explosions:")
#mods

#skill RaiseSpectre
#flags spell minion spectre duration permanentMinion
	minionList = {
	},
	statMap = {
		["accuracy_rating"] = {
			mod("MinionModifier", "LIST", { mod = mod("Accuracy", "BASE", nil) })
		},
		["raised_spectre_level"] = {
			skill("minionLevel", nil),
		},
	},
#mods

#skill RaiseSpectreAltX
#flags spell minion spectre duration
	minionList = {
	},
	statMap = {
		["accuracy_rating"] = {
			mod("MinionModifier", "LIST", { mod = mod("Accuracy", "BASE", nil) })
		},
		["raised_spectre_level"] = {
			skill("minionLevel", nil),
		},
		["spectres_have_base_duration_ms"] = {
			skill("duration", nil),
			div = 1000,
		},
	},
#mods

#skill RaiseZombie
#flags spell minion permanentMinion
	minionList = {
		"RaisedZombie",
	},
	statMap = {
		["quality_display_raise_zombie_is_gem+%"] = {
			-- Display only
		},
	},
#mods

#skill RaiseZombieAltX
#flags spell minion permanentMinion
	minionList = {
		"RaisedZombie",
	},
	statMap = {
		["zombie_slam_cooldown_speed_+%"] = {
			mod("MinionModifier", "LIST", { mod = mod("CooldownRecovery", "INC", nil, 0, 0, { type = "SkillId", skillId = "ZombieSlam" }) }),
		},
		["zombie_slam_area_of_effect_+%"] = {
			mod("MinionModifier", "LIST", { mod = mod("AreaOfEffect", "INC", nil, 0, 0, { type = "SkillId", skillId = "ZombieSlam" }) }),
		},
		["quality_display_raise_zombie_is_gem+%"] = {
			-- Display only
		},
	},
#mods

#skill RaiseZombieAltY
#flags spell minion
	minionList = {
		"RaisedZombie",
	},
	statMap = {
		["quality_display_raise_zombie_is_gem+%"] = {
			-- Display only
		},
		["minion_global_always_hit"] = {
			-- Display only
		},
		["raise_zombie_does_not_use_corpses"] = {
			-- Display only
		},
	},
#mods

#skill RighteousFire
#flags spell area
	preDamageFunc = function(activeSkill, output)
		if activeSkill.skillFlags.totem then
			activeSkill.skillData.FireDot = output.TotemLife * activeSkill.skillData.RFLifeMultiplier + output.TotemEnergyShield * activeSkill.skillData.RFESMultiplier
		else
			activeSkill.skillData.FireDot = output.Life * activeSkill.skillData.RFLifeMultiplier + output.EnergyShield * activeSkill.skillData.RFESMultiplier
		end
	end,
	statMap = {
		["righteous_fire_spell_damage_+%_final"] = {
			mod("Damage", "MORE", nil, ModFlag.Spell, 0, { type = "GlobalEffect", effectType = "Buff" }),
		},
		["base_nonlethal_fire_damage_%_of_maximum_life_taken_per_minute"] = {
			mod("FireDegen", "BASE", nil, 0, 0, { type = "PerStat", stat = "Life", div = 1}, { type = "GlobalEffect", effectType = "Buff" }),
			div = 6000,
		},
		["base_nonlethal_fire_damage_%_of_maximum_energy_shield_taken_per_minute"] = {
			mod("FireDegen", "BASE", nil, 0, 0, { type = "PerStat", stat = "EnergyShield", div = 1}, { type = "GlobalEffect", effectType = "Buff" }),
			div = 6000,
		},
		["spell_damage_+%"] = {
			mod("Damage", "INC", nil, ModFlag.Spell, 0, { type = "GlobalEffect", effectType = "Buff" }),
		},
		["base_righteous_fire_%_of_max_life_to_deal_to_nearby_per_minute"] = {
			skill("RFLifeMultiplier", nil),
			div = 6000,
		},
		["base_righteous_fire_%_of_max_energy_shield_to_deal_to_nearby_per_minute"] = {
			skill("RFESMultiplier", nil),
			div = 6000,
		},
	},
#baseMod skill("dotIsArea", true)
#mods

#skill RighteousFireAltX
#flags spell area
	preDamageFunc = function(activeSkill, output)
		activeSkill.skillData.FireDot = output.Mana * activeSkill.skillData.RFManaMultiplier
	end,
	statMap = {
		["base_nonlethal_fire_damage_%_of_maximum_mana_taken_per_minute"] = {
			mod("FireDegen", "BASE", nil, 0, 0, { type = "PerStat", stat = "Mana", div = 1}, { type = "GlobalEffect", effectType = "Buff" }),
			div = 6000,
		},
		["base_righteous_fire_%_of_max_mana_to_deal_to_nearby_per_minute"] = {
			skill("RFManaMultiplier", nil),
			div = 6000,
		},
		["righteous_fire_cast_speed_+%_final"] = {
			mod("Speed", "MORE", nil, ModFlag.Cast, 0, { type = "GlobalEffect", effectType = "Buff" }),
		},
	},
#baseMod skill("dotIsArea", true)
#mods

#skill VaalRighteousFire
#flags spell area
	preDamageFunc = function(activeSkill, output)
		if activeSkill.skillFlags.totem then
			activeSkill.skillData.FireDot = output.TotemLife * activeSkill.skillData.percentSacrificed * activeSkill.skillData.RFMultiplier
		else
			activeSkill.skillData.FireDot = (output.Life + output.EnergyShield) * activeSkill.skillData.percentSacrificed * activeSkill.skillData.RFMultiplier
		end
	end,
	statMap = {
		["vaal_righteous_fire_spell_damage_+%_final"] = {
			mod("Damage", "MORE", nil, ModFlag.Spell, 0, { type = "GlobalEffect", effectType = "Buff" }),
		},
		["vaal_righteous_fire_life_and_es_%_as_damage_per_second"] = {
			skill("RFMultiplier", nil),
			div = 100,
		},
		["vaal_righteous_fire_life_and_es_%_to_lose_on_use"] = {
			skill("percentSacrificed", nil),
			div = 100,
		}
	},
#baseMod skill("dotIsArea", true)
#mods

#skill ScorchingRay
#flags spell duration
	parts = {
		{
			name = "Manual Stages",
			stages = true,
		},
		{
			name = "Maximum Stages",
		},
	},
	statMap = {
		["base_fire_damage_resistance_%"] = {
			mod("FireExposure", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Debuff", effectName = "Fire Exposure", effectCond = "ScorchingRayMaxStages" }),
		},
		["fire_beam_additional_stack_damage_+%_final"] = {
			mod("Damage", "MORE", nil, 0, 0, { type = "Multiplier", var = "ScorchingRayStageAfterFirst" }),
			base = 100
		},
		["display_max_fire_beam_stacks"] = {
			mod("Multiplier:ScorchingRayMaxStages", "BASE", nil, 0, 0, { type = "SkillPart", skillPart = 1 }),
		},
		["quality_display_firebeam_is_gem"] = {
			-- Display only
		},
	},
#baseMod mod("Condition:ScorchingRayMaxStages", "FLAG", true, 0, 0, { type = "MultiplierThreshold", var = "ScorchingRayStageAfterFirst", threshold = 7 })
#baseMod flag("DotCanStackAsTotems")
#mods

#skill ScorchingRayAltX
#flags spell duration
	parts = {
		{
			name = "Manual Stages",
			stages = true,
		},
		{
			name = "Maximum Stages",
		},
	},
	statMap = {
		["fire_beam_additional_stack_damage_+%_final"] = {
			mod("Damage", "MORE", nil, 0, 0, { type = "Multiplier", var = "ScorchingRayofImmolationStageAfterFirst" }),
			base = 100
		},
		["display_max_fire_beam_stacks"] = {
			mod("Multiplier:ScorchingRayofImmolationMaxStages", "BASE", nil, 0, 0, { type = "SkillPart", skillPart = 1 }),
		},
		["quality_display_firebeam_is_gem"] = {
			-- Display only
		},
	},
#baseMod mod("Condition:ScorchingRayofImmolationMaxStages", "FLAG", true, 0, 0, { type = "MultiplierThreshold", var = "ScorchingRayofImmolationStageAfterFirst", threshold = 7 })
#baseMod flag("DotCanStackAsTotems")
#mods

#skill ShockNova
#flags spell area
	parts = {
		{
			name = "Ring",
		},
		{
			name = "Nova",
		},
	},
	statMap = {
		["shock_nova_ring_chance_to_shock_+%"] =  {
			mod("EnemyShockChance", "BASE", nil, 0, 0, { type = "SkillPart", skillPart = 1 }),
		},
	},
#baseMod skill("radius", 26)
#mods

#skill SigilOfPower
#flags spell area duration
	statMap = {
		["circle_of_power_min_added_lightning_per_stage"] = {
			mod("LightningMin", "BASE", nil, 0, 0, { type = "Multiplier", var = "SigilOfPowerStage", limitVar = "SigilOfPowerMaxStages" }, { type = "GlobalEffect", effectType = "Buff", effectName = "Sigil of Power" }),
			mod("LightningMin", "BASE", nil, 0, 0, { type = "Multiplier", actor = "parent", var = "SigilOfPowerStage", limitVar = "SigilOfPowerMaxStages" }, { type = "GlobalEffect", effectType = "Buff", effectName = "Sigil of Power" }),
		},
		["circle_of_power_max_added_lightning_per_stage"] = {
			mod("LightningMax", "BASE", nil, 0, 0, { type = "Multiplier", var = "SigilOfPowerStage", limitVar = "SigilOfPowerMaxStages" }, { type = "GlobalEffect", effectType = "Buff", effectName = "Sigil of Power" }),
			mod("LightningMax", "BASE", nil, 0, 0, { type = "Multiplier", actor = "parent", var = "SigilOfPowerStage", limitVar = "SigilOfPowerMaxStages" }, { type = "GlobalEffect", effectType = "Buff", effectName = "Sigil of Power" }),
		},
		["circle_of_power_enemy_damage_+%_final_at_max_stages"] = {
			mod("Damage", "MORE", nil, 0, 0, { type = "MultiplierThreshold", actor = "enemy", var = "SigilOfPowerStage", thresholdVar = "SigilOfPowerMaxStages" }, { type = "GlobalEffect", effectType = "Debuff", effectName = "Sigil of Power" }),
		},
		["circle_of_power_max_stages"] = {
			mod("Multiplier:SigilOfPowerMaxStages", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Buff", unscalable = true }),
		},
		["quality_display_circle_of_power_is_gem"] = {
			-- Display Only
		},
		["quality_display_circle_of_power_damage_is_gem"] = {
			-- Display Only
		},
	},
#baseMod skill("radius", 30)
#baseMod skill("buffAllies", true)
#mods

#skill SiphoningTrap
#flags spell trap duration
	statMap = {
		["skill_life_regeneration_per_minute_per_affected_enemy"] = {
			mod("LifeRegen", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Buff", effectCond = "SiphoningTrapSiphoning" }, { type = "Multiplier", var = "EnemyAffectedBySiphoningTrap", limit = 10 }),
			div = 60,
		},
		["skill_mana_regeneration_per_minute_per_affected_enemy"] = {
			mod("ManaRegen", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Buff", effectCond = "SiphoningTrapSiphoning" }, { type = "Multiplier", var = "EnemyAffectedBySiphoningTrap", limit = 10 }),
			div = 60,
		},
		["skill_life_regeneration_per_minute_with_at_least_1_affected_enemy"] = {
			mod("LifeRegen", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Buff", effectCond = "SiphoningTrapSiphoning" }, { type = "MultiplierThreshold", threshold = 1, var = "EnemyAffectedBySiphoningTrap" }),
			div = 60,
		},
		["skill_mana_regeneration_per_minute_with_at_least_1_affected_enemy"] = {
			mod("ManaRegen", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Buff", effectCond = "SiphoningTrapSiphoning" }, { type = "MultiplierThreshold", threshold = 1, var = "EnemyAffectedBySiphoningTrap" }),
			div = 60,
		},
	},
#mods

#skill Soulrend
#flags spell projectile duration
#baseMod skill("debuff", true)
#baseMod skill("radius", 10)
#mods

#skill SoulrendAltX
#flags spell projectile
#baseMod skill("radius", 10)
#mods

#skill SoulrendAltY
#flags spell projectile duration
#baseMod skill("debuff", true)
#baseMod skill("radius", 10)
#mods

#skill Spark
#flags spell projectile duration
	parts = {
		{
			name = "1 Hit",
		},
		{
			name = "Maximum Hits",
		},
	},
	preDamageFunc = function(activeSkill, output)
		local skillData = activeSkill.skillData
		if activeSkill.skillPart == 2 then
			skillData.dpsMultiplier = ( skillData.dpsMultiplier or 1 ) * (1 + math.floor( output.Duration / 0.66 ))
			output.SkillDPSMultiplier = skillData.dpsMultiplier
		end
	end,
#mods

#skill SparkAltX
#flags spell projectile duration
	parts = {
		{
			name = "1 Hit",
		},
		{
			name = "Maximum Hits",
		},
	},
	preDamageFunc = function(activeSkill, output) 
		local skillData = activeSkill.skillData
		if activeSkill.skillPart == 2 then
			skillData.dpsMultiplier = ( skillData.dpsMultiplier or 1 ) * (1 + math.floor( output.Duration / 0.66 ))
			output.SkillDPSMultiplier = skillData.dpsMultiplier
		end
	end,
#mods

#skill SparkAltY
#flags spell projectile duration
	parts = {
		{
			name = "1 Hit",
		},
		{
			name = "Maximum Hits",
		},
	},
	preDamageFunc = function(activeSkill, output)
		local skillData = activeSkill.skillData
		if activeSkill.skillPart == 2 then
			skillData.dpsMultiplier = ( skillData.dpsMultiplier or 1 ) * (1 + math.floor( output.Duration / 0.66 ))
			output.SkillDPSMultiplier = skillData.dpsMultiplier
		end
	end,
#mods

#skill VaalSpark
#flags spell projectile duration
#mods

#skill Spellslinger
#flags spell
	statMap = {
		["spellslinger_mana_reservation"] = {
			-- Display only
		},
	},
#mods

#skill SupportSpellslinger
	statMap = {
		["gain_%_of_base_wand_damage_as_added_spell_damage"] = {
			skill("gainPercentBaseWandDamage", nil),
		},
		["support_spellslinger_damage_+%_final"] = {
			mod("Damage", "MORE", nil, 0, bit.bor(KeywordFlag.Hit, KeywordFlag.Ailment)),
		},
		["spellslinger_trigger_on_wand_attack_%"] = {
			skill("triggeredBySpellSlinger", nil, { type = "SkillType", skillType = SkillType.Triggerable }, { type = "SkillType", skillType = SkillType.Spell }),
		},
		["quality_display_wand_damage_as_added_spell_damage_is_gem"] = {
			-- Display only
		},
	},
#baseMod flag("Condition:SupportedBySpellslinger")
#baseMod skill("showAverage", true)
#mods

#skill SoulLink
#flags spell duration
	statMap = {
		["soul_link_grants_damage_taken_+%_final"] = {
			mod("DamageTaken", "MORE", nil, 0, 0, { type = "GlobalEffect", effectType = "Link" }),
		},
		["soul_link_grants_mana_regeneration_+%"] = {
			mod("ManaRegen", "INC", nil, 0, 0, { type = "GlobalEffect", effectType = "Link" }),
		},
		["soul_link_grants_take_%_of_hit_damage_from_soul_link_source_energy_shield_before_you"] = {
			mod("TakenFromParentESBeforeYou", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Link" }),
		},
	},
#mods

#skill SpiritOffering
#flags spell duration
	statMap = {
		["spirit_offering_critical_strike_chance_+%"] = {
			mod("CritChance", "INC", nil, 0, 0, { type = "GlobalEffect", effectType = "Buff" }),
		},
		["spirit_offering_critical_strike_multiplier_+"] = {
			mod("CritMultiplier", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Buff" }),
		},
	},
#baseMod skill("buffMinions", true)
#baseMod skill("buffNotPlayer", true)
#mods

#skill StormBrand
#flags spell area duration brand chaining
	preDamageFunc = function(activeSkill, output)
		activeSkill.skillData.hitTimeOverride = activeSkill.skillData.repeatFrequency / (1 + activeSkill.skillModList:Sum("INC", activeSkill.skillCfg, "Speed", "BrandActivationFrequency") / 100) / activeSkill.skillModList:More(activeSkill.skillCfg, "BrandActivationFrequency")
	end,
	statMap = {
		["base_skill_show_average_damage_instead_of_dps"] = {
		},
	},
#baseMod skill("radius", 9)
#baseMod skill("debuff", true)
#mods

#skill StormBrandAltX
#flags spell area duration brand chaining
	preDamageFunc = function(activeSkill, output)
		activeSkill.skillData.hitTimeOverride = activeSkill.skillData.repeatFrequency / (1 + activeSkill.skillModList:Sum("INC", activeSkill.skillCfg, "Speed", "BrandActivationFrequency") / 100) / activeSkill.skillModList:More(activeSkill.skillCfg, "BrandActivationFrequency")
	end,
	statMap = {
		["base_skill_show_average_damage_instead_of_dps"] = {
		},
	},
#baseMod skill("radius", 9)
#baseMod skill("debuff", true)
#mods

#skill StormblastMine
#flags spell area mine
	statMap = {
		["lightning_explosion_mine_aura_damage_taken_+%"] = {
			mod("DamageTaken", "INC", nil, 0, 0, { type = "Limit", limit = 150 }, { type = "GlobalEffect", effectType = "AuraDebuff", effectStackVar = "ActiveMineCount" }),
		},
		["quality_display_stormblast_mine_is_gem"] = {
		-- Display only
		},
	},
#baseMod skill("radius", 20)
#mods

#skill Stormbind
#flags spell area duration
	parts = {
		{
			name = "Unimproved",
		},
		{
			name = "1 Improvement",
		},
		{
			name = "2 Improvements",
		},
		{
			name = "3 Improvements",
		},
	},
	statMap = {
		["rune_paint_damage_+%_final_per_rune_level"] = {
			mod("Damage", "MORE", nil, 0, bit.bor(KeywordFlag.Hit, KeywordFlag.Ailment), { type = "Multiplier", var = "RuneLevel" }),
		},
		["rune_paint_area_of_effect_+%_final_per_rune_level"] = {
			mod("AreaOfEffect", "MORE", nil, 0, 0, { type = "Multiplier", var = "RuneLevel" }),
		},
		["rune_paint_area_of_effect_+%_per_rune_level"] = {
			mod("AreaOfEffect", "INC", nil, 0, 0, { type = "Multiplier", var = "RuneLevel" }),
		},
		["quality_display_rune_paint_is_gem"] = {
			-- Display Only
		},
		["rune_paint_max_rune_level"] = {
			-- Display Only
		},
		["quality_display_rune_paint_area_is_gem"] = {
			-- Display Only
		},
	},
#baseMod mod("Multiplier:RuneLevel", "BASE", 1, 0, 0, { type = "SkillPart", skillPart = 2 })
#baseMod mod("Multiplier:RuneLevel", "BASE", 2, 0, 0, { type = "SkillPart", skillPart = 3 })
#baseMod mod("Multiplier:RuneLevel", "BASE", 3, 0, 0, { type = "SkillPart", skillPart = 4 })
#baseMod skill("radius", 16)
#mods

#skill RuneBlast
#flags spell
#mods

#skill StormbindAltX
#flags spell area duration
	parts = {
		{
			name = "Unimproved",
		},
		{
			name = "1 Improvement",
		},
		{
			name = "2 Improvements",
		},
		{
			name = "3 Improvements",
		},
	},
	statMap = {
		["rune_paint_damage_+%_final_per_rune_level"] = {
			mod("Damage", "MORE", nil, 0, bit.bor(KeywordFlag.Hit, KeywordFlag.Ailment), { type = "Multiplier", var = "RuneLevel" }),
		},
		["rune_paint_area_of_effect_+%_final_per_rune_level"] = {
			mod("AreaOfEffect", "MORE", nil, 0, 0, { type = "Multiplier", var = "RuneLevel" }),
		},
		["rune_paint_area_of_effect_+%_per_rune_level"] = {
			mod("AreaOfEffect", "INC", nil, 0, 0, { type = "Multiplier", var = "RuneLevel" }),
		},
		["quality_display_rune_paint_is_gem"] = {
			-- Display Only
		},
		["rune_paint_max_rune_level"] = {
			-- Display Only
		},
		["quality_display_rune_paint_area_is_gem"] = {
			-- Display Only
		},
	},
#baseMod mod("Multiplier:RuneLevel", "BASE", 1, 0, 0, { type = "SkillPart", skillPart = 2 })
#baseMod mod("Multiplier:RuneLevel", "BASE", 2, 0, 0, { type = "SkillPart", skillPart = 3 })
#baseMod mod("Multiplier:RuneLevel", "BASE", 3, 0, 0, { type = "SkillPart", skillPart = 4 })
#baseMod skill("radius", 16)
#mods

#skill RuneBlastAltX
#flags spell
#mods

#skill StormBurst
#flags spell area duration
	parts = {
		{
			name = "1 Orb Tick"
		},
		{
			name = "Max Channelled Orbs"
		}
	},
	preDamageFunc = function(activeSkill, output)
		if activeSkill.skillPart == 2 then
			local duration = activeSkill.skillData.duration * output.DurationMod
			-- duration * 10 / (jump * 10), instead of duration / jump to avoid floating point issues
			local jumpPeriod = activeSkill.skillData.repeatFrequency * 10
			-- additional 1 tick upon spawn of orb
			activeSkill.skillData.dpsMultiplier = (activeSkill.skillData.dpsMultiplier or 1) * (1 + math.floor(duration * 10 / jumpPeriod))
		end
	end,
	statMap = {
		["display_storm_burst_jump_time_ms"] = {
			skill("repeatFrequency", nil),
			div = 1000,
		},
		["storm_burst_new_damage_+%_final_per_remaining_teleport_zap"] = {
		},
	},
#baseMod skill("radius", 16)
#baseMod skill("radiusSecondary", 22)
#mods

#skill StormCall
#flags spell area duration
	statMap = {
		["base_skill_show_average_damage_instead_of_dps"] = {
		},
	},
#baseMod skill("radius", 20)
#mods

#skill StormCallAltX
#flags spell area duration
	statMap = {
		["base_skill_show_average_damage_instead_of_dps"] = {
		},
	},
#baseMod skill("radius", 20)
#mods

#skill VaalStormCall
#flags spell area duration
#baseMod skill("radius", 16)
#baseMod skill("radiusLabel", "Initial Lightning tether area:")
#baseMod skill("radiusSecondary", 40)
#baseMod skill("radiusSecondaryLabel", "Final Lightning Strike area:")
#mods

#skill SummonCarrionGolem
#flags spell minion golem permanentMinion
	minionList = {
		"SummonedCarrionGolem",
	},
	statMap = {
		["bone_golem_grants_minion_minimum_added_physical_damage"] = {
			mod("MinionModifier", "LIST", { mod = mod("PhysicalMin", "BASE", nil) }, 0, 0, { type = "SkillType", skillType = SkillType.Golem, neg = true }, { type = "GlobalEffect", effectType = "Buff" }),
		},
		["bone_golem_grants_minion_maximum_added_physical_damage"] = {
			mod("MinionModifier", "LIST", { mod = mod("PhysicalMax", "BASE", nil) }, 0, 0, { type = "SkillType", skillType = SkillType.Golem, neg = true }, { type = "GlobalEffect", effectType = "Buff" }),
		},
		["bone_golem_damage_+%_final_per_non_golem_minion_nearby"] = {
			mod("MinionModifier", "LIST", { type = "SummonedCarrionGolem", mod = mod("Damage", "MORE", nil, 0, 0, { type = "Multiplier", actor = "parent", var = "NearbyNonGolemMinion", limitVar = "MaxNearbyGolemDamage", limitTotal = true }) }),
		},
		["bone_golem_damage_per_non_golem_minion_nearby_maximum_%"] = {
			mod("MinionModifier", "LIST", { mod = mod("Multiplier:MaxNearbyGolemDamage", "BASE", nil) }),
		},
	},
#baseMod skill("allowTotemBuff", true)
#baseMod flag("Condition:HaveCarrionGolem", { type = "GlobalEffect", effectType = "Buff" })
#mods

#skill SummonCarrionGolemAltX
#flags spell minion golem permanentMinion
	minionList = {
		"SummonedCarrionGolem",
	},
#baseMod skill("allowTotemBuff", true)
#baseMod flag("Condition:HaveCarrionGolem", { type = "GlobalEffect", effectType = "Buff" })
#mods

#skill SummonCarrionGolemAltY
#flags spell minion golem permanentMinion
	minionList = {
		"SummonedCarrionGolem",
	},
	statMap = {
		["bone_golem_damage_+%_final_per_non_golem_minion_nearby"] = {
			mod("MinionModifier", "LIST", { type = "SummonedCarrionGolem", mod = mod("Damage", "MORE", nil, 0, 0, { type = "Multiplier", actor = "parent", var = "NearbyNonGolemMinion", limitVar = "MaxNearbyGolemDamage", limitTotal = true }) }),
		},
		["bone_golem_damage_per_non_golem_minion_nearby_maximum_%"] = {
			mod("MinionModifier", "LIST", { mod = mod("Multiplier:MaxNearbyGolemDamage", "BASE", nil) }),
		},
	},
#baseMod skill("allowTotemBuff", true)
#baseMod flag("Condition:HaveCarrionGolem", { type = "GlobalEffect", effectType = "Buff" })
#mods

#skill SummonChaosGolem
#flags spell minion golem permanentMinion
	minionList = {
		"SummonedChaosGolem",
	},
	statMap = {
		["chaos_golem_grants_dot_multiplier_+"] = {
			mod("DotMultiplier", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Buff" }),
		},
		["chaos_golem_grants_chaos_resistance_%"] = {
			mod("ChaosResist", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Buff" }),
		},
	},
#baseMod skill("allowTotemBuff", true)
#baseMod flag("Condition:HaveChaosGolem", { type = "GlobalEffect", effectType = "Buff" })
#mods

#skill SummonChaosGolemAltX
#flags spell minion golem permanentMinion
	minionList = {
		"SummonedChaosGolem",
	},
#baseMod skill("allowTotemBuff", true)
#baseMod flag("Condition:HaveChaosGolem", { type = "GlobalEffect", effectType = "Buff" })
#mods

#skill SummonChaosGolemAltY
#flags spell minion golem permanentMinion
	minionList = {
		"SummonedChaosGolem",
	},
	statMap = {
		-- Minions applying Wither from a skill does not work yet
		--["minion_withered_effect_+%"] = {
			--mod("MinionWitherEffect", "INC", nil, 0, 0, { type = "GlobalEffect", effectType = "Buff" }),
		--},
	},
#baseMod skill("allowTotemBuff", true)
#baseMod flag("Condition:HaveChaosGolem", { type = "GlobalEffect", effectType = "Buff" })
#mods

#skill SummonHolyRelic
#flags spell minion permanentMinion
	minionList = {
		"HolyLivingRelic",
	},
	statMap = {
		["base_number_of_relics_allowed"] = {
			mod("ActiveHolyRelicLimit", "BASE", nil)
		},
		["holy_relic_nova_life_regeneration_rate_per_minute"] = {
			mod("LifeRegen", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Buff", effectName = "Holy Relic's Boon", effectCond = "HolyRelicBoonActive" }),
			div = 60,
		},
		["holy_relic_nova_minion_life_regeneration_rate_per_second"] = {
			mod("LifeRegen", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Buff", effectName = "Holy Relic's Minion Boon", effectCond = "HolyRelicBoonActive", applyNotPlayer = true, applyMinions = true })
		},
	},
#mods

#skill SummonHolyRelicAltX
#flags spell minion permanentMinion
	minionList = {
		"HolyLivingRelic",
	},
	statMap = {
		["base_number_of_relics_allowed"] = {
			mod("ActiveHolyRelicLimit", "BASE", nil)
		},
	},
#mods

#skill SummonLightningGolem
#flags spell minion golem permanentMinion
	minionList = {
		"SummonedLightningGolem",
	},
	statMap = {
		["lightning_golem_grants_attack_and_cast_speed_+%"] = {
			mod("Speed", "INC", nil, 0, 0, { type = "GlobalEffect", effectType = "Buff" }),
		},
		["lightning_golem_grants_base_mana_regeneration_rate_per_minute"] = {
			mod("ManaRegen", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Buff" }),
			div = 60,
		},
	},
#baseMod skill("allowTotemBuff", true)
#baseMod flag("Condition:HaveLightningGolem", { type = "GlobalEffect", effectType = "Buff" })
#mods

#skill SummonLightningGolemAltX
#flags spell minion golem permanentMinion
	minionList = {
		"SummonedLightningGolem",
	},
#baseMod skill("allowTotemBuff", true)
#baseMod flag("Condition:HaveLightningGolem", { type = "GlobalEffect", effectType = "Buff" })
#mods

#skill SummonRagingSpirit
#flags spell minion duration
	minionList = {
		"SummonedRagingSpirit",
	},
#mods

#skill SummonRagingSpiritAltX
#flags spell minion duration
	minionList = {
		"SummonedRagingSpirit",
	},
#mods

#skill SummonReaper
#flags spell minion
	minionList = {
		"SummonedReaper",
	},
	statMap = {
		["bleed_on_hit_with_attacks_%"] = {
			mod("MinionModifier", "LIST", { mod = mod("BleedChance", "BASE", nil, ModFlag.Attack) })
		},
		["active_skill_non_reaper_minion_damage_+%_final"] = {
			mod("MinionModifier", "LIST", { mod = mod("Damage", "MORE", nil) }, 0, 0, { type = "SkillName", skillName = "Summon Reaper", includeTransfigured = true, neg = true }, { type = "GlobalEffect", effectType = "Buff", unscalable = true }),
		},
		["active_skill_non_reaper_minion_maximum_life_+%_final"] = {
			mod("MinionModifier", "LIST", { mod = mod("Life", "MORE", nil) }, 0, 0, { type = "SkillName", skillName = "Summon Reaper", includeTransfigured = true, neg = true }, { type = "GlobalEffect", effectType = "Buff", unscalable = true }),
		},
	},
#mods

#skill SummonReaperAltX
#flags spell minion
	minionList = {
		"SummonedReaper",
	},
	statMap = {
		["active_skill_non_reaper_minion_damage_+%_final"] = {
			mod("MinionModifier", "LIST", { mod = mod("Damage", "MORE", nil) }, 0, 0, { type = "SkillName", skillName = "Summon Reaper", includeTransfigured = true, neg = true }, { type = "GlobalEffect", effectType = "Buff", unscalable = true }),
		},
		["active_skill_non_reaper_minion_maximum_life_+%_final"] = {
			mod("MinionModifier", "LIST", { mod = mod("Life", "MORE", nil) }, 0, 0, { type = "SkillName", skillName = "Summon Reaper", includeTransfigured = true, neg = true }, { type = "GlobalEffect", effectType = "Buff", unscalable = true }),
		},
	},
#mods

#skill SummonReaperAltY
#flags spell minion
	minionList = {
		"SummonedReaper",
	},
	statMap = {
		["bleed_on_hit_with_attacks_%"] = {
			mod("MinionModifier", "LIST", { mod = mod("BleedChance", "BASE", nil, ModFlag.Attack) })
		},
		["active_skill_non_reaper_minion_damage_+%_final"] = {
			mod("MinionModifier", "LIST", { mod = mod("Damage", "MORE", nil) }, 0, 0, { type = "SkillName", skillName = "Summon Reaper", includeTransfigured = true, neg = true }, { type = "GlobalEffect", effectType = "Buff", unscalable = true }),
		},
		["active_skill_non_reaper_minion_maximum_life_+%_final"] = {
			mod("MinionModifier", "LIST", { mod = mod("Life", "MORE", nil) }, 0, 0, { type = "SkillName", skillName = "Summon Reaper", includeTransfigured = true, neg = true }, { type = "GlobalEffect", effectType = "Buff", unscalable = true }),
		},
	},
#mods

#skill SummonSkeletons
#flags spell minion duration
	minionList = {
		"RaisedSkeleton",
		"RaisedSkeletonCaster",
	},
	statMap = {
		["quality_display_summon_skeleton_is_gem"] = {
			-- Display only
		},
	},
#mods

#skill SummonSkeletonsAltX
#flags spell minion duration
	minionList = {
		"RaisedSkeletonArcher",
	},
	statMap = {
		["quality_display_summon_skeleton_is_gem"] = {
			-- Display only
		},
	},
#mods

#skill SummonSkeletonsAltY
#flags spell minion duration
	minionList = {
		"RaisedSkeletonCaster",
	},
	statMap = {
		["quality_display_summon_skeleton_is_gem"] = {
			-- Display only
		},
	},
#mods

#skill VaalSummonSkeletons
#flags spell minion duration
	minionList = {
		"RaisedSkeletonMeleeVaal",
		"RaisedSkeletonCaster",
		"RaisedSkeletonArcherVaal",
	},
	statMap = {
		["quality_display_summon_skeleton_is_gem"] = {
			-- Display only
		},
	},
#mods

#skill SummonSkitterbots
#flags spell minion permanentMinion
	minionList = {
		"SkitterbotCold",
		"SkitterbotLightning",
		"SkitterbotFire",
	},
	statMap = {
		["skitterbots_trap_mine_damage_+%_final"] = {
			mod("Damage", "MORE", nil, 0, bit.bor(KeywordFlag.Mine, KeywordFlag.Trap), { type = "GlobalEffect", effectType = "Buff" }),
		},
	},
#baseMod skill("radius", 30)
#mods

#skill TempestShield
#flags spell duration chaining
	statMap = {
		["shield_spell_block_%"] = {
			mod("SpellBlockChance", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Buff" }),
		},
		["skill_display_buff_grants_shock_immunity"] = {
			flag("ShockImmune", { type = "GlobalEffect", effectType = "Buff"}),
		},
	},
#baseMod skill("chanceToTriggerCounterattackOnBlock", 100, { type = "SkillType", skillType = SkillType.Spell })
#mods

#skill VoidSphere
#flags spell area duration
	preDamageFunc = function(activeSkill, output)
		activeSkill.skillData.hitTimeOverride = activeSkill.skillData.repeatFrequency / (1 + activeSkill.skillModList:Sum("INC", activeSkill.skillCfg, "VoidSphereFrequency") / 100)
	end,
	statMap = {
		["base_blackhole_tick_rate_ms"] = {
			skill("repeatFrequency", nil),
			div = 1000,
		},
	},
#mods

#skill VoidSphereAltX
#flags spell area duration
	preDamageFunc = function(activeSkill, output)
		activeSkill.skillData.hitTimeOverride = activeSkill.skillData.repeatFrequency / (1 + activeSkill.skillModList:Sum("INC", activeSkill.skillCfg, "VoidSphereFrequency") / 100)
	end,
	statMap = {
		["base_blackhole_tick_rate_ms"] = {
			skill("repeatFrequency", nil),
			div = 1000,
		},
	},
#mods

#skill VoltaxicBurst
#flags spell area nova duration
	preDamageFunc = function(activeSkill, output)
		local duration = math.floor(activeSkill.skillData.duration * output.DurationMod * 10)
		activeSkill.skillModList:NewMod("Damage", "INC", activeSkill.skillModList:Sum("INC", activeSkill.skillCfg, "VoltaxicDurationIncDamage") * duration, "Skill:VoltaxicBurst")
	end,
	statMap = {
		["voltaxic_burst_hit_and_ailment_damage_+%_final_per_stack"] = {
			mod("Damage", "MORE", nil, 0, bit.bor(KeywordFlag.Hit, KeywordFlag.Ailment), { type = "Multiplier", var = "VoltaxicWaitingStages" }),
		},
		["quality_display_voltaxic_burst_is_gem"] = {
			-- Display only
		},
	},
#baseMod skill("radius", 22)
#mods

#skill Vortex
#flags spell area duration forceInstant
	preDamageFunc = function(activeSkill, output)
		activeSkill.skillData.hitTimeOverride = output.Cooldown
	end,
#baseMod skill("dotIsArea", true)
#baseMod skill("radiusLabel", "Initial Hit:")
#baseMod skill("radiusSecondaryLabel", "Ground Degen:")
#mods

#skill VortexAltX
#flags spell area duration forceInstant
	preDamageFunc = function(activeSkill, output)
		activeSkill.skillData.hitTimeOverride = output.Cooldown
	end,
	statMap = {
		["active_skill_if_used_through_frostbolt_damage_+%_final"] = {
			mod("Damage", "MORE", nil, 0, 0, { type = "Condition", var = "CastOnFrostbolt" }),
		},
	},
#baseMod skill("dotIsArea", true)
#baseMod skill("radiusLabel", "Initial Hit:")
#baseMod skill("radiusSecondaryLabel", "Ground Degen:")
#mods

#skill WaveOfConviction
#flags spell area duration
	preDamageFunc = function(activeSkill, output)
		if activeSkill.skillData.duration then
			local duration = math.floor(math.ceil(activeSkill.skillData.duration * data.misc.ServerTickRate) / data.misc.ServerTickRate * output.DurationMod * 10)
			activeSkill.skillModList:NewMod("DotMultiplier", "BASE", activeSkill.skillModList:Sum("INC", activeSkill.skillCfg, "WaveOfConvictionDurationDotMulti") * duration / 100, "Skill:WaveOfConviction", 0, { type = "Multiplier", var = "WoCDurationExpired"})
		end
	end,
	statMap = {
		["purge_expose_resist_%_matching_highest_element_damage"] = {
			mod("FireExposure", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Debuff", effectName = "Fire Exposure", effectCond = "WaveOfConvictionFireExposureActive" }),
			mod("ColdExposure", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Debuff", effectName = "Cold Exposure", effectCond = "WaveOfConvictionColdExposureActive" }),
			mod("LightningExposure", "BASE", nil, 0, 0, { type = "GlobalEffect", effectType = "Debuff", effectName = "Lightning Exposure", effectCond = "WaveOfConvictionLightningExposureActive" }),
		},
	},
#mods

#skill WaveOfConvictionAltY
#flags spell area duration
	preDamageFunc = function(activeSkill, output)
		if activeSkill.skillData.duration then
			local duration = math.floor(math.ceil(activeSkill.skillData.duration * data.misc.ServerTickRate) / data.misc.ServerTickRate * output.DurationMod * 10)
			activeSkill.skillModList:NewMod("DotMultiplier", "BASE", activeSkill.skillModList:Sum("INC", activeSkill.skillCfg, "WaveOfConvictionDurationDotMulti") * duration / 100, "Skill:WaveOfConvictionAltY", 0, { type = "Multiplier", var = "WoCDurationExpired"})
		end
	end,
#mods

#skill WinterOrb
#flags spell projectile area duration
	parts = {
		{
			name = "Channelling",
			stages = true,
		},
		{
			name = "Idle",
			stages = true,
		},
	},
	preDamageFunc = function(activeSkill, output)
		local rateMod = (1 + activeSkill.skillModList:Sum("INC", activeSkill.skillCfg, "HitRate", "Speed") / 100)
		local mult = activeSkill.skillModList:More(activeSkill.skillCfg, "HitRate")
		activeSkill.skillData.hitTimeOverride = activeSkill.skillData.repeatFrequency / rateMod / mult
	end,
	statMap = {
		["base_skill_show_average_damage_instead_of_dps"] = {
		},
		["frost_fury_fire_speed_+%_per_stage"] = {
			mod("HitRate", "INC", nil, 0, 0, { type = "Multiplier", var = "WinterOrbStageAfterFirst" }),
		},
		["frost_fury_max_number_of_stages"] = {
			mod("Multiplier:WinterOrbMaxStages", "BASE", nil),
		},
		["frost_fury_base_fire_interval_ms"] = {
			skill("repeatFrequency", nil),
			div = 1000,
		},
		["frost_fury_duration_+%_per_stage"] = {
			mod("Duration", "INC", nil, 0, 0, { type = "Multiplier", var = "WinterOrbStageAfterFirst" }),
		},
		["frost_fury_fire_speed_+%_final_while_channelling"] = {
			mod("HitRate", "MORE", nil, 0, 0, { type = "SkillPart", skillPart = 1 }),
		},
		["display_frost_fury_additive_cast_speed_modifiers_apply_to_fire_speed"] = {
			-- Display only
		},
		["quality_display_winter_orb_is_gem"] = {
			-- Display only
		},
	},
#baseMod skill("radius", 16)
#mods

#skill WintertideBrand
#flags spell area duration brand
	preDamageFunc = function(activeSkill, output)
		activeSkill.skillData.hitTimeOverride = activeSkill.skillData.repeatFrequency / (1 + activeSkill.skillModList:Sum("INC", activeSkill.skillCfg, "Speed", "BrandActivationFrequency") / 100) / activeSkill.skillModList:More(activeSkill.skillCfg, "BrandActivationFrequency")
		if activeSkill.skillPart == 2 then
			local skillMaxStages = activeSkill.skillModList:Sum("BASE", activeSkill.skillCfg, "Multiplier:WintertideBrandMaxStages")
			local debuffDurationMult = 1 / math.max(data.misc.BuffExpirationSlowCap, calcLib.mod(activeSkill.actor.enemy.modDB, activeSkill.skillCfg, "BuffExpireFaster"))
			local duration = calcSkillDuration(activeSkill.skillModList, activeSkill.skillCfg, activeSkill.skillData, {}) * debuffDurationMult
			local maxStages = math.min(duration / activeSkill.skillData.hitTimeOverride + 1, skillMaxStages)
			local timeToReachMaxStages = (maxStages - 1) * activeSkill.skillData.hitTimeOverride
			local timeAtMaxStages = duration - timeToReachMaxStages
			local damagePerStage = activeSkill.skillModList:Sum("BASE", activeSkill.skillCfg, "Multiplier:WintertideBrandDamagePerStage")
			-- Get the average damage before reaching max stages and then damage at max stages
			local dpsMultiplier = ((2 + damagePerStage + maxStages * damagePerStage) / 2 * timeToReachMaxStages + timeAtMaxStages * (1 + maxStages * damagePerStage)) / duration
			activeSkill.skillModList:NewMod("Damage", "MORE", dpsMultiplier, "Wintertide Brand Average Multiplier")
		end
	end,
	parts = {
		{
			name = "Manual Stages",
			stages = true
		},
		{
			name = "Average Damage",
		}
	},
	statMap = {
		["base_skill_show_average_damage_instead_of_dps"] = {
		},
		["immolation_brand_burn_damage_+%_final_per_stage"] = {
			-- Only apply to Manual Stages part
			mod("Damage", "MORE", nil, 0, 0, { type = "Multiplier", var = "WintertideBrandStage", limitVar = "WintertideBrandMaxStages" }, { type = "SkillPart", skillPart = 1 }),
			mod("Multiplier:WintertideBrandDamagePerStage", "BASE", nil),
		},
		["winter_brand_max_number_of_stages"] = {
			mod("Multiplier:WintertideBrandMaxStages", "BASE", nil),
		},
		["quality_display_wintertide_brand_is_gem"] = {
			-- Display only
		},
	},
#baseMod skill("radius", 20)
#baseMod skill("debuff", true)
#baseMod skill("debuffTertiary", true)
#mods

#skill Wither
#flags spell area duration
	statMap = {
		["base_skill_effect_duration"] = {
		},
		["active_skill_withered_base_duration_ms"] = {
			skill("duration", nil),
			div = 1000,
		},
		["chaos_damage_taken_+%"] = {
			flag("Condition:CanWither"),
		},
		["base_movement_velocity_+%"] = {
			mod("MovementSpeed", "INC", nil, 0, 0, { type = "GlobalEffect", effectType = "Debuff", effectName = "Withered" }),
		},
	},
#baseMod skill("debuff", true)
#baseMod skill("radius", 18)
#mods

#skill Wrath
#flags spell aura area
	statMap = {
		["wrath_aura_spell_lightning_damage_+%_final"] = {
			mod("LightningDamage", "MORE", nil, ModFlag.Spell, 0, { type = "GlobalEffect", effectType = "Aura" }),
		},
		["attack_minimum_added_lightning_damage"] = {
			mod("LightningMin", "BASE", nil, 0, KeywordFlag.Attack, { type = "GlobalEffect", effectType = "Aura" }),
		},
		["attack_maximum_added_lightning_damage"] = {
			mod("LightningMax", "BASE", nil, 0, KeywordFlag.Attack, { type = "GlobalEffect", effectType = "Aura" }),
		},
	},
#baseMod skill("radius", 40)
#mods

#skill Zealotry
#flags spell aura area
	statMap = {
		["spell_damage_aura_spell_damage_+%_final"] = {
			mod("Damage", "MORE", nil, ModFlag.Spell, 0, { type = "GlobalEffect", effectType = "Aura" }),
		},
		["spell_critical_strike_chance_+%"] = {
			mod("CritChance", "INC", nil, ModFlag.Spell, 0, { type = "GlobalEffect", effectType = "Aura" }),
		},
	},
#baseMod skill("radius", 40)
#mods
