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

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