831 lines
34 KiB
Lua
831 lines
34 KiB
Lua
local ____lualib = require("lualib_bundle")
|
|
local __TS__Class = ____lualib.__TS__Class
|
|
local __TS__ClassExtends = ____lualib.__TS__ClassExtends
|
|
local __TS__StringStartsWith = ____lualib.__TS__StringStartsWith
|
|
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 BaseModifierMotionBoth = ____dota_ts_adapter.BaseModifierMotionBoth
|
|
local registerAbility = ____dota_ts_adapter.registerAbility
|
|
local registerModifier = ____dota_ts_adapter.registerModifier
|
|
local ____heal_tracker = require("utils.heal_tracker")
|
|
local HealWithBattlePass = ____heal_tracker.HealWithBattlePass
|
|
local HOMER_UNIT_NAME = "npc_homer"
|
|
____exports.ability_rubick_telekinesis_custom = __TS__Class()
|
|
local ability_rubick_telekinesis_custom = ____exports.ability_rubick_telekinesis_custom
|
|
ability_rubick_telekinesis_custom.name = "ability_rubick_telekinesis_custom"
|
|
ability_rubick_telekinesis_custom.____file_path = "scripts/vscripts/abilities/heroes/rubick/ability_rubick_telekinesis_custom.lua"
|
|
__TS__ClassExtends(ability_rubick_telekinesis_custom, BaseAbility)
|
|
function ability_rubick_telekinesis_custom.prototype.clearActiveTargetIfMatch(self, unit)
|
|
if self.activeTelekinesisTargetEntIndex == unit:GetEntityIndex() then
|
|
self.activeTelekinesisTargetEntIndex = nil
|
|
end
|
|
end
|
|
function ability_rubick_telekinesis_custom.prototype.clearTelekinesisFromPreviousTarget(self, newTarget)
|
|
local prevIndex = self.activeTelekinesisTargetEntIndex
|
|
if prevIndex == nil or prevIndex == newTarget:GetEntityIndex() then
|
|
return
|
|
end
|
|
self.activeTelekinesisTargetEntIndex = nil
|
|
local prev = EntIndexToHScript(prevIndex)
|
|
if not prev or not IsValidEntity(prev) then
|
|
return
|
|
end
|
|
self:removeTelekinesisFromUnit(prev, true)
|
|
end
|
|
function ability_rubick_telekinesis_custom.prototype.removeTelekinesisFromUnit(self, unit, suppressLandEffect)
|
|
local caster = self:GetCaster()
|
|
local liftMod = unit:FindModifierByName(____exports.modifier_rubick_telekinesis_lift.name)
|
|
if liftMod and liftMod:GetCaster() == caster then
|
|
liftMod:EndTelekinesisEffect(unit, suppressLandEffect)
|
|
return
|
|
end
|
|
local landMod = unit:FindModifierByName(____exports.modifier_rubick_telekinesis_land.name)
|
|
if landMod then
|
|
landMod:SetSuppressLandEffect(suppressLandEffect)
|
|
end
|
|
unit:RemoveModifierByName(____exports.modifier_rubick_telekinesis_lift.name)
|
|
unit:RemoveModifierByName(____exports.modifier_rubick_telekinesis_land.name)
|
|
end
|
|
function ability_rubick_telekinesis_custom.isBossTarget(self, target)
|
|
local unitName = target:GetUnitName()
|
|
if __TS__StringStartsWith(unitName, "npc_boss_") or __TS__StringStartsWith(unitName, "npc_wave_boss") then
|
|
return true
|
|
end
|
|
return target:IsBossCreature() or target:IsBoss()
|
|
end
|
|
function ability_rubick_telekinesis_custom.isHomerTarget(self, target)
|
|
return target:GetUnitName() == HOMER_UNIT_NAME
|
|
end
|
|
function ability_rubick_telekinesis_custom.isBlockedTarget(self, target)
|
|
return ____exports.ability_rubick_telekinesis_custom:isBossTarget(target) or ____exports.ability_rubick_telekinesis_custom:isHomerTarget(target)
|
|
end
|
|
function ability_rubick_telekinesis_custom.getCasterBreakMaxDistance(self, ability, target)
|
|
local castRange = ability:GetCastRange(
|
|
target:GetAbsOrigin(),
|
|
target
|
|
)
|
|
local mult = ability:GetSpecialValueFor("caster_break_range_mult")
|
|
return castRange * (mult > 0 and mult or 2)
|
|
end
|
|
function ability_rubick_telekinesis_custom.isCasterWithinBreakRange(self, ability, caster, target)
|
|
local maxDist = ____exports.ability_rubick_telekinesis_custom:getCasterBreakMaxDistance(ability, target)
|
|
return (caster:GetAbsOrigin() - target:GetAbsOrigin()):Length2D() <= maxDist
|
|
end
|
|
function ability_rubick_telekinesis_custom.prototype.CastFilterResultTarget(self, target)
|
|
if ____exports.ability_rubick_telekinesis_custom:isBlockedTarget(target) then
|
|
return UF_FAIL_CUSTOM
|
|
end
|
|
return UF_SUCCESS
|
|
end
|
|
function ability_rubick_telekinesis_custom.prototype.GetCustomCastErrorTarget(self, target)
|
|
if ____exports.ability_rubick_telekinesis_custom:isHomerTarget(target) then
|
|
return "#dota_hud_error_rubick_telekinesis_homer"
|
|
end
|
|
if ____exports.ability_rubick_telekinesis_custom:isBossTarget(target) then
|
|
return "#dota_hud_error_rubick_telekinesis_boss"
|
|
end
|
|
return ""
|
|
end
|
|
function ability_rubick_telekinesis_custom.prototype.Precache(self, context)
|
|
PrecacheResource("particle", "particles/econ/items/rubick/rubick_force_ambient/rubick_telekinesis_force_cube.vpcf", context)
|
|
PrecacheResource("particle", "particles/econ/items/rubick/rubick_puppet_master/rubick_telekinesis_puppet_string_src.vpcf", context)
|
|
PrecacheResource("particle", "particles/econ/items/rubick/rubick_force_gold_ambient/rubick_telekinesis_land_force_gold.vpcf", context)
|
|
PrecacheResource("particle", "particles/units/heroes/hero_rubick/rubick_telekinesis_debuff.vpcf", context)
|
|
PrecacheResource("particle", "particles/econ/items/rubick/rubick_puppet_master/rubick_telekinesis_puppet_string_attach.vpcf", context)
|
|
PrecacheResource("particle", "particles/units/heroes/hero_rubick/rubick_base_attack.vpcf", context)
|
|
end
|
|
function ability_rubick_telekinesis_custom.prototype.OnSpellStart(self)
|
|
local caster = self:GetCaster()
|
|
local target = self:GetCursorTarget()
|
|
if not target or not target:IsAlive() then
|
|
return
|
|
end
|
|
if ____exports.ability_rubick_telekinesis_custom:isBlockedTarget(target) then
|
|
return
|
|
end
|
|
self:clearTelekinesisFromPreviousTarget(target)
|
|
target:RemoveModifierByName(____exports.modifier_rubick_telekinesis_lift.name)
|
|
target:RemoveModifierByName(____exports.modifier_rubick_telekinesis_land.name)
|
|
local air_time = self:GetSpecialValueFor("air_time")
|
|
local lift_time = self:GetSpecialValueFor("lift_time")
|
|
local drop_time = self:GetSpecialValueFor("drop_time")
|
|
local throw_distance = self:GetSpecialValueFor("throw_distance")
|
|
local lift_height = self:GetSpecialValueFor("lift_height")
|
|
local cursor_pos = self:GetCursorPosition()
|
|
local target_origin = target:GetAbsOrigin()
|
|
local direction = cursor_pos - target_origin
|
|
local dist_to_cursor = direction:Length2D()
|
|
if dist_to_cursor > 1 then
|
|
direction = direction:Normalized()
|
|
else
|
|
direction = target_origin - caster:GetAbsOrigin()
|
|
direction = direction:Normalized()
|
|
end
|
|
local actual_distance = math.min(dist_to_cursor, throw_distance)
|
|
local land_pos = target_origin + direction * actual_distance
|
|
local ground_land = GetGroundPosition(land_pos, target)
|
|
EmitSoundOn("Hero_Rubick.Telekinesis.Cast", caster)
|
|
local is_ally = target:GetTeamNumber() == caster:GetTeamNumber()
|
|
local hasScepter = caster:HasScepter()
|
|
local isInfiniteAirTarget = is_ally
|
|
local isInfiniteAllyAir = isInfiniteAirTarget and hasScepter
|
|
local actual_air_time = is_ally and air_time or air_time * 0.5
|
|
local total_duration = lift_time + actual_air_time + drop_time
|
|
local appliedDuration = isInfiniteAllyAir and 99999 or total_duration
|
|
____exports.modifier_rubick_telekinesis_lift:apply(
|
|
target,
|
|
caster,
|
|
self,
|
|
{
|
|
duration = appliedDuration,
|
|
land_x = ground_land.x,
|
|
land_y = ground_land.y,
|
|
land_z = ground_land.z,
|
|
direction_x = direction.x,
|
|
direction_y = direction.y,
|
|
distance = actual_distance,
|
|
height = lift_height,
|
|
air_time = actual_air_time,
|
|
lift_time = lift_time,
|
|
drop_time = drop_time,
|
|
ally_air_radius = self:GetSpecialValueFor("ally_air_radius"),
|
|
ally_air_max_radius = self:GetSpecialValueFor("ally_air_max_radius"),
|
|
ally_air_move_speed_mult = self:GetSpecialValueFor("ally_air_move_speed_mult"),
|
|
ally_infinite_air_with_scepter = isInfiniteAllyAir and 1 or 0
|
|
}
|
|
)
|
|
target:AddNewModifier(
|
|
caster,
|
|
self,
|
|
____exports.modifier_rubick_telekinesis_land.name,
|
|
{
|
|
duration = appliedDuration,
|
|
land_radius = self:GetSpecialValueFor("land_stun_radius"),
|
|
land_stun = self:GetSpecialValueFor("land_stun_duration"),
|
|
land_damage = self:GetSpecialValueFor("land_damage"),
|
|
ally_heal_per_second = self:GetSpecialValueFor("ally_heal_per_second") * 0.2,
|
|
ally_attack_range_bonus = self:GetSpecialValueFor("ally_attack_range_bonus"),
|
|
ally_attack_speed_bonus = self:GetSpecialValueFor("ally_attack_speed_bonus"),
|
|
ally_bonus_magic_damage = self:GetSpecialValueFor("ally_bonus_magic_damage"),
|
|
ally_bonus_magic_damage_per_int = self:GetSpecialValueFor("ally_bonus_magic_damage_per_int"),
|
|
ally_infinite_air_with_scepter = isInfiniteAllyAir and 1 or 0
|
|
}
|
|
)
|
|
self.activeTelekinesisTargetEntIndex = target:GetEntityIndex()
|
|
end
|
|
ability_rubick_telekinesis_custom = __TS__Decorate(
|
|
ability_rubick_telekinesis_custom,
|
|
ability_rubick_telekinesis_custom,
|
|
{registerAbility(nil)},
|
|
{kind = "class", name = "ability_rubick_telekinesis_custom"}
|
|
)
|
|
____exports.ability_rubick_telekinesis_custom = ability_rubick_telekinesis_custom
|
|
--- Подъём → 5 сек в воздухе → приземление. Враги: стан. Союзники: без стана, могут передвигаться в воздухе.
|
|
____exports.modifier_rubick_telekinesis_lift = __TS__Class()
|
|
local modifier_rubick_telekinesis_lift = ____exports.modifier_rubick_telekinesis_lift
|
|
modifier_rubick_telekinesis_lift.name = "modifier_rubick_telekinesis_lift"
|
|
modifier_rubick_telekinesis_lift.____file_path = "scripts/vscripts/abilities/heroes/rubick/ability_rubick_telekinesis_custom.lua"
|
|
__TS__ClassExtends(modifier_rubick_telekinesis_lift, BaseModifierMotionBoth)
|
|
function modifier_rubick_telekinesis_lift.prototype.____constructor(self, ...)
|
|
BaseModifierMotionBoth.prototype.____constructor(self, ...)
|
|
self.start_pos = Vector(0, 0, 0)
|
|
self.apex_pos = Vector(0, 0, 0)
|
|
self.land_pos = Vector(0, 0, 0)
|
|
self.apex_z = 0
|
|
self.ally_air_radius = 150
|
|
self.ally_air_max_radius = 350
|
|
self.ally_air_move_speed_mult = 1.5
|
|
self.ally_drop_start_pos = nil
|
|
self.ally_infinite_air_with_scepter = false
|
|
self.ally_drop_started = false
|
|
self.last_pos = nil
|
|
self.throw_distance = 375
|
|
end
|
|
function modifier_rubick_telekinesis_lift.prototype.IsHidden(self)
|
|
return true
|
|
end
|
|
function modifier_rubick_telekinesis_lift.prototype.IsPurgable(self)
|
|
return false
|
|
end
|
|
function modifier_rubick_telekinesis_lift.prototype.GetEffectName(self)
|
|
return ""
|
|
end
|
|
function modifier_rubick_telekinesis_lift.prototype.GetEffectAttachType(self)
|
|
return PATTACH_ABSORIGIN_FOLLOW
|
|
end
|
|
function modifier_rubick_telekinesis_lift.prototype.IsAlly(self)
|
|
local caster = self:GetCaster()
|
|
return not not (caster and self:GetParent():GetTeamNumber() == caster:GetTeamNumber())
|
|
end
|
|
function modifier_rubick_telekinesis_lift.prototype.OnCreated(self, params)
|
|
if IsClient() then
|
|
return
|
|
end
|
|
local parent = self:GetParent()
|
|
self.start_pos = parent:GetAbsOrigin()
|
|
local ground_start = GetGroundHeight(self.start_pos, parent)
|
|
self.land_pos = Vector(params.land_x, params.land_y, params.land_z)
|
|
self.air_time = params.air_time
|
|
self.lift_time = params.lift_time
|
|
self.drop_time = params.drop_time
|
|
self.ally_air_radius = params.ally_air_radius or 150
|
|
self.ally_air_max_radius = params.ally_air_max_radius or 350
|
|
self.ally_air_move_speed_mult = params.ally_air_move_speed_mult or 1.5
|
|
self.ally_infinite_air_with_scepter = (params.ally_infinite_air_with_scepter or 0) == 1
|
|
self.throw_distance = params.distance or 375
|
|
self.last_pos = parent:GetAbsOrigin()
|
|
self.apex_pos = Vector(self.start_pos.x, self.start_pos.y, ground_start + params.height)
|
|
self.apex_z = ground_start + params.height
|
|
self:RefreshLiftParticle()
|
|
if self:IsInfiniteAllyAirActive() then
|
|
self:StartIntervalThink(2)
|
|
else
|
|
self:StartIntervalThink(-1)
|
|
end
|
|
if self:ApplyHorizontalMotionController() == false or self:ApplyVerticalMotionController() == false then
|
|
self:Destroy()
|
|
return
|
|
end
|
|
end
|
|
function modifier_rubick_telekinesis_lift.prototype.IsInfiniteAllyAirActive(self)
|
|
return self:IsAlly() and self.ally_infinite_air_with_scepter and not self.ally_drop_started
|
|
end
|
|
function modifier_rubick_telekinesis_lift.prototype.StartForcedDrop(self, me)
|
|
if self.ally_drop_started then
|
|
return
|
|
end
|
|
self.ally_drop_started = true
|
|
self:StartIntervalThink(-1)
|
|
self:EndTelekinesisEffect(me, false)
|
|
end
|
|
function modifier_rubick_telekinesis_lift.prototype.EndTelekinesisEffect(self, me, suppressLandEffect)
|
|
local landMod = me:FindModifierByName(____exports.modifier_rubick_telekinesis_land.name)
|
|
if landMod then
|
|
landMod:SetSuppressLandEffect(suppressLandEffect)
|
|
end
|
|
me:RemoveModifierByName(____exports.modifier_rubick_telekinesis_land.name)
|
|
self:Destroy()
|
|
end
|
|
function modifier_rubick_telekinesis_lift.prototype.GetEffectAnchorPos(self, me)
|
|
local t = self:GetElapsedTime()
|
|
if self:IsAlly() and t >= self.lift_time and (self:IsInfiniteAllyAirActive() or t < self.lift_time + self.air_time) then
|
|
return Vector(self.apex_pos.x, self.apex_pos.y, 0)
|
|
end
|
|
local origin = self.start_pos
|
|
return Vector(origin.x, origin.y, 0)
|
|
end
|
|
function modifier_rubick_telekinesis_lift.prototype.GetMaxEffectDistance(self)
|
|
if self:IsAlly() and self:GetElapsedTime() >= self.lift_time then
|
|
return self.ally_air_max_radius + 150
|
|
end
|
|
return self.throw_distance + 300
|
|
end
|
|
function modifier_rubick_telekinesis_lift.prototype.CheckAndBreakIfCasterTooFar(self, me)
|
|
local caster = self:GetCaster()
|
|
local ability = self:GetAbility()
|
|
if not ability then
|
|
return false
|
|
end
|
|
if not caster or caster:IsNull() or not caster:IsAlive() then
|
|
self:EndTelekinesisEffect(me, true)
|
|
return true
|
|
end
|
|
if not ____exports.ability_rubick_telekinesis_custom:isCasterWithinBreakRange(ability, caster, me) then
|
|
self:EndTelekinesisEffect(me, true)
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
function modifier_rubick_telekinesis_lift.prototype.CheckAndBreakOnTeleport(self, me)
|
|
if not IsServer() then
|
|
return false
|
|
end
|
|
if self:CheckAndBreakIfCasterTooFar(me) then
|
|
return true
|
|
end
|
|
local cur = me:GetAbsOrigin()
|
|
local cur2d = Vector(cur.x, cur.y, 0)
|
|
if self.last_pos then
|
|
local last2d = Vector(self.last_pos.x, self.last_pos.y, 0)
|
|
local jump = (cur2d - last2d):Length2D()
|
|
if jump >= ____exports.modifier_rubick_telekinesis_lift.TELEPORT_JUMP_THRESHOLD then
|
|
self:EndTelekinesisEffect(me, true)
|
|
return true
|
|
end
|
|
end
|
|
local anchor = self:GetEffectAnchorPos(me)
|
|
local distFromAnchor = (cur2d - anchor):Length2D()
|
|
if distFromAnchor > self:GetMaxEffectDistance() then
|
|
self:EndTelekinesisEffect(me, true)
|
|
return true
|
|
end
|
|
self.last_pos = cur
|
|
return false
|
|
end
|
|
function modifier_rubick_telekinesis_lift.prototype.RefreshLiftParticle(self)
|
|
if self.liftParticle ~= nil then
|
|
ParticleManager:DestroyParticle(self.liftParticle, false)
|
|
ParticleManager:ReleaseParticleIndex(self.liftParticle)
|
|
self.liftParticle = nil
|
|
end
|
|
self.liftParticle = ParticleManager:CreateParticle(
|
|
"particles/econ/items/rubick/rubick_force_ambient/rubick_telekinesis_force_cube.vpcf",
|
|
PATTACH_ROOTBONE_FOLLOW,
|
|
self:GetParent()
|
|
)
|
|
self:AddParticle(
|
|
self.liftParticle,
|
|
false,
|
|
false,
|
|
-1,
|
|
false,
|
|
false
|
|
)
|
|
end
|
|
function modifier_rubick_telekinesis_lift.prototype.OnIntervalThink(self)
|
|
if not IsServer() then
|
|
return
|
|
end
|
|
local parent = self:GetParent()
|
|
if self:CheckAndBreakOnTeleport(parent) then
|
|
return
|
|
end
|
|
if not self:IsInfiniteAllyAirActive() then
|
|
self:StartIntervalThink(-1)
|
|
return
|
|
end
|
|
local cur = parent:GetAbsOrigin()
|
|
self.apex_pos = Vector(cur.x, cur.y, self.apex_z)
|
|
self:RefreshLiftParticle()
|
|
end
|
|
function modifier_rubick_telekinesis_lift.prototype.OnDestroy(self)
|
|
if IsClient() then
|
|
return
|
|
end
|
|
local ability = self:GetAbility()
|
|
if ability ~= nil then
|
|
ability:clearActiveTargetIfMatch(self:GetParent())
|
|
end
|
|
if self.liftParticle ~= nil then
|
|
ParticleManager:DestroyParticle(self.liftParticle, false)
|
|
ParticleManager:ReleaseParticleIndex(self.liftParticle)
|
|
self.liftParticle = nil
|
|
end
|
|
self:GetParent():RemoveHorizontalMotionController(self)
|
|
self:GetParent():RemoveVerticalMotionController(self)
|
|
end
|
|
function modifier_rubick_telekinesis_lift.prototype.GetCurrentTargetPosition(self, me)
|
|
local t = self:GetElapsedTime()
|
|
local function lerp(____, a, b, p)
|
|
return a + (b - a) * p
|
|
end
|
|
if t < self.lift_time then
|
|
local p = t / self.lift_time
|
|
return Vector(
|
|
lerp(nil, self.start_pos.x, self.apex_pos.x, p),
|
|
lerp(nil, self.start_pos.y, self.apex_pos.y, p),
|
|
lerp(nil, self.start_pos.z, self.apex_pos.z, p)
|
|
)
|
|
end
|
|
if self:IsInfiniteAllyAirActive() then
|
|
local cur = me:GetAbsOrigin()
|
|
return Vector(cur.x, cur.y, self.apex_z)
|
|
end
|
|
if t < self.lift_time + self.air_time then
|
|
if self:IsAlly() then
|
|
local cur = me:GetAbsOrigin()
|
|
return Vector(cur.x, cur.y, self.apex_z)
|
|
end
|
|
return self.apex_pos
|
|
end
|
|
local drop_t = t - self.lift_time - self.air_time
|
|
if self:IsAlly() and not self.ally_drop_start_pos then
|
|
self.ally_drop_start_pos = me:GetAbsOrigin()
|
|
self.land_pos = GetGroundPosition(self.ally_drop_start_pos, me)
|
|
end
|
|
local drop_from = self:IsAlly() and self.ally_drop_start_pos and self.ally_drop_start_pos or self.apex_pos
|
|
local raw_p = math.max(
|
|
0,
|
|
math.min(1, drop_t / self.drop_time)
|
|
)
|
|
local p = raw_p * raw_p * raw_p * raw_p * raw_p
|
|
return Vector(
|
|
lerp(nil, drop_from.x, self.land_pos.x, p),
|
|
lerp(nil, drop_from.y, self.land_pos.y, p),
|
|
lerp(nil, drop_from.z, self.land_pos.z, p)
|
|
)
|
|
end
|
|
function modifier_rubick_telekinesis_lift.prototype.UpdateHorizontalMotion(self, me, dt)
|
|
if self:CheckAndBreakOnTeleport(me) then
|
|
return
|
|
end
|
|
local t = self:GetElapsedTime()
|
|
if self:IsAlly() and t >= self.lift_time and (self:IsInfiniteAllyAirActive() or t < self.lift_time + self.air_time) then
|
|
if self.ally_drop_started then
|
|
me:SetOrigin(self:GetCurrentTargetPosition(me))
|
|
return
|
|
end
|
|
local cur = me:GetAbsOrigin()
|
|
local center = Vector(self.apex_pos.x, self.apex_pos.y, 0)
|
|
local cur_2d = Vector(cur.x, cur.y, 0)
|
|
local dist = (cur_2d - center):Length2D()
|
|
if dist > self.ally_air_max_radius then
|
|
local from_center = cur_2d - center
|
|
local len = from_center:Length2D()
|
|
if len > 0.01 then
|
|
local norm = from_center:Normalized()
|
|
local clamped = Vector(center.x + norm.x * self.ally_air_max_radius, center.y + norm.y * self.ally_air_max_radius, 0)
|
|
me:SetOrigin(Vector(clamped.x, clamped.y, self.apex_z))
|
|
else
|
|
me:SetOrigin(Vector(cur.x, cur.y, self.apex_z))
|
|
end
|
|
return
|
|
end
|
|
if me:IsMoving() then
|
|
local fwd = me:GetForwardVector()
|
|
local dir = Vector(fwd.x, fwd.y, 0)
|
|
local len = dir:Length2D()
|
|
if len > 0.01 then
|
|
local norm = dir:Normalized()
|
|
local from_center = cur_2d - center
|
|
local dot = norm.x * from_center.x + norm.y * from_center.y
|
|
local moving_away = dot > 0
|
|
if self:IsInfiniteAllyAirActive() and moving_away and dist >= self.ally_air_max_radius - 8 then
|
|
self:StartForcedDrop(me)
|
|
return
|
|
end
|
|
local speed_mult = 1
|
|
if moving_away and dist > self.ally_air_radius then
|
|
local range = self.ally_air_max_radius - self.ally_air_radius
|
|
speed_mult = math.max(0, 1 - (dist - self.ally_air_radius) / range)
|
|
end
|
|
local speed = me:GetMoveSpeedModifier(
|
|
me:GetBaseMoveSpeed(),
|
|
false
|
|
) * self.ally_air_move_speed_mult * dt * speed_mult
|
|
local new_x = cur.x + norm.x * speed
|
|
local new_y = cur.y + norm.y * speed
|
|
local new_dist = (Vector(new_x, new_y, 0) - center):Length2D()
|
|
if self:IsInfiniteAllyAirActive() and new_dist > self.ally_air_max_radius then
|
|
self:StartForcedDrop(me)
|
|
return
|
|
end
|
|
if new_dist > self.ally_air_max_radius then
|
|
local scale = self.ally_air_max_radius / new_dist
|
|
new_x = center.x + (new_x - center.x) * scale
|
|
new_y = center.y + (new_y - center.y) * scale
|
|
end
|
|
me:SetOrigin(Vector(new_x, new_y, self.apex_z))
|
|
return
|
|
end
|
|
end
|
|
me:SetOrigin(Vector(cur.x, cur.y, self.apex_z))
|
|
return
|
|
end
|
|
me:SetOrigin(self:GetCurrentTargetPosition(me))
|
|
end
|
|
function modifier_rubick_telekinesis_lift.prototype.UpdateVerticalMotion(self, me, dt)
|
|
if self:CheckAndBreakOnTeleport(me) then
|
|
return
|
|
end
|
|
local t = self:GetElapsedTime()
|
|
if self:IsAlly() and t >= self.lift_time and (self:IsInfiniteAllyAirActive() or t < self.lift_time + self.air_time) then
|
|
if self.ally_drop_started then
|
|
me:SetOrigin(self:GetCurrentTargetPosition(me))
|
|
return
|
|
end
|
|
local cur = me:GetAbsOrigin()
|
|
me:SetOrigin(Vector(cur.x, cur.y, self.apex_z))
|
|
return
|
|
end
|
|
me:SetOrigin(self:GetCurrentTargetPosition(me))
|
|
end
|
|
function modifier_rubick_telekinesis_lift.prototype.OnHorizontalMotionInterrupted(self)
|
|
if not IsServer() then
|
|
return
|
|
end
|
|
self:EndTelekinesisEffect(
|
|
self:GetParent(),
|
|
true
|
|
)
|
|
end
|
|
function modifier_rubick_telekinesis_lift.prototype.OnVerticalMotionInterrupted(self)
|
|
if not IsServer() then
|
|
return
|
|
end
|
|
self:EndTelekinesisEffect(
|
|
self:GetParent(),
|
|
true
|
|
)
|
|
end
|
|
function modifier_rubick_telekinesis_lift.prototype.DeclareFunctions(self)
|
|
return {MODIFIER_PROPERTY_OVERRIDE_ANIMATION, MODIFIER_PROPERTY_OVERRIDE_ANIMATION_RATE}
|
|
end
|
|
function modifier_rubick_telekinesis_lift.prototype.GetOverrideAnimation(self)
|
|
if self:GetParent():GetTeamNumber() == self:GetCaster():GetTeamNumber() then
|
|
return ACT_IDLE
|
|
end
|
|
return ACT_DOTA_FLAIL
|
|
end
|
|
function modifier_rubick_telekinesis_lift.prototype.GetOverrideAnimationRate(self)
|
|
return 0.75
|
|
end
|
|
modifier_rubick_telekinesis_lift.TELEPORT_JUMP_THRESHOLD = 400
|
|
modifier_rubick_telekinesis_lift = __TS__Decorate(
|
|
modifier_rubick_telekinesis_lift,
|
|
modifier_rubick_telekinesis_lift,
|
|
{registerModifier(nil)},
|
|
{kind = "class", name = "modifier_rubick_telekinesis_lift"}
|
|
)
|
|
____exports.modifier_rubick_telekinesis_lift = modifier_rubick_telekinesis_lift
|
|
____exports.modifier_rubick_telekinesis_land = __TS__Class()
|
|
local modifier_rubick_telekinesis_land = ____exports.modifier_rubick_telekinesis_land
|
|
modifier_rubick_telekinesis_land.name = "modifier_rubick_telekinesis_land"
|
|
modifier_rubick_telekinesis_land.____file_path = "scripts/vscripts/abilities/heroes/rubick/ability_rubick_telekinesis_custom.lua"
|
|
__TS__ClassExtends(modifier_rubick_telekinesis_land, BaseModifier)
|
|
function modifier_rubick_telekinesis_land.prototype.____constructor(self, ...)
|
|
BaseModifier.prototype.____constructor(self, ...)
|
|
self.land_radius = 375
|
|
self.land_stun = 1
|
|
self.land_damage = 0
|
|
self.ally_heal_per_second = 0
|
|
self.ally_attack_range_bonus = 0
|
|
self.ally_attack_speed_bonus = 0
|
|
self.ally_bonus_magic_damage = 0
|
|
self.ally_bonus_magic_damage_per_int = 0
|
|
self.ally_infinite_air_with_scepter = false
|
|
self.nextAttachParticleRefreshTime = 0
|
|
self.suppress_land_effect = false
|
|
self.last_pos = nil
|
|
end
|
|
function modifier_rubick_telekinesis_land.prototype.GetEffectName(self)
|
|
return ""
|
|
end
|
|
function modifier_rubick_telekinesis_land.prototype.GetEffectAttachType(self)
|
|
return PATTACH_ABSORIGIN_FOLLOW
|
|
end
|
|
function modifier_rubick_telekinesis_land.prototype.IsHidden(self)
|
|
return true
|
|
end
|
|
function modifier_rubick_telekinesis_land.prototype.IsPurgable(self)
|
|
return false
|
|
end
|
|
function modifier_rubick_telekinesis_land.prototype.IsAlly(self)
|
|
local caster = self:GetCaster()
|
|
return not not (caster and self:GetParent():GetTeamNumber() == caster:GetTeamNumber())
|
|
end
|
|
function modifier_rubick_telekinesis_land.prototype.CheckState(self)
|
|
if not self:IsAlly() then
|
|
return {[MODIFIER_STATE_STUNNED] = true}
|
|
end
|
|
return {[MODIFIER_STATE_INVULNERABLE] = true, [MODIFIER_STATE_FLYING] = true}
|
|
end
|
|
function modifier_rubick_telekinesis_land.prototype.OnCreated(self, params)
|
|
self.land_radius = params.land_radius or 375
|
|
self.land_stun = params.land_stun or 1
|
|
self.land_damage = params.land_damage or 0
|
|
self.ally_heal_per_second = params.ally_heal_per_second or 0
|
|
self.ally_attack_range_bonus = params.ally_attack_range_bonus or 0
|
|
self.ally_attack_speed_bonus = params.ally_attack_speed_bonus or 0
|
|
self.ally_bonus_magic_damage = params.ally_bonus_magic_damage or 0
|
|
self.ally_bonus_magic_damage_per_int = params.ally_bonus_magic_damage_per_int or 0
|
|
self.ally_infinite_air_with_scepter = (params.ally_infinite_air_with_scepter or 0) == 1
|
|
if IsServer() then
|
|
self.last_pos = self:GetParent():GetAbsOrigin()
|
|
self:RefreshAttachParticle()
|
|
if self.ally_infinite_air_with_scepter then
|
|
self.nextAttachParticleRefreshTime = GameRules:GetGameTime() + 2
|
|
end
|
|
end
|
|
if IsServer() and self:IsAlly() and (self.ally_heal_per_second > 0 or self.ally_infinite_air_with_scepter) then
|
|
self:StartIntervalThink(0.2)
|
|
end
|
|
end
|
|
function modifier_rubick_telekinesis_land.prototype.SetSuppressLandEffect(self, value)
|
|
self.suppress_land_effect = value
|
|
end
|
|
function modifier_rubick_telekinesis_land.prototype.CheckAndBreakOnTeleport(self)
|
|
if not IsServer() then
|
|
return false
|
|
end
|
|
local parent = self:GetParent()
|
|
local liftMod = parent:FindModifierByName(____exports.modifier_rubick_telekinesis_lift.name)
|
|
if liftMod and liftMod:CheckAndBreakOnTeleport(parent) then
|
|
return true
|
|
end
|
|
local caster = self:GetCaster()
|
|
local ability = self:GetAbility()
|
|
if ability and (not caster or caster:IsNull() or not caster:IsAlive() or not ____exports.ability_rubick_telekinesis_custom:isCasterWithinBreakRange(ability, caster, parent)) then
|
|
self:SetSuppressLandEffect(true)
|
|
parent:RemoveModifierByName(____exports.modifier_rubick_telekinesis_lift.name)
|
|
parent:RemoveModifierByName(____exports.modifier_rubick_telekinesis_land.name)
|
|
return true
|
|
end
|
|
local cur = parent:GetAbsOrigin()
|
|
if self.last_pos then
|
|
local jump = (Vector(cur.x, cur.y, 0) - Vector(self.last_pos.x, self.last_pos.y, 0)):Length2D()
|
|
if jump >= ____exports.modifier_rubick_telekinesis_land.TELEPORT_JUMP_THRESHOLD then
|
|
self:SetSuppressLandEffect(true)
|
|
parent:RemoveModifierByName(____exports.modifier_rubick_telekinesis_lift.name)
|
|
parent:RemoveModifierByName(____exports.modifier_rubick_telekinesis_land.name)
|
|
return true
|
|
end
|
|
end
|
|
self.last_pos = cur
|
|
return false
|
|
end
|
|
function modifier_rubick_telekinesis_land.prototype.RefreshAttachParticle(self)
|
|
if not IsServer() then
|
|
return
|
|
end
|
|
if self.attachParticle ~= nil then
|
|
ParticleManager:DestroyParticle(self.attachParticle, false)
|
|
ParticleManager:ReleaseParticleIndex(self.attachParticle)
|
|
self.attachParticle = nil
|
|
end
|
|
self.attachParticle = ParticleManager:CreateParticle(
|
|
"particles/econ/items/rubick/rubick_puppet_master/rubick_telekinesis_puppet_string_attach.vpcf",
|
|
PATTACH_ABSORIGIN_FOLLOW,
|
|
self:GetParent()
|
|
)
|
|
self:AddParticle(
|
|
self.attachParticle,
|
|
false,
|
|
false,
|
|
-1,
|
|
false,
|
|
false
|
|
)
|
|
end
|
|
function modifier_rubick_telekinesis_land.prototype.DeclareFunctions(self)
|
|
local funcs = {}
|
|
if self:IsAlly() then
|
|
funcs[#funcs + 1] = MODIFIER_PROPERTY_ATTACK_RANGE_BONUS
|
|
funcs[#funcs + 1] = MODIFIER_PROPERTY_ATTACKSPEED_BONUS_CONSTANT
|
|
funcs[#funcs + 1] = MODIFIER_PROPERTY_PROCATTACK_BONUS_DAMAGE_MAGICAL
|
|
funcs[#funcs + 1] = MODIFIER_PROPERTY_PROJECTILE_NAME
|
|
funcs[#funcs + 1] = MODIFIER_EVENT_ON_ATTACK_LANDED
|
|
end
|
|
return funcs
|
|
end
|
|
function modifier_rubick_telekinesis_land.prototype.GetModifierAttackRangeBonus(self)
|
|
if not self:IsAlly() or not self:GetParent():IsRangedAttacker() then
|
|
return 0
|
|
end
|
|
return self.ally_attack_range_bonus
|
|
end
|
|
function modifier_rubick_telekinesis_land.prototype.GetModifierAttackSpeedBonus_Constant(self)
|
|
if not self:IsAlly() then
|
|
return 0
|
|
end
|
|
return self.ally_attack_speed_bonus
|
|
end
|
|
function modifier_rubick_telekinesis_land.prototype.GetBonusMagicDamage(self)
|
|
if not self:IsAlly() then
|
|
return 0
|
|
end
|
|
local caster = self:GetCaster()
|
|
if not caster or not caster:IsHero() then
|
|
return self.ally_bonus_magic_damage
|
|
end
|
|
local int = caster:GetIntellect(true)
|
|
return self.ally_bonus_magic_damage + self.ally_bonus_magic_damage_per_int * int
|
|
end
|
|
function modifier_rubick_telekinesis_land.prototype.GetModifierProcAttack_BonusDamage_Magical(self)
|
|
if not IsServer() or not self:IsAlly() then
|
|
return 0
|
|
end
|
|
local damage = self:GetBonusMagicDamage()
|
|
return damage <= 0 and 0 or damage
|
|
end
|
|
function modifier_rubick_telekinesis_land.prototype.OnAttackLanded(self, event)
|
|
if not IsServer() or not self:IsAlly() then
|
|
return
|
|
end
|
|
if event.attacker ~= self:GetParent() then
|
|
return
|
|
end
|
|
local damage = self:GetBonusMagicDamage()
|
|
if damage <= 0 then
|
|
return
|
|
end
|
|
local target = event.target
|
|
if not target or not target:IsAlive() then
|
|
return
|
|
end
|
|
SendOverheadEventMessage(
|
|
nil,
|
|
OVERHEAD_ALERT_BONUS_SPELL_DAMAGE,
|
|
target,
|
|
math.floor(damage),
|
|
nil
|
|
)
|
|
end
|
|
function modifier_rubick_telekinesis_land.prototype.OnIntervalThink(self)
|
|
if not IsServer() then
|
|
return
|
|
end
|
|
if self:CheckAndBreakOnTeleport() then
|
|
return
|
|
end
|
|
local parent = self:GetParent()
|
|
local ability = self:GetAbility()
|
|
if not parent:IsAlive() then
|
|
return
|
|
end
|
|
if self.ally_infinite_air_with_scepter then
|
|
local now = GameRules:GetGameTime()
|
|
if now >= self.nextAttachParticleRefreshTime then
|
|
self:RefreshAttachParticle()
|
|
self.nextAttachParticleRefreshTime = now + 2
|
|
end
|
|
end
|
|
if not self:IsAlly() or self.ally_heal_per_second <= 0 or not ability then
|
|
return
|
|
end
|
|
HealWithBattlePass(
|
|
nil,
|
|
parent,
|
|
self.ally_heal_per_second,
|
|
ability,
|
|
self:GetCaster()
|
|
)
|
|
SendOverheadEventMessage(
|
|
nil,
|
|
OVERHEAD_ALERT_HEAL,
|
|
parent,
|
|
self.ally_heal_per_second,
|
|
nil
|
|
)
|
|
end
|
|
function modifier_rubick_telekinesis_land.prototype.GetModifierProjectileName(self)
|
|
return "particles/units/heroes/hero_rubick/rubick_base_attack.vpcf"
|
|
end
|
|
function modifier_rubick_telekinesis_land.prototype.OnDestroy(self)
|
|
if not IsServer() then
|
|
return
|
|
end
|
|
if self.attachParticle ~= nil then
|
|
ParticleManager:DestroyParticle(self.attachParticle, false)
|
|
ParticleManager:ReleaseParticleIndex(self.attachParticle)
|
|
self.attachParticle = nil
|
|
end
|
|
local parent = self:GetParent()
|
|
local caster = self:GetCaster()
|
|
local ability = self:GetAbility()
|
|
if not caster or not ability or not parent:IsAlive() then
|
|
return
|
|
end
|
|
if self.suppress_land_effect then
|
|
return
|
|
end
|
|
local land_pos = parent:GetAbsOrigin()
|
|
if not self:IsAlly() then
|
|
EmitSoundOnLocationWithCaster(land_pos, "Hero_Rubick.Telekinesis.Stun", caster)
|
|
local particle = ParticleManager:CreateParticle("particles/econ/items/rubick/rubick_force_gold_ambient/rubick_telekinesis_land_force_gold.vpcf", PATTACH_WORLDORIGIN, nil)
|
|
ParticleManager:SetParticleControl(particle, 0, land_pos)
|
|
ParticleManager:SetParticleControl(
|
|
particle,
|
|
1,
|
|
Vector(self.land_radius, 0, 0)
|
|
)
|
|
ParticleManager:ReleaseParticleIndex(particle)
|
|
local land_damage = ability:GetSpecialValueFor("land_damage")
|
|
local land_stun = ability:GetSpecialValueFor("land_stun_duration")
|
|
local land_radius = ability:GetSpecialValueFor("land_stun_radius")
|
|
local enemies = FindUnitsInRadius(
|
|
caster:GetTeamNumber(),
|
|
land_pos,
|
|
nil,
|
|
land_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
|
|
ApplyDamage({
|
|
victim = enemy,
|
|
attacker = caster,
|
|
damage = land_damage,
|
|
damage_type = DAMAGE_TYPE_MAGICAL,
|
|
ability = ability
|
|
})
|
|
enemy:AddNewModifier(caster, ability, "modifier_stunned", {duration = land_stun})
|
|
end
|
|
end
|
|
end
|
|
modifier_rubick_telekinesis_land.TELEPORT_JUMP_THRESHOLD = 400
|
|
modifier_rubick_telekinesis_land = __TS__Decorate(
|
|
modifier_rubick_telekinesis_land,
|
|
modifier_rubick_telekinesis_land,
|
|
{registerModifier(nil)},
|
|
{kind = "class", name = "modifier_rubick_telekinesis_land"}
|
|
)
|
|
____exports.modifier_rubick_telekinesis_land = modifier_rubick_telekinesis_land
|
|
return ____exports
|