285 lines
13 KiB
Lua
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
|