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

285 lines
13 KiB
Lua

local ____lualib = require("lualib_bundle")
local __TS__Class = ____lualib.__TS__Class
local __TS__ClassExtends = ____lualib.__TS__ClassExtends
local __TS__Decorate = ____lualib.__TS__Decorate
local ____exports = {}
local modifier_boss_nevermore_hub_crossburst_lock
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 ____modifier_boss_nevermore_coil_debuff = require("abilities.creep.modifier_boss_nevermore_coil_debuff")
local modifier_boss_nevermore_coil_debuff = ____modifier_boss_nevermore_coil_debuff.modifier_boss_nevermore_coil_debuff
--- Случайная точка-хаб: от неё волны взрывов по 4 лучам (плюс или крест).
____exports.boss_nevermore_hub_crossburst = __TS__Class()
local boss_nevermore_hub_crossburst = ____exports.boss_nevermore_hub_crossburst
boss_nevermore_hub_crossburst.name = "boss_nevermore_hub_crossburst"
boss_nevermore_hub_crossburst.____file_path = "scripts/vscripts/abilities/creep/boss_nevermore_hub_crossburst.lua"
__TS__ClassExtends(boss_nevermore_hub_crossburst, BaseAbility)
function boss_nevermore_hub_crossburst.prototype.Precache(self, context)
PrecacheResource("particle", "particles/units/heroes/hero_nevermore/nevermore_shadowraze.vpcf", context)
PrecacheResource("particle", "particles/darkmoon_creep_warning.vpcf", context)
PrecacheResource("particle", "particles/econ/events/darkmoon_2017/darkmoon_generic_aoe.vpcf", context)
PrecacheResource("soundfile", "sounds/units/heroes/nevermore/shadowraze.vsnd", context)
end
function boss_nevermore_hub_crossburst.prototype.GetAOERadius(self)
local pick = self:getSpecialOrDefault("spawn_pick_radius", ____exports.boss_nevermore_hub_crossburst.DEFAULT_PICK_RADIUS)
local start = self:getSpecialOrDefault("ring_start_dist", ____exports.boss_nevermore_hub_crossburst.DEFAULT_RING_START)
local step = self:getSpecialOrDefault("ring_step", ____exports.boss_nevermore_hub_crossburst.DEFAULT_RING_STEP)
local n = self:getRingCountForPhase(self:getBossPhase())
local reach = start + (n - 1) * step + self:GetSpecialValueFor("radius")
return pick + reach
end
function boss_nevermore_hub_crossburst.prototype.getSpecialOrDefault(self, name, fallback)
local v = self:GetSpecialValueFor(name)
if not v or v <= 0 then
return fallback
end
return v
end
function boss_nevermore_hub_crossburst.prototype.getBossPhase(self)
local c = self:GetCaster()
if not c or c:IsNull() then
return 1
end
local hp = c:GetHealthPercent()
if hp <= 25 then
return 4
end
if hp <= 50 then
return 3
end
if hp <= 75 then
return 2
end
return 1
end
function boss_nevermore_hub_crossburst.prototype.getRingCountForPhase(self, phase)
local base = math.floor(self:getSpecialOrDefault("ring_count", ____exports.boss_nevermore_hub_crossburst.DEFAULT_RING_COUNT))
local perPhase = math.floor(self:getSpecialOrDefault("ring_count_phase_bonus", ____exports.boss_nevermore_hub_crossburst.DEFAULT_RING_COUNT_PHASE_BONUS))
return math.max(1, base + (phase - 1) * perPhase)
end
function boss_nevermore_hub_crossburst.prototype.pickRandomHub(self, groundOrigin)
local maxR = self:getSpecialOrDefault("spawn_pick_radius", ____exports.boss_nevermore_hub_crossburst.DEFAULT_PICK_RADIUS)
local ang = RandomFloat(0, 360)
local dist = maxR * math.sqrt(RandomFloat(0.001, 1))
local rad = ang * math.pi / 180
local dx = math.cos(rad) * dist
local dy = math.sin(rad) * dist
return GetGroundPosition(
groundOrigin + Vector(dx, dy, 0),
nil
)
end
function boss_nevermore_hub_crossburst.prototype.getDirections(self, pattern)
if pattern == "plus" then
return {
Vector(1, 0, 0),
Vector(-1, 0, 0),
Vector(0, 1, 0),
Vector(0, -1, 0)
}
end
local s = 1 / math.sqrt(2)
return {
Vector(s, s, 0),
Vector(s, -s, 0),
Vector(-s, s, 0),
Vector(-s, -s, 0)
}
end
function boss_nevermore_hub_crossburst.prototype.OnSpellStart(self)
if not IsServer() then
return
end
local caster = self:GetCaster()
if not caster or caster:IsNull() or not caster:IsAlive() then
return
end
local bossGround = GetGroundPosition(
caster:GetAbsOrigin(),
nil
)
local hub = self:pickRandomHub(bossGround)
local pattern = RandomInt(0, 1) == 0 and "plus" or "cross"
local radius = self:GetSpecialValueFor("radius")
local rEff = radius > 0 and radius or ____exports.boss_nevermore_hub_crossburst.DEFAULT_RADIUS
local phaseNow = self:getBossPhase()
local ringCount = self:getRingCountForPhase(phaseNow)
local ringStep = self:getSpecialOrDefault("ring_step", ____exports.boss_nevermore_hub_crossburst.DEFAULT_RING_STEP)
local ringStart = self:getSpecialOrDefault("ring_start_dist", ____exports.boss_nevermore_hub_crossburst.DEFAULT_RING_START)
local baseInterval = self:getSpecialOrDefault("ring_interval", ____exports.boss_nevermore_hub_crossburst.DEFAULT_RING_INTERVAL)
--- Ниже фаза — чуть быстрее волны (до ~−12% на фазе 4).
local ringInterval = baseInterval * math.max(0.72, 1 - (phaseNow - 1) * 0.06)
local firstDelay = self:getSpecialOrDefault("first_ring_delay", ____exports.boss_nevermore_hub_crossburst.DEFAULT_FIRST_DELAY)
local warningTime = self:getSpecialOrDefault("precast_warning_time", ____exports.boss_nevermore_hub_crossburst.DEFAULT_WARNING_TIME)
local dirs = self:getDirections(pattern)
local function ringPositionsForIndex(____, ringIdx)
local along = ringStart + ringIdx * ringStep
local out = {}
for ____, d in ipairs(dirs) do
local scaled = Vector(d.x * along, d.y * along, 0)
out[#out + 1] = GetGroundPosition(hub + scaled, nil)
end
return out
end
local lastHitTime = firstDelay + (ringCount - 1) * ringInterval
caster:AddNewModifier(caster, self, modifier_boss_nevermore_hub_crossburst_lock.name, {duration = lastHitTime + 0.5})
do
local r = 0
while r < ringCount do
local hitTime = firstDelay + r * ringInterval
local warnT = math.max(0, hitTime - warningTime)
local positions = ringPositionsForIndex(nil, r)
local isRedWave = r % 2 == 0
Timers:CreateTimer(
warnT,
function()
if not caster or caster:IsNull() or not caster:IsAlive() then
return nil
end
local ____pairs = {}
for ____, p in ipairs(positions) do
____pairs[#____pairs + 1] = self:createWarningPair(p, rEff, isRedWave)
end
Timers:CreateTimer(
hitTime - warnT,
function()
if not caster or caster:IsNull() or not caster:IsAlive() then
return nil
end
for ____, pair in ipairs(____pairs) do
do
if not pair then
goto __continue28
end
ParticleManager:DestroyParticle(pair[1], true)
ParticleManager:ReleaseParticleIndex(pair[1])
ParticleManager:DestroyParticle(pair[2], true)
ParticleManager:ReleaseParticleIndex(pair[2])
end
::__continue28::
end
for ____, p in ipairs(positions) do
self:applyRingDamage(p, rEff)
end
return nil
end
)
return nil
end
)
r = r + 1
end
end
end
function boss_nevermore_hub_crossburst.prototype.createWarningPair(self, pos, rad, firstStyle)
local caster = self:GetCaster()
local warningParticle = ParticleManager:CreateParticle("particles/darkmoon_creep_warning.vpcf", PATTACH_CUSTOMORIGIN, caster)
local aoeParticle = ParticleManager:CreateParticle("particles/econ/events/darkmoon_2017/darkmoon_generic_aoe.vpcf", PATTACH_CUSTOMORIGIN, caster)
ParticleManager:SetParticleControl(warningParticle, 0, pos)
ParticleManager:SetParticleControl(
warningParticle,
1,
Vector(rad, 100, 100)
)
ParticleManager:SetParticleControl(aoeParticle, 0, pos)
ParticleManager:SetParticleControl(
aoeParticle,
1,
Vector(rad, 0, 0)
)
ParticleManager:SetParticleControl(
aoeParticle,
2,
Vector(6, 0, 1)
)
local rgb = firstStyle and Vector(230, 80, 40) or Vector(180, 80, 230)
ParticleManager:SetParticleControl(aoeParticle, 3, rgb)
ParticleManager:SetParticleControl(aoeParticle, 4, pos)
return {warningParticle, aoeParticle}
end
function boss_nevermore_hub_crossburst.prototype.applyRingDamage(self, pulsePoint, rad)
local caster = self:GetCaster()
if not caster or caster:IsNull() or not caster:IsAlive() then
return
end
local phase = self:getBossPhase()
local baseDamage = caster:GetAttackDamage()
local bonusDamagePerStack = self:GetSpecialValueFor("coil_stack_bonus_damage")
local damageMultiplier = 1 + (phase - 1) * 0.3
local enemies = FindUnitsInRadius(
caster:GetTeamNumber(),
pulsePoint,
nil,
rad,
DOTA_UNIT_TARGET_TEAM_ENEMY,
bit.bor(DOTA_UNIT_TARGET_HERO, DOTA_UNIT_TARGET_BASIC),
DOTA_UNIT_TARGET_FLAG_NONE,
FIND_ANY_ORDER,
false
)
for ____, enemy in ipairs(enemies) do
local coilDebuff = enemy:FindModifierByName(modifier_boss_nevermore_coil_debuff.name)
local stacks = coilDebuff and coilDebuff:GetStackCount() or 0
local damage = (baseDamage + stacks * bonusDamagePerStack) * damageMultiplier * 0.85
ApplyDamage({
victim = enemy,
attacker = caster,
damage = damage,
damage_type = DAMAGE_TYPE_MAGICAL,
ability = self
})
local updatedDebuff = enemy:AddNewModifier(caster, self, modifier_boss_nevermore_coil_debuff.name, {})
if updatedDebuff ~= nil then
updatedDebuff:SetStackCount(stacks > 0 and stacks + 1 or 1)
end
end
EmitSoundOnLocationWithCaster(pulsePoint, "Hero_Nevermore.Shadowraze", caster)
local hitParticle = ParticleManager:CreateParticle("particles/units/heroes/hero_nevermore/nevermore_shadowraze.vpcf", PATTACH_WORLDORIGIN, nil)
ParticleManager:SetParticleControl(hitParticle, 0, pulsePoint)
ParticleManager:SetParticleControl(
hitParticle,
1,
Vector(rad, 1, 1)
)
ParticleManager:ReleaseParticleIndex(hitParticle)
end
boss_nevermore_hub_crossburst.DEFAULT_PICK_RADIUS = 1500
boss_nevermore_hub_crossburst.DEFAULT_RADIUS = 165
boss_nevermore_hub_crossburst.DEFAULT_RING_COUNT = 7
boss_nevermore_hub_crossburst.DEFAULT_RING_COUNT_PHASE_BONUS = 1
boss_nevermore_hub_crossburst.DEFAULT_RING_STEP = 190
boss_nevermore_hub_crossburst.DEFAULT_RING_START = 90
boss_nevermore_hub_crossburst.DEFAULT_RING_INTERVAL = 0.52
boss_nevermore_hub_crossburst.DEFAULT_FIRST_DELAY = 0.55
boss_nevermore_hub_crossburst.DEFAULT_WARNING_TIME = 0.82
boss_nevermore_hub_crossburst = __TS__Decorate(
boss_nevermore_hub_crossburst,
boss_nevermore_hub_crossburst,
{registerAbility(nil)},
{kind = "class", name = "boss_nevermore_hub_crossburst"}
)
____exports.boss_nevermore_hub_crossburst = boss_nevermore_hub_crossburst
modifier_boss_nevermore_hub_crossburst_lock = __TS__Class()
modifier_boss_nevermore_hub_crossburst_lock.name = "modifier_boss_nevermore_hub_crossburst_lock"
modifier_boss_nevermore_hub_crossburst_lock.____file_path = "scripts/vscripts/abilities/creep/boss_nevermore_hub_crossburst.lua"
__TS__ClassExtends(modifier_boss_nevermore_hub_crossburst_lock, BaseModifier)
function modifier_boss_nevermore_hub_crossburst_lock.prototype.IsHidden(self)
return true
end
function modifier_boss_nevermore_hub_crossburst_lock.prototype.IsPurgable(self)
return false
end
function modifier_boss_nevermore_hub_crossburst_lock.prototype.CheckState(self)
return {[MODIFIER_STATE_DISARMED] = true}
end
modifier_boss_nevermore_hub_crossburst_lock = __TS__Decorate(
modifier_boss_nevermore_hub_crossburst_lock,
modifier_boss_nevermore_hub_crossburst_lock,
{registerModifier(nil)},
{kind = "class", name = "modifier_boss_nevermore_hub_crossburst_lock"}
)
return ____exports