Files
Dota-Zombie-Invasion/scripts/vscripts/abilities/creep/toxin.lua
T
2026-05-29 15:11:31 +07:00

845 lines
28 KiB
Lua

local ____lualib = require("lualib_bundle")
local __TS__NumberIsFinite = ____lualib.__TS__NumberIsFinite
local __TS__ArraySort = ____lualib.__TS__ArraySort
local Map = ____lualib.Map
local __TS__New = ____lualib.__TS__New
local __TS__ArrayFilter = ____lualib.__TS__ArrayFilter
local __TS__Class = ____lualib.__TS__Class
local __TS__ClassExtends = ____lualib.__TS__ClassExtends
local __TS__Decorate = ____lualib.__TS__Decorate
local ____exports = {}
local clampToxinPoolRadius, toxinIsValidUnit, isToxinPoolThinker, toxinIsValidAbility, toxinPoolsOverlap, getToxinPoolRadiusForThinker, getToxinPoolMergeStackForThinker, getToxinDamagePerTickForThinker, getToxinPoolRemainingDuration, toxinDistSqHoriz, buildToxinOverlapCluster, computeMergedPoolStats, toxinSortDedupeEntIndices, toxinEnqueuePoolMergeForThinker, toxinDrainToxinMergeQueueFrame, toxinTryResolvePoolMergeForThinkerIndex, TOXIN_POOL_MODIFIER_NAME, TOXIN_MERGE_SCAN_RADIUS, TOXIN_MERGE_OVERLAP_EPSILON, TOXIN_THINKER_CLASS, TOXIN_POOL_MAX_RADIUS, TOXIN_MAX_MERGE_SCAN_THINKERS, toxinMergeQueue, toxinMergeDrainScheduled, toxinIsDrainingMerge
local ____difficulty_manager = require("difficulty_manager")
local Difficulty = ____difficulty_manager.Difficulty
local ____dota_ts_adapter = require("lib.dota_ts_adapter")
local BaseAbility = ____dota_ts_adapter.BaseAbility
local BaseModifier = ____dota_ts_adapter.BaseModifier
local registerAbility = ____dota_ts_adapter.registerAbility
local registerModifier = ____dota_ts_adapter.registerModifier
local ____creep_render_color = require("utils.creep_render_color")
local trySetIntrinsicCreepRenderColor = ____creep_render_color.trySetIntrinsicCreepRenderColor
local ____entity_radius = require("utils.entity_radius")
local findAllByClassnameInRadius = ____entity_radius.findAllByClassnameInRadius
function clampToxinPoolRadius(self, r)
return math.max(
1,
math.min(r, TOXIN_POOL_MAX_RADIUS)
)
end
function toxinIsValidUnit(self, unit)
return unit ~= nil and unit ~= nil and not unit:IsNull() and IsValidEntity(unit)
end
function isToxinPoolThinker(self, unit)
return toxinIsValidUnit(nil, unit) and unit:GetClassname() == TOXIN_THINKER_CLASS
end
function toxinIsValidAbility(self, ab)
return ab ~= nil and ab ~= nil and not ab:IsNull() and IsValidEntity(ab)
end
function toxinPoolsOverlap(self, centerA, radiusA, centerB, radiusB)
local dx = centerA.x - centerB.x
local dy = centerA.y - centerB.y
local dist = math.sqrt(dx * dx + dy * dy)
return dist <= radiusA + radiusB + TOXIN_MERGE_OVERLAP_EPSILON
end
function getToxinPoolRadiusForThinker(self, thinker, poolMod, ability)
if not toxinIsValidAbility(nil, ability) then
return 0
end
local raw
if poolMod and poolMod.effectiveRadius > 0 then
raw = poolMod.effectiveRadius
else
raw = ability:GetSpecialValueFor("radius")
end
return clampToxinPoolRadius(nil, raw)
end
function getToxinPoolMergeStackForThinker(self, poolMod, _ability)
if poolMod ~= nil and poolMod.poolMergeStackCount > 0 then
return math.floor(poolMod.poolMergeStackCount)
end
return 1
end
function getToxinDamagePerTickForThinker(self, poolMod, ability)
if not toxinIsValidAbility(nil, ability) then
return 0
end
if poolMod and poolMod.damagePerTick > 0 then
return poolMod.damagePerTick
end
return ability:GetSpecialValueFor("damage") * 0.33
end
function getToxinPoolRemainingDuration(self, poolMod, fallbackDuration)
local getter = poolMod.GetRemainingTime
if getter ~= nil then
local v = getter(poolMod)
if type(v) == "number" and __TS__NumberIsFinite(v) then
return math.max(0, v)
end
end
return math.max(0, fallbackDuration)
end
function toxinDistSqHoriz(self, ax, bx)
local dx = ax.x - bx.x
local dy = ax.y - bx.y
return dx * dx + dy * dy
end
function buildToxinOverlapCluster(self, seed, ability)
if not toxinIsValidUnit(nil, seed) or not toxinIsValidAbility(nil, ability) then
return {}
end
local seedTeam = seed:GetTeamNumber()
local origin = seed:GetAbsOrigin()
local raw = findAllByClassnameInRadius("npc_dota_thinker", origin, TOXIN_MERGE_SCAN_RADIUS)
local toxinThinkers = {}
for ____, ent in ipairs(raw) do
do
local npc = ent
if not toxinIsValidUnit(nil, npc) then
goto __continue22
end
if npc:GetTeamNumber() ~= seedTeam then
goto __continue22
end
if not npc:FindModifierByName(TOXIN_POOL_MODIFIER_NAME) then
goto __continue22
end
toxinThinkers[#toxinThinkers + 1] = npc
end
::__continue22::
end
if #toxinThinkers > TOXIN_MAX_MERGE_SCAN_THINKERS then
__TS__ArraySort(
toxinThinkers,
function(____, a, b) return toxinDistSqHoriz(
nil,
a:GetAbsOrigin(),
origin
) - toxinDistSqHoriz(
nil,
b:GetAbsOrigin(),
origin
) end
)
while #toxinThinkers > TOXIN_MAX_MERGE_SCAN_THINKERS do
table.remove(toxinThinkers)
end
end
local cluster = {}
local visited = __TS__New(Map)
local queue = {seed}
while #queue > 0 do
do
local cur = table.remove(queue)
if not toxinIsValidUnit(nil, cur) then
goto __continue30
end
local idx = cur:GetEntityIndex()
if visited:get(idx) then
goto __continue30
end
visited:set(idx, true)
cluster[#cluster + 1] = cur
local curMod = cur:FindModifierByName(TOXIN_POOL_MODIFIER_NAME)
local rCur = getToxinPoolRadiusForThinker(nil, cur, curMod, ability)
local pCur = cur:GetAbsOrigin()
for ____, other in ipairs(toxinThinkers) do
do
if not toxinIsValidUnit(nil, other) then
goto __continue33
end
local oIdx = other:GetEntityIndex()
if visited:get(oIdx) then
goto __continue33
end
local oMod = other:FindModifierByName(TOXIN_POOL_MODIFIER_NAME)
local rO = getToxinPoolRadiusForThinker(nil, other, oMod, ability)
if toxinPoolsOverlap(
nil,
pCur,
rCur,
other:GetAbsOrigin(),
rO
) then
queue[#queue + 1] = other
end
end
::__continue33::
end
end
::__continue30::
end
return cluster
end
function computeMergedPoolStats(self, cluster, ability)
if not toxinIsValidAbility(nil, ability) then
return {
centroid = Vector(0, 0, 0),
damagePerTick = 0,
radius = 1,
duration = 0.05,
mergeStackCount = 1
}
end
local valid = __TS__ArrayFilter(
cluster,
function(____, t) return toxinIsValidUnit(nil, t) end
)
local n = #valid
if n <= 0 then
local baseRadius = ability:GetSpecialValueFor("radius")
local baseDur = ability:GetSpecialValueFor("duration")
return {
centroid = Vector(0, 0, 0),
damagePerTick = ability:GetSpecialValueFor("damage") * 0.33,
radius = clampToxinPoolRadius(nil, baseRadius),
duration = math.max(0.05, baseDur),
mergeStackCount = 1
}
end
local baseDuration = ability:GetSpecialValueFor("duration")
local radiusBonusPerMerge = math.max(
0,
ability:GetSpecialValueFor("merge_radius_bonus")
)
local durationBonusPerMerge = math.max(
0,
ability:GetSpecialValueFor("merge_duration_bonus")
)
local sumR2 = 0
local sumDmg = 0
local sumStacks = 0
local maxRem = 0
local sx = 0
local sy = 0
local sz = 0
for ____, t in ipairs(valid) do
local mod = t:FindModifierByName(TOXIN_POOL_MODIFIER_NAME)
local r = getToxinPoolRadiusForThinker(nil, t, mod, ability)
sumR2 = sumR2 + r * r
sumDmg = sumDmg + getToxinDamagePerTickForThinker(nil, mod, ability)
sumStacks = sumStacks + getToxinPoolMergeStackForThinker(nil, mod, ability)
local p = t:GetAbsOrigin()
sx = sx + p.x
sy = sy + p.y
sz = sz + p.z
if mod then
maxRem = math.max(
maxRem,
getToxinPoolRemainingDuration(nil, mod, baseDuration)
)
else
maxRem = math.max(maxRem, baseDuration)
end
end
local mergedRadiusUncapped = math.max(
1,
math.sqrt(sumR2) + math.max(0, n - 1) * radiusBonusPerMerge
)
local mergedRadius = clampToxinPoolRadius(nil, mergedRadiusUncapped)
local mergedDuration = math.max(
0.05,
maxRem + math.max(0, n - 1) * durationBonusPerMerge
)
return {
centroid = Vector(sx / n, sy / n, sz / n),
damagePerTick = sumDmg,
radius = mergedRadius,
duration = mergedDuration,
mergeStackCount = math.max(1, sumStacks)
}
end
function toxinSortDedupeEntIndices(self, arr)
if #arr <= 1 then
return arr
end
local sorted = {unpack(arr)}
__TS__ArraySort(
sorted,
function(____, a, b) return a - b end
)
local out = {}
local prev = -2147483648
for ____, x in ipairs(sorted) do
local n = x
if n ~= prev then
out[#out + 1] = x
prev = n
end
end
return out
end
function toxinEnqueuePoolMergeForThinker(self, entIndex)
if not IsServer() then
return
end
toxinMergeQueue[#toxinMergeQueue + 1] = entIndex
if toxinMergeDrainScheduled or toxinIsDrainingMerge then
return
end
toxinMergeDrainScheduled = true
Timers:CreateTimer(0, toxinDrainToxinMergeQueueFrame)
end
function toxinDrainToxinMergeQueueFrame(self)
if not IsServer() then
return
end
toxinIsDrainingMerge = true
toxinMergeDrainScheduled = false
local batch = toxinMergeQueue
toxinMergeQueue = {}
local uniq = toxinSortDedupeEntIndices(nil, batch)
for ____, idx in ipairs(uniq) do
toxinTryResolvePoolMergeForThinkerIndex(nil, idx)
end
toxinIsDrainingMerge = false
if #toxinMergeQueue > 0 then
toxinMergeDrainScheduled = true
Timers:CreateTimer(0, toxinDrainToxinMergeQueueFrame)
end
end
function toxinTryResolvePoolMergeForThinkerIndex(self, entIndex)
if not IsServer() then
return
end
local npc = EntIndexToHScript(entIndex)
if not isToxinPoolThinker(nil, npc) then
return
end
local buff = npc:FindModifierByName(TOXIN_POOL_MODIFIER_NAME)
if not buff then
return
end
local poolMod = buff
local abilityRaw = buff:GetAbility()
if not toxinIsValidAbility(nil, abilityRaw) then
return
end
local ability = abilityRaw
local caster = buff:GetCaster()
if not toxinIsValidUnit(nil, caster) then
return
end
local cluster = buildToxinOverlapCluster(nil, npc, ability)
if #cluster < 2 then
poolMod:initializeSinglePoolFromAbility()
return
end
local mergeLeader
local minIdx = 2147483647
for ____, t in ipairs(cluster) do
do
if not toxinIsValidUnit(nil, t) then
goto __continue141
end
local ei = t:GetEntityIndex()
if ei < minIdx then
minIdx = ei
mergeLeader = t
end
end
::__continue141::
end
if not mergeLeader or not toxinIsValidUnit(nil, mergeLeader) then
return
end
if npc:GetEntityIndex() ~= mergeLeader:GetEntityIndex() then
toxinEnqueuePoolMergeForThinker(
nil,
mergeLeader:GetEntityIndex()
)
return
end
local merged = computeMergedPoolStats(nil, cluster, ability)
local spawnParams = {duration = merged.duration, merged_damage_per_tick = merged.damagePerTick, merged_radius = merged.radius, merged_stack_count = merged.mergeStackCount}
for ____, t in ipairs(cluster) do
if toxinIsValidUnit(nil, t) then
UTIL_Remove(t)
end
end
if not toxinIsValidAbility(nil, ability) or not toxinIsValidUnit(nil, caster) then
return
end
CreateModifierThinker(
caster,
ability,
TOXIN_POOL_MODIFIER_NAME,
spawnParams,
merged.centroid,
caster:GetTeamNumber(),
false
)
end
TOXIN_POOL_MODIFIER_NAME = "modifier_spider_nethertoxin_lua"
TOXIN_MERGE_SCAN_RADIUS = 2400
TOXIN_MERGE_OVERLAP_EPSILON = 12
TOXIN_THINKER_CLASS = "npc_dota_thinker"
TOXIN_POOL_MAX_RADIUS = 900
TOXIN_MAX_MERGE_SCAN_THINKERS = 220
toxinMergeQueue = {}
toxinMergeDrainScheduled = false
toxinIsDrainingMerge = false
____exports.toxin = __TS__Class()
local toxin = ____exports.toxin
toxin.name = "toxin"
toxin.____file_path = "scripts/vscripts/abilities/creep/toxin.lua"
__TS__ClassExtends(toxin, BaseAbility)
function toxin.prototype.Precache(self, context)
PrecacheResource("particle", "particles/units/heroes/hero_alchemist/alchemist_acid_spray.vpcf", context)
PrecacheResource("particle", "particles/units/heroes/hero_viper/viper_nethertoxin.vpcf", context)
PrecacheResource("particle", "particles/units/heroes/hero_viper/viper_nethertoxin_debuff.vpcf", context)
PrecacheResource("soundfile", "soundevents/game_sounds_heroes/game_sounds_broodmother.vsndevts", context)
PrecacheResource("soundfile", "soundevents/game_sounds_heroes/game_sounds_viper.vsndevts", context)
end
function toxin.prototype.GetIntrinsicModifierName(self)
return "modifier_spider_toxin_death_listener"
end
toxin = __TS__Decorate(
toxin,
toxin,
{registerAbility(nil)},
{kind = "class", name = "toxin"}
)
____exports.toxin = toxin
____exports.modifier_spider_toxin_death_listener = __TS__Class()
local modifier_spider_toxin_death_listener = ____exports.modifier_spider_toxin_death_listener
modifier_spider_toxin_death_listener.name = "modifier_spider_toxin_death_listener"
modifier_spider_toxin_death_listener.____file_path = "scripts/vscripts/abilities/creep/toxin.lua"
__TS__ClassExtends(modifier_spider_toxin_death_listener, BaseModifier)
function modifier_spider_toxin_death_listener.prototype.IsHidden(self)
return true
end
function modifier_spider_toxin_death_listener.prototype.IsPurgable(self)
return false
end
function modifier_spider_toxin_death_listener.prototype.OnCreated(self, _params)
if not IsServer() then
return
end
local parent = self:GetParent()
if not toxinIsValidUnit(nil, parent) then
return
end
trySetIntrinsicCreepRenderColor(
nil,
parent,
51,
102,
0
)
end
function modifier_spider_toxin_death_listener.prototype.DeclareFunctions(self)
return {MODIFIER_EVENT_ON_DEATH}
end
function modifier_spider_toxin_death_listener.prototype.OnDeath(self, event)
if not IsServer() then
return
end
if event.unit ~= self:GetParent() then
return
end
local ability = self:GetAbility()
if not toxinIsValidAbility(nil, ability) then
return
end
local caster = self:GetCaster()
if not toxinIsValidUnit(nil, caster) then
return
end
local parent = self:GetParent()
local spawnOrigin = parent:GetAbsOrigin()
local duration = ability:GetSpecialValueFor("duration")
local radius = clampToxinPoolRadius(
nil,
ability:GetSpecialValueFor("radius")
)
local splashPfx = ParticleManager:CreateParticle("particles/units/heroes/hero_alchemist/alchemist_acid_spray.vpcf", PATTACH_WORLDORIGIN, nil)
ParticleManager:SetParticleControl(splashPfx, 0, spawnOrigin)
ParticleManager:SetParticleControl(
splashPfx,
1,
Vector(radius, 1, 1)
)
ParticleManager:SetParticleControl(
splashPfx,
15,
Vector(255, 153, 102)
)
ParticleManager:SetParticleControl(
splashPfx,
16,
Vector(1, 0, 0)
)
Timers:CreateTimer(
4,
function()
if not IsServer() then
return
end
ParticleManager:DestroyParticle(splashPfx, false)
ParticleManager:ReleaseParticleIndex(splashPfx)
end
)
EmitSoundOn("Hero_Broodmother.SpawnSpiderlings", parent)
CreateModifierThinker(
caster,
ability,
TOXIN_POOL_MODIFIER_NAME,
{duration = duration},
spawnOrigin,
caster:GetTeamNumber(),
false
)
end
modifier_spider_toxin_death_listener = __TS__Decorate(
modifier_spider_toxin_death_listener,
modifier_spider_toxin_death_listener,
{registerModifier(nil)},
{kind = "class", name = "modifier_spider_toxin_death_listener"}
)
____exports.modifier_spider_toxin_death_listener = modifier_spider_toxin_death_listener
____exports.modifier_spider_nethertoxin_lua = __TS__Class()
local modifier_spider_nethertoxin_lua = ____exports.modifier_spider_nethertoxin_lua
modifier_spider_nethertoxin_lua.name = "modifier_spider_nethertoxin_lua"
modifier_spider_nethertoxin_lua.____file_path = "scripts/vscripts/abilities/creep/toxin.lua"
__TS__ClassExtends(modifier_spider_nethertoxin_lua, BaseModifier)
function modifier_spider_nethertoxin_lua.prototype.____constructor(self, ...)
BaseModifier.prototype.____constructor(self, ...)
self.effectiveRadius = 0
self.damagePerTick = 0
self.poolMergeStackCount = 0
self.auraPenaltyStack = 1
self.damageTable = {}
end
function modifier_spider_nethertoxin_lua.prototype.GetTexture(self)
return "viper_nethertoxin"
end
function modifier_spider_nethertoxin_lua.prototype.IsHidden(self)
return false
end
function modifier_spider_nethertoxin_lua.prototype.IsDebuff(self)
return true
end
function modifier_spider_nethertoxin_lua.prototype.IsStunDebuff(self)
return false
end
function modifier_spider_nethertoxin_lua.prototype.IsPurgable(self)
return false
end
function modifier_spider_nethertoxin_lua.prototype.initializeAuraVictimDebuff(self)
local ability = self:GetAbility()
if toxinIsValidAbility(nil, ability) then
self:SetDuration(
math.max(
0.05,
self:GetAuraDuration()
),
true
)
end
self.effectiveRadius = 0
self.damagePerTick = 0
self.poolMergeStackCount = 0
if IsServer() then
self:syncAuraPenaltyFromCasterThinker()
self:SetStackCount(math.max(
1,
math.floor(self.auraPenaltyStack)
))
end
end
function modifier_spider_nethertoxin_lua.prototype.syncAuraPenaltyFromCasterThinker(self)
local parent = self:GetParent()
if isToxinPoolThinker(nil, parent) then
return
end
local source = self:GetCaster()
if not toxinIsValidUnit(nil, source) then
return
end
local srcMod = source:FindModifierByName(TOXIN_POOL_MODIFIER_NAME)
if srcMod ~= nil and srcMod.poolMergeStackCount > 0 then
self.auraPenaltyStack = math.floor(srcMod.poolMergeStackCount)
else
self.auraPenaltyStack = math.max(1, self.auraPenaltyStack)
end
end
function modifier_spider_nethertoxin_lua.prototype.initializeSinglePoolFromAbility(self)
local ability = self:GetAbility()
local caster = self:GetCaster()
local parent = self:GetParent()
if not toxinIsValidAbility(nil, ability) or not toxinIsValidUnit(nil, caster) or not toxinIsValidUnit(nil, parent) then
return
end
local baseDamage = ability:GetSpecialValueFor("damage")
local baseRadius = ability:GetSpecialValueFor("radius")
local baseDuration = ability:GetSpecialValueFor("duration")
self.damagePerTick = (baseDamage + self:GetCaster():GetAttackDamage() * 0.15) * 0.33
local totaldamage = self:GetParent():IsRangedAttacker() and self.damagePerTick or self.damagePerTick * 0.25
self.effectiveRadius = clampToxinPoolRadius(nil, baseRadius)
self.poolMergeStackCount = 1
self:SetDuration(
math.max(0.05, baseDuration),
true
)
self.damageTable = {
victim = caster,
attacker = caster,
damage = totaldamage,
damage_type = ability:GetAbilityDamageType(),
ability = ability
}
self:StartIntervalThink(0.33)
self:PlayEffects()
end
function modifier_spider_nethertoxin_lua.prototype.initializeMergedPoolFromParams(self, params)
local ability = self:GetAbility()
local caster = self:GetCaster()
local parent = self:GetParent()
if not toxinIsValidAbility(nil, ability) or not toxinIsValidUnit(nil, caster) or not toxinIsValidUnit(nil, parent) then
return
end
self.damagePerTick = params.merged_damage_per_tick
self.effectiveRadius = clampToxinPoolRadius(nil, params.merged_radius)
self.poolMergeStackCount = math.max(
1,
math.floor(params.merged_stack_count or 1)
)
self:SetDuration(
math.max(0.05, params.duration),
true
)
self.damageTable = {
victim = caster,
attacker = caster,
damage = self.damagePerTick,
damage_type = ability:GetAbilityDamageType(),
ability = ability
}
self:StartIntervalThink(0.33)
self:PlayEffects()
end
function modifier_spider_nethertoxin_lua.prototype.OnCreated(self, params)
if not IsServer() then
return
end
local parent = self:GetParent()
if not isToxinPoolThinker(nil, parent) then
self:initializeAuraVictimDebuff()
return
end
local p = params
if p.merged_damage_per_tick ~= nil and p.merged_radius ~= nil and p.duration ~= nil then
self:initializeMergedPoolFromParams(p)
return
end
toxinEnqueuePoolMergeForThinker(
nil,
parent:GetEntityIndex()
)
end
function modifier_spider_nethertoxin_lua.prototype.OnRefresh(self, _params)
if not IsServer() then
return
end
local ability = self:GetAbility()
if not toxinIsValidAbility(nil, ability) then
return
end
if not isToxinPoolThinker(
nil,
self:GetParent()
) then
self:syncAuraPenaltyFromCasterThinker()
self:SetStackCount(math.max(
1,
math.floor(self.auraPenaltyStack)
))
return
end
self.damageTable.damage = self.damagePerTick
self.damageTable.damage_type = ability:GetAbilityDamageType()
end
function modifier_spider_nethertoxin_lua.prototype.DeclareFunctions(self)
return {MODIFIER_PROPERTY_PHYSICAL_ARMOR_BONUS, MODIFIER_PROPERTY_MAGICAL_RESISTANCE_BONUS}
end
function modifier_spider_nethertoxin_lua.prototype.GetModifierPhysicalArmorBonus(self)
local parent = self:GetParent()
if isToxinPoolThinker(nil, parent) then
return 0
end
local ability = self:GetAbility()
if not toxinIsValidAbility(nil, ability) then
return 0
end
local per = math.max(
0,
ability:GetSpecialValueFor("armor_magic_reduce_per_stack")
)
local scale = Difficulty:getNpcStatScale()
return -per * math.max(
1,
math.floor(self.auraPenaltyStack)
) * scale
end
function modifier_spider_nethertoxin_lua.prototype.GetModifierMagicalResistanceBonus(self)
local parent = self:GetParent()
if isToxinPoolThinker(nil, parent) then
return 0
end
local ability = self:GetAbility()
if not toxinIsValidAbility(nil, ability) then
return 0
end
local per = math.max(
0,
ability:GetSpecialValueFor("armor_magic_reduce_per_stack")
)
local scale = Difficulty:getNpcStatScale()
return -per * math.max(
1,
math.floor(self.auraPenaltyStack)
) * scale
end
function modifier_spider_nethertoxin_lua.prototype.OnDestroy(self)
if not IsServer() then
return
end
local parent = self:GetParent()
if not isToxinPoolThinker(nil, parent) then
return
end
local entIndex = parent:GetEntityIndex()
Timers:CreateTimer(
0,
function()
if not IsServer() then
return
end
local npc = EntIndexToHScript(entIndex)
if not toxinIsValidUnit(nil, npc) then
return
end
if npc:GetClassname() ~= TOXIN_THINKER_CLASS then
return
end
UTIL_Remove(npc)
end
)
end
function modifier_spider_nethertoxin_lua.prototype.CheckState(self)
return {[MODIFIER_STATE_PASSIVES_DISABLED] = true}
end
function modifier_spider_nethertoxin_lua.prototype.OnIntervalThink(self)
local parent = self:GetParent()
if not isToxinPoolThinker(nil, parent) then
return
end
local caster = self:GetCaster()
local ability = self:GetAbility()
if not toxinIsValidUnit(nil, caster) or not toxinIsValidAbility(nil, ability) then
return
end
local units = FindUnitsInRadius(
caster:GetTeamNumber(),
parent:GetAbsOrigin(),
nil,
self.effectiveRadius,
self:GetAuraSearchTeam(),
self:GetAuraSearchType(),
DOTA_UNIT_TARGET_FLAG_NONE,
FIND_ANY_ORDER,
false
)
self.damageTable.attacker = caster
self.damageTable.ability = ability
self.damageTable.damage_type = ability:GetAbilityDamageType()
local damagedAny = false
for ____, unit in ipairs(units) do
do
if not toxinIsValidUnit(nil, unit) or not unit:IsAlive() then
goto __continue104
end
self.damageTable.victim = unit
ApplyDamage(self.damageTable)
damagedAny = true
end
::__continue104::
end
if damagedAny then
EmitSoundOn("Hero_Viper.NetherToxin.Damage", parent)
end
end
function modifier_spider_nethertoxin_lua.prototype.IsAura(self)
return isToxinPoolThinker(
nil,
self:GetParent()
)
end
function modifier_spider_nethertoxin_lua.prototype.GetModifierAura(self)
return TOXIN_POOL_MODIFIER_NAME
end
function modifier_spider_nethertoxin_lua.prototype.GetAuraRadius(self)
local ability = self:GetAbility()
if self.effectiveRadius > 0 then
return self.effectiveRadius
end
if toxinIsValidAbility(nil, ability) then
return clampToxinPoolRadius(
nil,
ability:GetSpecialValueFor("radius")
)
end
return 0
end
function modifier_spider_nethertoxin_lua.prototype.GetAuraDuration(self)
return 0.25
end
function modifier_spider_nethertoxin_lua.prototype.GetAuraSearchTeam(self)
return DOTA_UNIT_TARGET_TEAM_ENEMY
end
function modifier_spider_nethertoxin_lua.prototype.GetAuraSearchType(self)
return DOTA_UNIT_TARGET_HERO + DOTA_UNIT_TARGET_BASIC
end
function modifier_spider_nethertoxin_lua.prototype.GetEffectName(self)
return "particles/units/heroes/hero_viper/viper_nethertoxin_debuff.vpcf"
end
function modifier_spider_nethertoxin_lua.prototype.GetEffectAttachType(self)
return PATTACH_ABSORIGIN_FOLLOW
end
function modifier_spider_nethertoxin_lua.prototype.PlayEffects(self)
local parent = self:GetParent()
if not toxinIsValidUnit(nil, parent) then
return
end
local particle_cast = "particles/units/heroes/hero_viper/viper_nethertoxin.vpcf"
local sound_cast = "Hero_Viper.NetherToxin"
local effect_cast = ParticleManager:CreateParticle(particle_cast, PATTACH_WORLDORIGIN, nil)
ParticleManager:SetParticleControl(
effect_cast,
0,
parent:GetOrigin()
)
ParticleManager:SetParticleControl(
effect_cast,
1,
Vector(self.effectiveRadius, 1, 1)
)
self:AddParticle(
effect_cast,
false,
false,
-1,
false,
false
)
EmitSoundOn(sound_cast, parent)
end
modifier_spider_nethertoxin_lua = __TS__Decorate(
modifier_spider_nethertoxin_lua,
modifier_spider_nethertoxin_lua,
{registerModifier(nil)},
{kind = "class", name = "modifier_spider_nethertoxin_lua"}
)
____exports.modifier_spider_nethertoxin_lua = modifier_spider_nethertoxin_lua
return ____exports