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_coil_beam_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 --- Как 1-я способность (две волны по «шахматке»), но слоты в линию от босса к точке каста — «к врагу». ____exports.boss_nevermore_coil_beam = __TS__Class() local boss_nevermore_coil_beam = ____exports.boss_nevermore_coil_beam boss_nevermore_coil_beam.name = "boss_nevermore_coil_beam" boss_nevermore_coil_beam.____file_path = "scripts/vscripts/abilities/creep/boss_nevermore_coil_beam.lua" __TS__ClassExtends(boss_nevermore_coil_beam, BaseAbility) function boss_nevermore_coil_beam.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_coil_beam.prototype.GetAOERadius(self) local r = self:GetSpecialValueFor("radius") local start = self:getSpecialOrDefault("beam_start_dist", ____exports.boss_nevermore_coil_beam.DEFAULT_BEAM_START) local step = self:getBeamStepDistance() local slots = self:getSlotCountForPhase(self:getBossPhase()) return start + step * math.max(0, slots - 1) + r * 2 end function boss_nevermore_coil_beam.prototype.getSpecialOrDefault(self, name, fallback) local value = self:GetSpecialValueFor(name) if not value or value <= 0 then return fallback end return value end function boss_nevermore_coil_beam.prototype.getBossPhase(self) local caster = self:GetCaster() if not caster or caster:IsNull() then return 1 end local hp = caster: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_coil_beam.prototype.getSlotCountForPhase(self, phase) local baseSlots = math.floor(self:getSpecialOrDefault("lane_slot_count", ____exports.boss_nevermore_coil_beam.DEFAULT_SLOT_COUNT)) local perPhase = math.floor(self:getSpecialOrDefault("lane_slot_phase_bonus", ____exports.boss_nevermore_coil_beam.DEFAULT_SLOT_PHASE_BONUS)) return baseSlots + (phase - 1) * perPhase end function boss_nevermore_coil_beam.prototype.getBeamStepDistance(self) local radius = self:GetSpecialValueFor("radius") local stepKv = self:GetSpecialValueFor("beam_step") if stepKv > 0 then return stepKv end return math.max(radius * 1.22, ____exports.boss_nevermore_coil_beam.DEFAULT_BEAM_STEP) end function boss_nevermore_coil_beam.prototype.buildBeamSlots(self, origin, direction, phase) local dir2d = direction:Normalized() dir2d.z = 0 local slotCount = self:getSlotCountForPhase(phase) local startDist = self:getSpecialOrDefault("beam_start_dist", ____exports.boss_nevermore_coil_beam.DEFAULT_BEAM_START) local step = self:getBeamStepDistance() local firstWaveHitsEvenIndex = RandomInt(0, 1) == 0 local slots = {} do local i = 0 while i < slotCount do local along = startDist + i * step local pos = GetGroundPosition(origin + dir2d * along, nil) local isEven = i % 2 == 0 local ____firstWaveHitsEvenIndex_0 if firstWaveHitsEvenIndex then ____firstWaveHitsEvenIndex_0 = isEven else ____firstWaveHitsEvenIndex_0 = not isEven end local hitsOnFirstWave = ____firstWaveHitsEvenIndex_0 slots[#slots + 1] = {position = pos, hitsOnFirstWave = hitsOnFirstWave} i = i + 1 end end return slots end function boss_nevermore_coil_beam.prototype.OnSpellStart(self) if not IsServer() then return end local caster = self:GetCaster() local origin = caster:GetAbsOrigin() local point = self:GetCursorPosition() local toPoint = point - origin local direction = toPoint:Length2D() < 1 and caster:GetForwardVector() or toPoint:Normalized() local phase = self:getBossPhase() self:spawnBeamPattern(origin, direction, phase) end function boss_nevermore_coil_beam.prototype.spawnBeamPattern(self, origin, direction, phase) local caster = self:GetCaster() local slots = self:buildBeamSlots(origin, direction, phase) if #slots == 0 then return end local radius = self:GetSpecialValueFor("radius") local startDelay = self:getSpecialOrDefault("start_delay", ____exports.boss_nevermore_coil_beam.DEFAULT_START_DELAY) local warningTime = self:getSpecialOrDefault("precast_warning_time", ____exports.boss_nevermore_coil_beam.DEFAULT_WARNING_TIME) local secondDelayRaw = self:GetSpecialValueFor("second_wave_delay") local secondWaveDelay = secondDelayRaw > 0 and secondDelayRaw or ____exports.boss_nevermore_coil_beam.DEFAULT_SECOND_WAVE_DELAY local firstHitTime = startDelay local secondHitTime = firstHitTime + secondWaveDelay local firstWarningStart = math.max(0, firstHitTime - warningTime) local secondWarningStart = math.max(firstHitTime + 0.1, secondHitTime - warningTime) caster:AddNewModifier(caster, self, modifier_boss_nevermore_coil_beam_lock.name, {duration = secondHitTime + 0.35}) local warnings = {} local function destroyWarningPair(____, pair) ParticleManager:DestroyParticle(pair[1], true) ParticleManager:ReleaseParticleIndex(pair[1]) ParticleManager:DestroyParticle(pair[2], true) ParticleManager:ReleaseParticleIndex(pair[2]) end local function clearWarning(____, idx) local pair = warnings[idx + 1] if not pair then return end destroyWarningPair(nil, pair) warnings[idx + 1] = nil end Timers:CreateTimer( firstWarningStart, function() if not caster or caster:IsNull() or not caster:IsAlive() then return nil end do local i = 0 while i < #slots do do if not slots[i + 1].hitsOnFirstWave then goto __continue25 end warnings[i + 1] = self:createPulseWarningColored(slots[i + 1].position, radius, true) end ::__continue25:: i = i + 1 end end return nil end ) Timers:CreateTimer( firstHitTime, function() if not caster or caster:IsNull() or not caster:IsAlive() then return nil end local impactPhase = self:getBossPhase() local baseDamage = caster:GetAttackDamage() local bonusDamagePerStack = self:GetSpecialValueFor("coil_stack_bonus_damage") local damageMultiplier = 1 + (impactPhase - 1) * 0.3 do local i = 0 while i < #slots do do if not slots[i + 1].hitsOnFirstWave then goto __continue29 end clearWarning(nil, i) self:applyPulseDamage( slots[i + 1].position, radius, baseDamage, bonusDamagePerStack, damageMultiplier ) end ::__continue29:: i = i + 1 end end return nil end ) Timers:CreateTimer( secondWarningStart, function() if not caster or caster:IsNull() or not caster:IsAlive() then return nil end do local i = 0 while i < #slots do do if slots[i + 1].hitsOnFirstWave then goto __continue33 end if warnings[i + 1] then goto __continue33 end warnings[i + 1] = self:createPulseWarningColored(slots[i + 1].position, radius, false) end ::__continue33:: i = i + 1 end end return nil end ) Timers:CreateTimer( secondHitTime, function() if not caster or caster:IsNull() or not caster:IsAlive() then return nil end local impactPhase = self:getBossPhase() local baseDamage = caster:GetAttackDamage() local bonusDamagePerStack = self:GetSpecialValueFor("coil_stack_bonus_damage") local damageMultiplier = 1 + (impactPhase - 1) * 0.3 do local i = 0 while i < #slots do do if slots[i + 1].hitsOnFirstWave then goto __continue38 end clearWarning(nil, i) self:applyPulseDamage( slots[i + 1].position, radius, baseDamage, bonusDamagePerStack, damageMultiplier ) end ::__continue38:: i = i + 1 end end return nil end ) end function boss_nevermore_coil_beam.prototype.createPulseWarningColored(self, pulsePoint, radius, firstWaveStrike) 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, pulsePoint) ParticleManager:SetParticleControl( warningParticle, 1, Vector(radius, 100, 100) ) ParticleManager:SetParticleControl(aoeParticle, 0, pulsePoint) ParticleManager:SetParticleControl( aoeParticle, 1, Vector(radius, 0, 0) ) ParticleManager:SetParticleControl( aoeParticle, 2, Vector(6, 0, 1) ) local rgb = firstWaveStrike and Vector(240, 40, 40) or Vector(70, 170, 255) ParticleManager:SetParticleControl(aoeParticle, 3, rgb) ParticleManager:SetParticleControl(aoeParticle, 4, pulsePoint) return {warningParticle, aoeParticle} end function boss_nevermore_coil_beam.prototype.applyPulseDamage(self, pulsePoint, radius, baseDamage, bonusDamagePerStack, damageMultiplier) if not IsServer() then return end local caster = self:GetCaster() if not caster or caster:IsNull() or not caster:IsAlive() then return end local enemies = FindUnitsInRadius( caster:GetTeamNumber(), pulsePoint, nil, radius, 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 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 if stacks > 0 then updatedDebuff:SetStackCount(stacks + 1) else updatedDebuff:SetStackCount(1) end 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(radius, 1, 1) ) ParticleManager:ReleaseParticleIndex(hitParticle) end boss_nevermore_coil_beam.DEFAULT_START_DELAY = 0.9 boss_nevermore_coil_beam.DEFAULT_SECOND_WAVE_DELAY = 1 boss_nevermore_coil_beam.DEFAULT_WARNING_TIME = 0.85 boss_nevermore_coil_beam.DEFAULT_SLOT_COUNT = 12 boss_nevermore_coil_beam.DEFAULT_SLOT_PHASE_BONUS = 2 boss_nevermore_coil_beam.DEFAULT_BEAM_START = 140 boss_nevermore_coil_beam.DEFAULT_BEAM_STEP = 195 boss_nevermore_coil_beam = __TS__Decorate( boss_nevermore_coil_beam, boss_nevermore_coil_beam, {registerAbility(nil)}, {kind = "class", name = "boss_nevermore_coil_beam"} ) ____exports.boss_nevermore_coil_beam = boss_nevermore_coil_beam modifier_boss_nevermore_coil_beam_lock = __TS__Class() modifier_boss_nevermore_coil_beam_lock.name = "modifier_boss_nevermore_coil_beam_lock" modifier_boss_nevermore_coil_beam_lock.____file_path = "scripts/vscripts/abilities/creep/boss_nevermore_coil_beam.lua" __TS__ClassExtends(modifier_boss_nevermore_coil_beam_lock, BaseModifier) function modifier_boss_nevermore_coil_beam_lock.prototype.IsHidden(self) return true end function modifier_boss_nevermore_coil_beam_lock.prototype.IsPurgable(self) return false end function modifier_boss_nevermore_coil_beam_lock.prototype.CheckState(self) return {[MODIFIER_STATE_DISARMED] = true} end modifier_boss_nevermore_coil_beam_lock = __TS__Decorate( modifier_boss_nevermore_coil_beam_lock, modifier_boss_nevermore_coil_beam_lock, {registerModifier(nil)}, {kind = "class", name = "modifier_boss_nevermore_coil_beam_lock"} ) return ____exports