380 lines
14 KiB
Lua
380 lines
14 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 ____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 SHRAPNEL_ABILITY_NAME = "ability_sniper_shrapnel_custom"
|
|
local function isHeroUnit(self, unit)
|
|
return not not unit and not unit:IsNull() and unit:IsHero()
|
|
end
|
|
--- Каст → задержка → thinker: тик урона по врагам в радиусе + аура дебафа (замедление, +% входящего урона).
|
|
____exports.ability_sniper_shrapnel_custom = __TS__Class()
|
|
local ability_sniper_shrapnel_custom = ____exports.ability_sniper_shrapnel_custom
|
|
ability_sniper_shrapnel_custom.name = "ability_sniper_shrapnel_custom"
|
|
ability_sniper_shrapnel_custom.____file_path = "scripts/vscripts/abilities/heroes/sniper/ability_sniper_shrapnel_custom.lua"
|
|
__TS__ClassExtends(ability_sniper_shrapnel_custom, BaseAbility)
|
|
function ability_sniper_shrapnel_custom.prototype.Precache(self, context)
|
|
PrecacheResource("particle", "particles/units/heroes/hero_sniper/sniper_shrapnel_launch.vpcf", context)
|
|
PrecacheResource("particle", "particles/units/heroes/hero_sniper/sniper_shrapnel.vpcf", context)
|
|
PrecacheResource("soundfile", "soundevents/game_sounds_heroes/game_sounds_sniper.vsndevts", context)
|
|
end
|
|
function ability_sniper_shrapnel_custom.prototype.GetAOERadius(self)
|
|
return self:GetSpecialValueFor("radius")
|
|
end
|
|
function ability_sniper_shrapnel_custom.prototype.OnSpellStart(self)
|
|
if not IsServer() then
|
|
return
|
|
end
|
|
local caster = self:GetCaster()
|
|
local rawPos = self:GetCursorPosition()
|
|
local position = GetGroundPosition(rawPos, nil)
|
|
local radius = self:GetSpecialValueFor("radius")
|
|
local damageDelay = self:GetSpecialValueFor("damage_delay")
|
|
local duration = self:GetSpecialValueFor("duration")
|
|
local enemiesInAoE = FindUnitsInRadius(
|
|
caster:GetTeamNumber(),
|
|
position,
|
|
nil,
|
|
radius,
|
|
DOTA_UNIT_TARGET_TEAM_ENEMY,
|
|
DOTA_UNIT_TARGET_HERO,
|
|
DOTA_UNIT_TARGET_FLAG_NOT_ILLUSIONS + DOTA_UNIT_TARGET_FLAG_NO_INVIS + DOTA_UNIT_TARGET_FLAG_INVULNERABLE + DOTA_UNIT_TARGET_FLAG_MAGIC_IMMUNE_ENEMIES,
|
|
FIND_ANY_ORDER,
|
|
false
|
|
)
|
|
if #enemiesInAoE >= 4 then
|
|
if RollPercentage(5) then
|
|
EmitSoundOn("sniper_snip_ability_shrapnel_06", caster)
|
|
elseif RollPercentage(75) then
|
|
local group = {"sniper_snip_ability_shrapnel_02", "sniper_snip_ability_shrapnel_04", "sniper_snip_ability_shrapnel_06"}
|
|
EmitSoundOn(
|
|
group[RandomInt(0, #group - 1) + 1],
|
|
caster
|
|
)
|
|
end
|
|
elseif RollPercentage(75) then
|
|
local castResp = {"sniper_snip_ability_shrapnel_01", "sniper_snip_ability_shrapnel_03"}
|
|
EmitSoundOn(
|
|
castResp[RandomInt(0, #castResp - 1) + 1],
|
|
caster
|
|
)
|
|
end
|
|
EmitSoundOn("Hero_Sniper.ShrapnelShoot", caster)
|
|
EmitSoundOnLocationWithCaster(position, "Hero_Sniper.ShrapnelShatter", caster)
|
|
local cOrig = caster:GetAbsOrigin()
|
|
local flat = Vector(position.x - cOrig.x, position.y - cOrig.y, 0)
|
|
local distance = flat:Length2D()
|
|
local direction = distance > 0 and flat:Normalized() or Vector(1, 0, 0)
|
|
local midX = cOrig.x + direction.x * (distance / 2)
|
|
local midY = cOrig.y + direction.y * (distance / 2)
|
|
local midZ = cOrig.z + direction.z * (distance / 2)
|
|
local fx = ParticleManager:CreateParticle("particles/units/heroes/hero_sniper/sniper_shrapnel_launch.vpcf", PATTACH_CUSTOMORIGIN, caster)
|
|
ParticleManager:SetParticleControlEnt(
|
|
fx,
|
|
0,
|
|
caster,
|
|
PATTACH_POINT_FOLLOW,
|
|
"attach_attack1",
|
|
caster:GetAbsOrigin(),
|
|
true
|
|
)
|
|
ParticleManager:SetParticleControl(
|
|
fx,
|
|
1,
|
|
Vector(midX, midY, midZ + 1000)
|
|
)
|
|
ParticleManager:ReleaseParticleIndex(fx)
|
|
local ability = self
|
|
Timers:CreateTimer(
|
|
damageDelay,
|
|
function()
|
|
if not IsServer() then
|
|
return nil
|
|
end
|
|
if not IsValidEntity(ability) or ability:IsNull() then
|
|
return nil
|
|
end
|
|
local sniper = ability:GetCaster()
|
|
if not isHeroUnit(nil, sniper) or not sniper:IsAlive() then
|
|
return nil
|
|
end
|
|
local thinker = CreateModifierThinker(
|
|
sniper,
|
|
ability,
|
|
____exports.modifier_sniper_shrapnel_aura.name,
|
|
{duration = duration},
|
|
position,
|
|
sniper:GetTeamNumber(),
|
|
false
|
|
)
|
|
local auraMod = thinker and thinker:FindModifierByName(____exports.modifier_sniper_shrapnel_aura.name)
|
|
if auraMod ~= nil then
|
|
auraMod:bindSniper(sniper)
|
|
end
|
|
AddFOWViewer(
|
|
sniper:GetTeamNumber(),
|
|
position,
|
|
radius,
|
|
duration,
|
|
false
|
|
)
|
|
return nil
|
|
end
|
|
)
|
|
end
|
|
ability_sniper_shrapnel_custom = __TS__Decorate(
|
|
ability_sniper_shrapnel_custom,
|
|
ability_sniper_shrapnel_custom,
|
|
{registerAbility(nil)},
|
|
{kind = "class", name = "ability_sniper_shrapnel_custom"}
|
|
)
|
|
____exports.ability_sniper_shrapnel_custom = ability_sniper_shrapnel_custom
|
|
--- Thinker: тик урона + аура. GetAuraOwner = Снайпер.
|
|
____exports.modifier_sniper_shrapnel_aura = __TS__Class()
|
|
local modifier_sniper_shrapnel_aura = ____exports.modifier_sniper_shrapnel_aura
|
|
modifier_sniper_shrapnel_aura.name = "modifier_sniper_shrapnel_aura"
|
|
modifier_sniper_shrapnel_aura.____file_path = "scripts/vscripts/abilities/heroes/sniper/ability_sniper_shrapnel_custom.lua"
|
|
__TS__ClassExtends(modifier_sniper_shrapnel_aura, BaseModifier)
|
|
function modifier_sniper_shrapnel_aura.prototype.____constructor(self, ...)
|
|
BaseModifier.prototype.____constructor(self, ...)
|
|
self.radius = 400
|
|
end
|
|
function modifier_sniper_shrapnel_aura.prototype.bindSniper(self, sniper)
|
|
if not IsServer() or not isHeroUnit(nil, sniper) then
|
|
return
|
|
end
|
|
self.sniper = sniper
|
|
end
|
|
function modifier_sniper_shrapnel_aura.prototype.IsHidden(self)
|
|
return true
|
|
end
|
|
function modifier_sniper_shrapnel_aura.prototype.IsPurgable(self)
|
|
return false
|
|
end
|
|
function modifier_sniper_shrapnel_aura.prototype.IsDebuff(self)
|
|
return false
|
|
end
|
|
function modifier_sniper_shrapnel_aura.prototype.IsBuff(self)
|
|
return true
|
|
end
|
|
function modifier_sniper_shrapnel_aura.prototype.RemoveOnDeath(self)
|
|
return true
|
|
end
|
|
function modifier_sniper_shrapnel_aura.prototype.IsAura(self)
|
|
return true
|
|
end
|
|
function modifier_sniper_shrapnel_aura.prototype.GetAuraOwner(self)
|
|
return self:getSniper() or self:GetParent()
|
|
end
|
|
function modifier_sniper_shrapnel_aura.prototype.GetModifierAura(self)
|
|
return ____exports.modifier_sniper_shrapnel_slow.name
|
|
end
|
|
function modifier_sniper_shrapnel_aura.prototype.GetAuraRadius(self)
|
|
return self.radius
|
|
end
|
|
function modifier_sniper_shrapnel_aura.prototype.GetAuraSearchTeam(self)
|
|
return DOTA_UNIT_TARGET_TEAM_ENEMY
|
|
end
|
|
function modifier_sniper_shrapnel_aura.prototype.GetAuraSearchType(self)
|
|
return DOTA_UNIT_TARGET_HERO + DOTA_UNIT_TARGET_BASIC
|
|
end
|
|
function modifier_sniper_shrapnel_aura.prototype.GetAuraSearchFlags(self)
|
|
return DOTA_UNIT_TARGET_FLAG_NONE
|
|
end
|
|
function modifier_sniper_shrapnel_aura.prototype.GetAuraDuration(self)
|
|
return 0.5
|
|
end
|
|
function modifier_sniper_shrapnel_aura.prototype.OnCreated(self)
|
|
if not IsServer() then
|
|
return
|
|
end
|
|
local ability = self:GetAbility()
|
|
local parent = self:GetParent()
|
|
if not ability or not parent then
|
|
return
|
|
end
|
|
local fromAbility = ability:GetCaster()
|
|
if isHeroUnit(nil, fromAbility) then
|
|
self.sniper = fromAbility
|
|
end
|
|
self.radius = ability:GetSpecialValueFor("radius")
|
|
local point = parent:GetAbsOrigin()
|
|
self.fx = ParticleManager:CreateParticle("particles/units/heroes/hero_sniper/sniper_shrapnel.vpcf", PATTACH_WORLDORIGIN, parent)
|
|
ParticleManager:SetParticleControl(self.fx, 0, point)
|
|
ParticleManager:SetParticleControl(
|
|
self.fx,
|
|
1,
|
|
Vector(self.radius, self.radius, 0)
|
|
)
|
|
ParticleManager:SetParticleControl(self.fx, 2, point)
|
|
self:AddParticle(
|
|
self.fx,
|
|
false,
|
|
false,
|
|
-1,
|
|
false,
|
|
false
|
|
)
|
|
local interval = ability:GetSpecialValueFor("tick_interval")
|
|
self:tickDamage()
|
|
self:StartIntervalThink(interval > 0 and interval or 1)
|
|
end
|
|
function modifier_sniper_shrapnel_aura.prototype.OnIntervalThink(self)
|
|
if not IsServer() then
|
|
return
|
|
end
|
|
self:tickDamage()
|
|
end
|
|
function modifier_sniper_shrapnel_aura.prototype.OnDestroy(self)
|
|
if not IsServer() then
|
|
return
|
|
end
|
|
if self.fx ~= nil then
|
|
ParticleManager:DestroyParticle(self.fx, false)
|
|
ParticleManager:ReleaseParticleIndex(self.fx)
|
|
self.fx = nil
|
|
end
|
|
end
|
|
function modifier_sniper_shrapnel_aura.prototype.getSniper(self)
|
|
if self.sniper and not self.sniper:IsNull() and self.sniper:IsAlive() then
|
|
return self.sniper
|
|
end
|
|
local ability = self:GetAbility()
|
|
local caster = ability and ability:GetCaster()
|
|
if isHeroUnit(nil, caster) then
|
|
self.sniper = caster
|
|
return caster
|
|
end
|
|
return nil
|
|
end
|
|
function modifier_sniper_shrapnel_aura.prototype.tickDamage(self)
|
|
local ability = self:GetAbility()
|
|
local sniper = self:getSniper()
|
|
local parent = self:GetParent()
|
|
if not ability or not sniper or not parent or parent:IsNull() then
|
|
return
|
|
end
|
|
local attackPct = ability:GetSpecialValueFor("attack_damage_pct")
|
|
if attackPct <= 0 then
|
|
return
|
|
end
|
|
local enemies = FindUnitsInRadius(
|
|
sniper:GetTeamNumber(),
|
|
parent:GetAbsOrigin(),
|
|
nil,
|
|
self.radius,
|
|
DOTA_UNIT_TARGET_TEAM_ENEMY,
|
|
DOTA_UNIT_TARGET_HERO + DOTA_UNIT_TARGET_BASIC,
|
|
DOTA_UNIT_TARGET_FLAG_NONE,
|
|
FIND_ANY_ORDER,
|
|
false
|
|
)
|
|
for ____, enemy in ipairs(enemies) do
|
|
do
|
|
if not enemy or enemy:IsNull() or not enemy:IsAlive() then
|
|
goto __continue45
|
|
end
|
|
local damage = sniper:GetAverageTrueAttackDamage(enemy) * (attackPct / 100)
|
|
if damage <= 0 then
|
|
goto __continue45
|
|
end
|
|
ApplyDamage({
|
|
victim = enemy,
|
|
attacker = sniper,
|
|
damage = damage,
|
|
damage_type = DAMAGE_TYPE_PHYSICAL,
|
|
ability = ability,
|
|
damage_flags = DOTA_DAMAGE_FLAG_NO_SPELL_AMPLIFICATION
|
|
})
|
|
end
|
|
::__continue45::
|
|
end
|
|
end
|
|
modifier_sniper_shrapnel_aura = __TS__Decorate(
|
|
modifier_sniper_shrapnel_aura,
|
|
modifier_sniper_shrapnel_aura,
|
|
{registerModifier(nil)},
|
|
{kind = "class", name = "modifier_sniper_shrapnel_aura"}
|
|
)
|
|
____exports.modifier_sniper_shrapnel_aura = modifier_sniper_shrapnel_aura
|
|
--- Дебаф: замедление и +% входящего урона (урон — thinker).
|
|
____exports.modifier_sniper_shrapnel_slow = __TS__Class()
|
|
local modifier_sniper_shrapnel_slow = ____exports.modifier_sniper_shrapnel_slow
|
|
modifier_sniper_shrapnel_slow.name = "modifier_sniper_shrapnel_slow"
|
|
modifier_sniper_shrapnel_slow.____file_path = "scripts/vscripts/abilities/heroes/sniper/ability_sniper_shrapnel_custom.lua"
|
|
__TS__ClassExtends(modifier_sniper_shrapnel_slow, BaseModifier)
|
|
function modifier_sniper_shrapnel_slow.prototype.IsHidden(self)
|
|
return false
|
|
end
|
|
function modifier_sniper_shrapnel_slow.prototype.IsPurgable(self)
|
|
return false
|
|
end
|
|
function modifier_sniper_shrapnel_slow.prototype.IsDebuff(self)
|
|
return true
|
|
end
|
|
function modifier_sniper_shrapnel_slow.prototype.GetTexture(self)
|
|
return "sniper_shrapnel"
|
|
end
|
|
function modifier_sniper_shrapnel_slow.prototype.DeclareFunctions(self)
|
|
return {MODIFIER_PROPERTY_MOVESPEED_BONUS_PERCENTAGE, MODIFIER_PROPERTY_INCOMING_DAMAGE_PERCENTAGE}
|
|
end
|
|
function modifier_sniper_shrapnel_slow.prototype.OnCreated(self)
|
|
if not IsServer() then
|
|
return
|
|
end
|
|
local parent = self:GetParent()
|
|
local sniper = self:getSniper()
|
|
if not parent or not sniper then
|
|
self:Destroy()
|
|
return
|
|
end
|
|
if parent:GetTeamNumber() == sniper:GetTeamNumber() then
|
|
self:Destroy()
|
|
return
|
|
end
|
|
self.sniperEntIndex = sniper:entindex()
|
|
end
|
|
function modifier_sniper_shrapnel_slow.prototype.getShrapnelAbility(self)
|
|
local ability = self:GetAbility()
|
|
if ability and not ability:IsNull() then
|
|
return ability
|
|
end
|
|
local sniper = self:getSniper()
|
|
return sniper and sniper:FindAbilityByName(SHRAPNEL_ABILITY_NAME)
|
|
end
|
|
function modifier_sniper_shrapnel_slow.prototype.getSniper(self)
|
|
if self.sniperEntIndex ~= nil then
|
|
local unit = EntIndexToHScript(self.sniperEntIndex)
|
|
if isHeroUnit(nil, unit) then
|
|
return unit
|
|
end
|
|
end
|
|
local ability = self:GetAbility()
|
|
local caster = ability and ability:GetCaster()
|
|
if isHeroUnit(nil, caster) then
|
|
return caster
|
|
end
|
|
return nil
|
|
end
|
|
function modifier_sniper_shrapnel_slow.prototype.GetModifierMoveSpeedBonus_Percentage(self)
|
|
local ____opt_10 = self:getShrapnelAbility()
|
|
return ____opt_10 and ____opt_10:GetSpecialValueFor("slow_movement_speed") or 0
|
|
end
|
|
function modifier_sniper_shrapnel_slow.prototype.GetModifierIncomingDamage_Percentage(self)
|
|
local ____opt_12 = self:getShrapnelAbility()
|
|
return ____opt_12 and ____opt_12:GetSpecialValueFor("incoming_damage_pct") or 0
|
|
end
|
|
modifier_sniper_shrapnel_slow = __TS__Decorate(
|
|
modifier_sniper_shrapnel_slow,
|
|
modifier_sniper_shrapnel_slow,
|
|
{registerModifier(nil)},
|
|
{kind = "class", name = "modifier_sniper_shrapnel_slow"}
|
|
)
|
|
____exports.modifier_sniper_shrapnel_slow = modifier_sniper_shrapnel_slow
|
|
return ____exports
|