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

255 lines
10 KiB
Lua

local ____lualib = require("lualib_bundle")
local Set = ____lualib.Set
local __TS__New = ____lualib.__TS__New
local Map = ____lualib.Map
local __TS__Class = ____lualib.__TS__Class
local __TS__ClassExtends = ____lualib.__TS__ClassExtends
local __TS__ArrayIncludes = ____lualib.__TS__ArrayIncludes
local __TS__Decorate = ____lualib.__TS__Decorate
local ____exports = {}
local isHeroAbilityStealable, getLastUsedAbilityFromUnit, UNSTEALABLE_NAMES
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
function isHeroAbilityStealable(self, hero, abilityName)
if not abilityName then
return false
end
if UNSTEALABLE_NAMES:has(abilityName) then
return false
end
local ability = hero:FindAbilityByName(abilityName)
if not ability or ability:IsNull() then
return false
end
if ability:IsItem() or ability:IsHidden() then
return false
end
if ability:IsPassive() then
return false
end
if ability:GetLevel() <= 0 then
return false
end
return true
end
function getLastUsedAbilityFromUnit(self, unit)
local best = nil
do
local i = 0
while i < 16 do
do
local ab = unit:GetAbilityByIndex(i)
if not ab or ab:IsNull() then
goto __continue13
end
local name = ab:GetAbilityName()
if not isHeroAbilityStealable(nil, unit, name) then
goto __continue13
end
local cd = ab:GetCooldownTimeRemaining()
if cd > 0 and (not best or cd < best.cd) then
best = {name = name, cd = cd}
end
end
::__continue13::
i = i + 1
end
end
return best and best.name or ""
end
local STOLEN_SLOT_1 = 3
local STOLEN_SLOT_2 = 4
local EMPTY_SLOTS = {"rubick_empty1", "rubick_empty2"}
UNSTEALABLE_NAMES = __TS__New(Set, {"ability_rubick_spellsteal_custom"})
local lastAbilityByUnit = __TS__New(Map)
local function getStealableAbilityFromTarget(self, target)
if not target:IsRealHero() then
return ""
end
local heroTarget = target
local lastUsedName = lastAbilityByUnit:get(target:entindex()) or ""
if isHeroAbilityStealable(nil, heroTarget, lastUsedName) then
return lastUsedName
end
return getLastUsedAbilityFromUnit(nil, heroTarget)
end
if IsServer() then
ListenToGameEvent(
"dota_player_used_ability",
function(event)
local name = event.abilityname
local playerId = event.PlayerID
if not name or playerId == nil then
return
end
local ____opt_2 = PlayerResource:GetPlayer(playerId)
local hero = ____opt_2 and ____opt_2:GetAssignedHero()
if hero and not hero:IsNull() and hero:IsRealHero() and isHeroAbilityStealable(nil, hero, name) then
lastAbilityByUnit:set(
hero:entindex(),
name
)
print((((("[SpellSteal] dota_player_used_ability: " .. hero:GetUnitName()) .. " (") .. tostring(hero:entindex())) .. ") cast ") .. name)
end
end,
nil
)
end
____exports.ability_rubick_spellsteal_custom = __TS__Class()
local ability_rubick_spellsteal_custom = ____exports.ability_rubick_spellsteal_custom
ability_rubick_spellsteal_custom.name = "ability_rubick_spellsteal_custom"
ability_rubick_spellsteal_custom.____file_path = "scripts/vscripts/abilities/heroes/rubick/ability_rubick_spellsteal_custom.lua"
__TS__ClassExtends(ability_rubick_spellsteal_custom, BaseAbility)
function ability_rubick_spellsteal_custom.prototype.Precache(self, context)
PrecacheResource("particle", "particles/econ/items/rubick/rubick_arcana/rubick_arc_loadout_spell_steal.vpcf", context)
PrecacheResource("soundfile", "soundevents/game_sounds_heroes/game_sounds_rubick.vsndevts", context)
end
function ability_rubick_spellsteal_custom.prototype.CastFilterResultTarget(self, target)
local caster = self:GetCaster()
if target == caster then
return UF_FAIL_CUSTOM
end
local result = UnitFilter(
target,
DOTA_UNIT_TARGET_TEAM_FRIENDLY,
DOTA_UNIT_TARGET_HERO,
DOTA_UNIT_TARGET_FLAG_NONE,
caster:GetTeamNumber()
)
if result ~= UF_SUCCESS then
return result
end
if not target:IsRealHero() then
return UF_FAIL_CUSTOM
end
local lastAbilityName = getStealableAbilityFromTarget(nil, target)
if not lastAbilityName then
return UF_FAIL_CUSTOM
end
if UNSTEALABLE_NAMES:has(lastAbilityName) then
return UF_FAIL_CUSTOM
end
return UF_SUCCESS
end
function ability_rubick_spellsteal_custom.prototype.GetCustomCastErrorTarget(self, target)
local caster = self:GetCaster()
if target == caster then
return "#dota_hud_error_rubick_spellsteal_self"
end
local lastAbilityName = getStealableAbilityFromTarget(nil, target)
if not lastAbilityName then
return "#dota_hud_error_rubick_spellsteal_no_ability"
end
if UNSTEALABLE_NAMES:has(lastAbilityName) then
return "#dota_hud_error_rubick_spellsteal_unstealable"
end
return ""
end
function ability_rubick_spellsteal_custom.prototype.OnSpellStart(self)
if not IsServer() then
return
end
local ability = self
local caster = ability:GetCaster()
local target = ability:GetCursorTarget()
if not caster or not target or target:IsNull() or target == caster then
return
end
if not target:IsRealHero() then
EmitSoundOn("Hero_Rubick.SpellSteal.Fail", caster)
return
end
local duration = -1
print(((((("[SpellSteal] OnSpellStart: caster=" .. caster:GetUnitName()) .. ", target=") .. target:GetUnitName()) .. " (") .. tostring(target:entindex())) .. ")")
local lastAbilityName = getStealableAbilityFromTarget(nil, target)
if not lastAbilityName or UNSTEALABLE_NAMES:has(lastAbilityName) then
EmitSoundOn("Hero_Rubick.SpellSteal.Fail", caster)
return
end
print("[SpellSteal] stealing: " .. lastAbilityName)
local targetAbility = target:FindAbilityByName(lastAbilityName)
local stolenLevel = targetAbility and targetAbility:GetLevel() or 1
local hasAghs = caster:HasScepter()
local slot1Ability = caster:GetAbilityByIndex(STOLEN_SLOT_1)
local slot1Name = slot1Ability and not slot1Ability:IsNull() and slot1Ability:GetAbilityName() or nil
local slot2Ability = caster:GetAbilityByIndex(STOLEN_SLOT_2)
local slot2Name = slot2Ability and not slot2Ability:IsNull() and slot2Ability:GetAbilityName() or nil
if lastAbilityName ~= slot1Name and lastAbilityName ~= slot2Name then
local newAbility = caster:AddAbility(lastAbilityName)
if newAbility and not newAbility:IsNull() then
newAbility:SetLevel(math.min(
stolenLevel,
newAbility:GetMaxLevel()
))
newAbility:SetStolen(true)
if slot1Name then
caster:SwapAbilities(slot1Name, lastAbilityName, false, true)
if hasAghs and not __TS__ArrayIncludes(EMPTY_SLOTS, slot1Name) then
if slot2Name then
caster:SwapAbilities(slot2Name, slot1Name, false, true)
caster:RemoveAbility(slot2Name)
end
else
caster:RemoveAbility(slot1Name)
end
end
end
print((((("[SpellSteal] SUCCESS: stole " .. lastAbilityName) .. " (lvl ") .. tostring(stolenLevel)) .. ")") .. (hasAghs and " [Aghs]" or ""))
else
print(("[SpellSteal] already have " .. lastAbilityName) .. ", skipping swap")
end
caster:AddNewModifier(caster, ability, ____exports.modifier_rubick_spellsteal_stolen.name, {duration = duration})
local arcanaParticle = "particles/econ/items/rubick/rubick_arcana/rubick_arc_loadout_spell_steal.vpcf"
local particle = ParticleManager:CreateParticle(arcanaParticle, PATTACH_WORLDORIGIN, nil)
ParticleManager:SetParticleControlEnt(
particle,
0,
target,
PATTACH_POINT_FOLLOW,
"attach_hitloc",
Vector(0, 0, 0),
true
)
ParticleManager:SetParticleControlEnt(
particle,
1,
caster,
PATTACH_POINT_FOLLOW,
"attach_hitloc",
Vector(0, 0, 0),
true
)
ParticleManager:ReleaseParticleIndex(particle)
EmitSoundOn("Hero_Rubick.SpellSteal.Target", target)
EmitSoundOn("Hero_Rubick.SpellSteal", caster)
end
ability_rubick_spellsteal_custom = __TS__Decorate(
ability_rubick_spellsteal_custom,
ability_rubick_spellsteal_custom,
{registerAbility(nil)},
{kind = "class", name = "ability_rubick_spellsteal_custom"}
)
____exports.ability_rubick_spellsteal_custom = ability_rubick_spellsteal_custom
____exports.modifier_rubick_spellsteal_stolen = __TS__Class()
local modifier_rubick_spellsteal_stolen = ____exports.modifier_rubick_spellsteal_stolen
modifier_rubick_spellsteal_stolen.name = "modifier_rubick_spellsteal_stolen"
modifier_rubick_spellsteal_stolen.____file_path = "scripts/vscripts/abilities/heroes/rubick/ability_rubick_spellsteal_custom.lua"
__TS__ClassExtends(modifier_rubick_spellsteal_stolen, BaseModifier)
function modifier_rubick_spellsteal_stolen.prototype.IsHidden(self)
return true
end
function modifier_rubick_spellsteal_stolen.prototype.IsPurgable(self)
return false
end
modifier_rubick_spellsteal_stolen = __TS__Decorate(
modifier_rubick_spellsteal_stolen,
modifier_rubick_spellsteal_stolen,
{registerModifier(nil)},
{kind = "class", name = "modifier_rubick_spellsteal_stolen"}
)
____exports.modifier_rubick_spellsteal_stolen = modifier_rubick_spellsteal_stolen
return ____exports