Files
2026-05-29 15:11:31 +07:00

616 lines
23 KiB
Lua
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
local ____lualib = require("lualib_bundle")
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 ____fish_basic = require("abilities.fish_basic")
local modifier_fish_basic = ____fish_basic.modifier_fish_basic
local ____dota_ts_adapter = require("lib.dota_ts_adapter")
local BaseItem = ____dota_ts_adapter.BaseItem
local BaseModifier = ____dota_ts_adapter.BaseModifier
local registerAbility = ____dota_ts_adapter.registerAbility
local registerModifier = ____dota_ts_adapter.registerModifier
--- Цепь из клиента Dota 2 (кастомный .vpcf без файла в content/particles не появится в игре)
local PARTICLE_MEATHOOK = "particles/units/heroes/hero_pudge/pudge_meathook.vpcf"
--- Юнит рыбы (npc_units_custom) — при успешном вываживании удаляется, дроп летит к герою
local BOMB_UNIT_NAMES = {"npc_bomb"}
local FISH_UNIT_NAMES = {"npc_fish_1", "npc_fish_2"}
local FISH_CATCH_ITEM_DEFAULT = "item_meat"
--- У Пуджа есть attach_weapon_chain_rt; у большинства героев — attach_attack1
local function getMeathookAttachName(self, unit)
local candidates = {"attach_attack1", "attach_attack2"}
for ____, name in ipairs(candidates) do
if unit:ScriptLookupAttachment(name) >= 0 then
return name
end
end
return "attach_hitloc"
end
____exports.item_fishing_rod = __TS__Class()
local item_fishing_rod = ____exports.item_fishing_rod
item_fishing_rod.name = "item_fishing_rod"
item_fishing_rod.____file_path = "scripts/vscripts/items/default_items/item_fishing_rod.lua"
__TS__ClassExtends(item_fishing_rod, BaseItem)
function item_fishing_rod.prototype.____constructor(self, ...)
BaseItem.prototype.____constructor(self, ...)
self.vHookOffset = Vector(0, 0, 96)
self.bRetracting = false
self.vTargetPosition = Vector(0, 0, 0)
self.vStartPosition = Vector(0, 0, 0)
end
function item_fishing_rod.prototype.GetAbilityDamageType(self)
return DAMAGE_TYPE_PHYSICAL
end
function item_fishing_rod.prototype.GetAbilityDamage(self)
return self:GetSpecialValueFor("Damage")
end
function item_fishing_rod.prototype.Precache(self, context)
PrecacheResource("particle", PARTICLE_MEATHOOK, context)
PrecacheResource("particle", "particles/units/heroes/hero_pudge/pudge_meathook_impact.vpcf", context)
end
function item_fishing_rod.prototype.GetCastRange(self, location, target)
return self:GetSpecialValueFor("hook_distance")
end
function item_fishing_rod.prototype.OnAbilityPhaseStart(self)
self:GetCaster():StartGesture(ACT_DOTA_ATTACK)
return true
end
function item_fishing_rod.prototype.OnAbilityPhaseInterrupted(self)
self:GetCaster():RemoveGesture(ACT_DOTA_ATTACK)
return true
end
function item_fishing_rod.prototype.clearHookedVictimPull(self)
if self.hookedVictimEntIndex == nil then
return
end
local ent = EntIndexToHScript(self.hookedVictimEntIndex)
self.hookedVictimEntIndex = nil
if ent and IsValidEntity(ent) and ent:IsBaseNPC() then
ent:RemoveModifierByName(____exports.modifier_item_fishing_rod_hook_pull.name)
end
end
function item_fishing_rod.prototype.releaseCasterMovementLock(self, caster)
caster:RemoveModifierByName(____exports.modifier_item_fishing_rod_caster_root.name)
end
function item_fishing_rod.prototype.getFishCatchItemName(self)
local raw = self:GetAbilityKeyValues()
local ____opt_result_4
if raw ~= nil then
____opt_result_4 = raw.AbilityValues
end
local ____opt_result_5
if ____opt_result_4 ~= nil then
____opt_result_5 = ____opt_result_4.fish_catch_item
end
local ____opt_result_5_9 = ____opt_result_5
if ____opt_result_5_9 == nil then
local ____opt_result_8
if raw ~= nil then
____opt_result_8 = raw.fish_catch_item
end
____opt_result_5_9 = ____opt_result_8
end
local name = ____opt_result_5_9
if type(name) == "string" and #name > 0 then
return name
end
return FISH_CATCH_ITEM_DEFAULT
end
function item_fishing_rod.prototype.trySpawnFishCatchReward(self, caster, caughtEntIndex)
if caughtEntIndex == nil then
return
end
local ent = EntIndexToHScript(caughtEntIndex)
if not ent or not IsValidEntity(ent) or not ent:IsBaseNPC() then
return
end
local unit = ent
local parent = self:GetCaster()
if not caster then
return
end
if __TS__ArrayIncludes(
BOMB_UNIT_NAMES,
unit:GetUnitName()
) and unit:IsAlive() then
local debuffs = {{name = "modifier_bad_cast_debuff", minDuration = 10, maxDuration = 20}, {name = "modifier_blind_debuff", minDuration = 1, maxDuration = 3.5}, {name = "modifier_stunned", minDuration = 1.5, maxDuration = 3.5}}
ApplyDamage({
victim = parent,
attacker = caster,
damage = self:GetCaster():GetMaxHealth() * 0.25,
damage_type = DAMAGE_TYPE_PURE,
ability = self
})
local randomDebuff = debuffs[RandomInt(0, #debuffs - 1) + 1]
local duration = RandomFloat(randomDebuff.minDuration, randomDebuff.maxDuration)
caster:AddNewModifier(caster, self, randomDebuff.name, {duration = duration})
EmitSoundOn("Hero_TemplarAssassin.Trap.Explode", parent)
unit:RemoveModifierByName(modifier_fish_basic.name)
unit:Kill(self, caster)
UTIL_Remove(unit)
return
end
if not __TS__ArrayIncludes(
FISH_UNIT_NAMES,
unit:GetUnitName()
) or not unit:IsAlive() then
return
end
local fromPos = unit:GetAbsOrigin()
unit:RemoveModifierByName(modifier_fish_basic.name)
unit:Kill(self, caster)
UTIL_Remove(unit)
local itemName = self:getFishCatchItemName()
local launchH = self:GetSpecialValueFor("fish_loot_launch_height")
local launchDist = self:GetSpecialValueFor("fish_loot_launch_distance")
local launchDur = self:GetSpecialValueFor("fish_loot_launch_duration")
if launchH <= 0 then
launchH = 100
end
if launchDist <= 0 then
launchDist = 150
end
if launchDur <= 0 then
launchDur = 0.5
end
local item = CreateItem(itemName, nil, nil)
if not item then
return
end
CreateItemOnPositionForLaunch(fromPos, item)
item:LaunchLootInitialHeight(
false,
launchH,
launchDist,
launchDur,
caster:GetAbsOrigin()
)
end
function item_fishing_rod.prototype.OnSpellStart(self)
local caster = self:GetCaster()
self:clearHookedVictimPull()
if not caster:IsInstance(CDOTA_BaseNPC_Hero) then
return
end
local hero = caster
self:releaseCasterMovementLock(hero)
local hookSpeed = self:GetSpecialValueFor("hook_speed")
local hookDistance = self:GetCastRange(
caster:GetOrigin(),
nil
)
local hookWidth = self:GetSpecialValueFor("hook_width")
local attachName = getMeathookAttachName(nil, caster)
local ____opt_10 = caster.clothes
local hook = ____opt_10 and ____opt_10.weapon
if not not hook then
hook:AddEffects(EF_NODRAW)
end
local casterPoint = caster:GetOrigin()
self.vStartPosition = casterPoint
local vDirection = (casterPoint == self:GetCursorPosition() and self:GetCursorPosition() + Vector(1, 1, 0) or self:GetCursorPosition()) - casterPoint
vDirection.z = 0
if caster:HasModifier(____exports.modifier_bad_cast_debuff.name) then
local angle = RandomFloat(0, 2 * math.pi)
local randomDir = Vector(
math.cos(angle),
math.sin(angle),
0
)
vDirection = randomDir * vDirection:Length2D()
end
vDirection = vDirection:Normalized() * hookDistance
self.vTargetPosition = casterPoint + vDirection
local vHookTarget = self.vTargetPosition + Vector(0, 0, 96)
local vKillswitch = Vector(hookDistance / hookSpeed * 2, 0, 0)
EmitSoundOn("Hero_Pudge.AttackHookExtend", caster)
self.nChainParticleFXIndex = ParticleManager:CreateParticle(PARTICLE_MEATHOOK, PATTACH_CUSTOMORIGIN, caster)
ParticleManager:SetParticleAlwaysSimulate(self.nChainParticleFXIndex)
ParticleManager:SetParticleControlEnt(
self.nChainParticleFXIndex,
0,
caster,
PATTACH_POINT_FOLLOW,
attachName,
casterPoint + self.vHookOffset,
true
)
ParticleManager:SetParticleControl(self.nChainParticleFXIndex, 1, vHookTarget)
ParticleManager:SetParticleControl(
self.nChainParticleFXIndex,
2,
Vector(hookSpeed, hookDistance, hookWidth)
)
ParticleManager:SetParticleControl(self.nChainParticleFXIndex, 3, vKillswitch)
ParticleManager:SetParticleControl(
self.nChainParticleFXIndex,
4,
Vector(1, 0, 0)
)
ParticleManager:SetParticleControl(
self.nChainParticleFXIndex,
5,
Vector(0, 0, 0)
)
if hook then
ParticleManager:SetParticleControlEnt(
self.nChainParticleFXIndex,
7,
hook,
PATTACH_CUSTOMORIGIN,
nil,
hook:GetOrigin(),
true
)
end
ProjectileManager:CreateLinearProjectile({
Ability = self,
vSpawnOrigin = casterPoint,
vVelocity = vDirection:Normalized() * hookSpeed,
fDistance = hookDistance,
fStartRadius = hookWidth,
fEndRadius = hookWidth,
Source = caster,
iUnitTargetTeam = DOTA_UNIT_TARGET_TEAM_ENEMY,
iUnitTargetType = bit.bor(DOTA_UNIT_TARGET_HERO, DOTA_UNIT_TARGET_BASIC),
iUnitTargetFlags = DOTA_UNIT_TARGET_FLAG_MAGIC_IMMUNE_ENEMIES
})
self.bRetracting = false
____exports.modifier_item_fishing_rod_caster_root:apply(hero, hero, self, {})
self:SetActivated(false)
end
function item_fishing_rod.prototype.OnProjectileHit(self, target, location)
local caster = self:GetCaster()
local attachName = getMeathookAttachName(nil, caster)
if target == caster then
return false
end
if self.bRetracting == false then
if target then
local isFish = __TS__ArrayIncludes(
FISH_UNIT_NAMES,
target:GetUnitName()
)
EmitSoundOn("Hero_Pudge.AttackHookImpact", target)
if not isFish then
local damage = self:GetAbilityDamage() + self:GetCaster():GetAverageTrueAttackDamage(caster)
ApplyDamage({
victim = target,
attacker = caster,
damage = damage,
damage_type = self:GetAbilityDamageType(),
ability = self
})
end
local nFXIndex = ParticleManager:CreateParticle("particles/econ/items/pudge/pudge_ti6_immortal/pudge_meathook_impact_ti6.vpcf", PATTACH_CUSTOMORIGIN, target)
ParticleManager:SetParticleControlEnt(
nFXIndex,
0,
target,
PATTACH_POINT_FOLLOW,
"attach_hitloc",
caster:GetOrigin(),
true
)
AddFOWViewer(
caster:GetTeamNumber(),
target:GetOrigin(),
self:GetSpecialValueFor("vision_radius"),
self:GetSpecialValueFor("vision_duration"),
false
)
target:InterruptChannel()
self.hookedVictimEntIndex = target:entindex()
____exports.modifier_item_fishing_rod_hook_pull:apply(target, caster, self, {})
if not isFish then
____exports.modifier_item_fishing_rod_debuff:apply(
target,
caster,
self,
{duration = self:GetSpecialValueFor("duration")}
)
end
end
local flPad = caster:GetPaddedCollisionRadius()
local vHookPos = self.vTargetPosition
if target then
vHookPos = target:GetOrigin()
flPad = flPad + target:GetPaddedCollisionRadius()
end
local vVelocity = self.vStartPosition - vHookPos
vVelocity.z = 0
local flDistance = vVelocity:Length2D() - flPad
vVelocity = vVelocity:Normalized() * self:GetSpecialValueFor("hook_speed")
ProjectileManager:CreateLinearProjectile({
Ability = self,
vSpawnOrigin = vHookPos,
vVelocity = vVelocity,
fDistance = flDistance,
Source = caster
})
if target then
ParticleManager:SetParticleControlEnt(
self.nChainParticleFXIndex,
1,
caster,
PATTACH_POINT_FOLLOW,
attachName,
target:GetOrigin() + self.vHookOffset,
true
)
else
ParticleManager:SetParticleControlEnt(
self.nChainParticleFXIndex,
1,
caster,
PATTACH_POINT_FOLLOW,
attachName,
caster:GetOrigin() + self.vHookOffset,
true
)
end
StopSoundOn("Hero_Pudge.AttackHookExtend", caster)
EmitSoundOn("Hero_Pudge.AttackHookRetract", caster)
if caster:IsAlive() then
caster:FadeGesture(ACT_DOTA_ATTACK)
caster:StartGesture(ACT_DOTA_ATTACK)
end
local casterAny = caster
local ____opt_12 = casterAny.SetRageTimeIncrease
if ____opt_12 ~= nil then
____opt_12(casterAny)
end
self.bRetracting = true
else
local caughtEntIndex = self.hookedVictimEntIndex
self:clearHookedVictimPull()
self:releaseCasterMovementLock(caster)
self:trySpawnFishCatchReward(caster, caughtEntIndex)
local ____opt_14 = caster.clothes
local w = ____opt_14 and ____opt_14.weapon
if not not w then
w:RemoveEffects(EF_NODRAW)
end
ParticleManager:DestroyParticle(self.nChainParticleFXIndex, true)
self:SetActivated(true)
EmitSoundOn("Hero_Pudge.AttackHookRetractStop", caster)
end
return true
end
item_fishing_rod = __TS__Decorate(
item_fishing_rod,
item_fishing_rod,
{registerAbility(nil)},
{kind = "class", name = "item_fishing_rod"}
)
____exports.item_fishing_rod = item_fishing_rod
--- Кастер стоит на месте, пока крюк не вернётся (откат цепи завершён)
____exports.modifier_item_fishing_rod_caster_root = __TS__Class()
local modifier_item_fishing_rod_caster_root = ____exports.modifier_item_fishing_rod_caster_root
modifier_item_fishing_rod_caster_root.name = "modifier_item_fishing_rod_caster_root"
modifier_item_fishing_rod_caster_root.____file_path = "scripts/vscripts/items/default_items/item_fishing_rod.lua"
__TS__ClassExtends(modifier_item_fishing_rod_caster_root, BaseModifier)
function modifier_item_fishing_rod_caster_root.prototype.IsHidden(self)
return true
end
function modifier_item_fishing_rod_caster_root.prototype.IsDebuff(self)
return false
end
function modifier_item_fishing_rod_caster_root.prototype.IsPurgable(self)
return false
end
function modifier_item_fishing_rod_caster_root.prototype.RemoveOnDeath(self)
return true
end
function modifier_item_fishing_rod_caster_root.prototype.CheckState(self)
return {[MODIFIER_STATE_ROOTED] = true, [MODIFIER_STATE_SILENCED] = true}
end
modifier_item_fishing_rod_caster_root = __TS__Decorate(
modifier_item_fishing_rod_caster_root,
modifier_item_fishing_rod_caster_root,
{registerModifier(nil)},
{kind = "class", name = "modifier_item_fishing_rod_caster_root"}
)
____exports.modifier_item_fishing_rod_caster_root = modifier_item_fishing_rod_caster_root
--- Притягивание к кастеру на скорости hook_speed (синхронно с откатом цепи)
____exports.modifier_item_fishing_rod_hook_pull = __TS__Class()
local modifier_item_fishing_rod_hook_pull = ____exports.modifier_item_fishing_rod_hook_pull
modifier_item_fishing_rod_hook_pull.name = "modifier_item_fishing_rod_hook_pull"
modifier_item_fishing_rod_hook_pull.____file_path = "scripts/vscripts/items/default_items/item_fishing_rod.lua"
__TS__ClassExtends(modifier_item_fishing_rod_hook_pull, BaseModifier)
function modifier_item_fishing_rod_hook_pull.prototype.____constructor(self, ...)
BaseModifier.prototype.____constructor(self, ...)
self.thinkInterval = 0.03
end
function modifier_item_fishing_rod_hook_pull.prototype.IsHidden(self)
return true
end
function modifier_item_fishing_rod_hook_pull.prototype.IsDebuff(self)
return true
end
function modifier_item_fishing_rod_hook_pull.prototype.IsPurgable(self)
return false
end
function modifier_item_fishing_rod_hook_pull.prototype.CheckState(self)
return {[MODIFIER_STATE_STUNNED] = true}
end
function modifier_item_fishing_rod_hook_pull.prototype.OnCreated(self)
if not IsServer() then
return
end
self:StartIntervalThink(self.thinkInterval)
end
function modifier_item_fishing_rod_hook_pull.prototype.OnIntervalThink(self)
if not IsServer() then
return
end
local caster = self:GetCaster()
local parent = self:GetParent()
if not caster or not caster:IsAlive() or not parent or not parent:IsAlive() then
self:Destroy()
return
end
local ability = self:GetAbility()
local speed = ability and ability:GetSpecialValueFor("hook_speed") or 1600
local step = speed * self.thinkInterval
local pos = parent:GetAbsOrigin()
local casterPos = caster:GetAbsOrigin()
local flatDelta = Vector(casterPos.x - pos.x, casterPos.y - pos.y, 0)
local dist2d = flatDelta:Length2D()
local stopDist = caster:GetPaddedCollisionRadius() + parent:GetPaddedCollisionRadius() + 32
if dist2d <= stopDist then
FindClearSpaceForUnit(
parent,
caster:GetAbsOrigin(),
true
)
self:Destroy()
return
end
local dir = flatDelta:Normalized()
local move = math.min(step, dist2d)
local newPos = Vector(pos.x + dir.x * move, pos.y + dir.y * move, pos.z)
parent:SetAbsOrigin(GetGroundPosition(newPos, nil))
end
function modifier_item_fishing_rod_hook_pull.prototype.OnDestroy(self)
if not IsServer() then
return
end
local parent = self:GetParent()
if parent and IsValidEntity(parent) and parent:IsAlive() then
FindClearSpaceForUnit(
parent,
parent:GetAbsOrigin(),
true
)
end
end
modifier_item_fishing_rod_hook_pull = __TS__Decorate(
modifier_item_fishing_rod_hook_pull,
modifier_item_fishing_rod_hook_pull,
{registerModifier(nil)},
{kind = "class", name = "modifier_item_fishing_rod_hook_pull"}
)
____exports.modifier_item_fishing_rod_hook_pull = modifier_item_fishing_rod_hook_pull
____exports.modifier_item_fishing_rod_debuff = __TS__Class()
local modifier_item_fishing_rod_debuff = ____exports.modifier_item_fishing_rod_debuff
modifier_item_fishing_rod_debuff.name = "modifier_item_fishing_rod_debuff"
modifier_item_fishing_rod_debuff.____file_path = "scripts/vscripts/items/default_items/item_fishing_rod.lua"
__TS__ClassExtends(modifier_item_fishing_rod_debuff, BaseModifier)
function modifier_item_fishing_rod_debuff.prototype.____constructor(self, ...)
BaseModifier.prototype.____constructor(self, ...)
self.slow_movespeed = 0
self.damage_per_tick = 0
end
function modifier_item_fishing_rod_debuff.prototype.IsHidden(self)
return true
end
function modifier_item_fishing_rod_debuff.prototype.GetEffectName(self)
return "particles/units/heroes/hero_bloodseeker/bloodseeker_rupture.vpcf"
end
function modifier_item_fishing_rod_debuff.prototype.OnCreated(self, params)
local ability = self:GetAbility()
self.slow_movespeed = ability:GetSpecialValueFor("slow_movespeed")
self.damage_per_tick = ability:GetSpecialValueFor("damage_per_tick")
self:StartIntervalThink(ability:GetSpecialValueFor("tick"))
end
function modifier_item_fishing_rod_debuff.prototype.DeclareFunctions(self)
return {MODIFIER_PROPERTY_MOVESPEED_BONUS_PERCENTAGE}
end
function modifier_item_fishing_rod_debuff.prototype.OnIntervalThink(self)
if IsClient() then
return
end
local ability = self:GetAbility()
ApplyDamage({
victim = self:GetParent(),
attacker = self:GetCaster(),
damage_type = ability:GetAbilityDamageType(),
damage = self.damage_per_tick,
ability = ability
})
end
function modifier_item_fishing_rod_debuff.prototype.GetModifierMoveSpeedBonus_Percentage(self)
return self.slow_movespeed
end
modifier_item_fishing_rod_debuff = __TS__Decorate(
modifier_item_fishing_rod_debuff,
modifier_item_fishing_rod_debuff,
{registerModifier(nil)},
{kind = "class", name = "modifier_item_fishing_rod_debuff"}
)
____exports.modifier_item_fishing_rod_debuff = modifier_item_fishing_rod_debuff
____exports.modifier_blind_debuff = __TS__Class()
local modifier_blind_debuff = ____exports.modifier_blind_debuff
modifier_blind_debuff.name = "modifier_blind_debuff"
modifier_blind_debuff.____file_path = "scripts/vscripts/items/default_items/item_fishing_rod.lua"
__TS__ClassExtends(modifier_blind_debuff, BaseModifier)
function modifier_blind_debuff.prototype.IsHidden(self)
return false
end
function modifier_blind_debuff.prototype.IsDebuff(self)
return true
end
function modifier_blind_debuff.prototype.IsPurgable(self)
return true
end
function modifier_blind_debuff.prototype.OnCreated(self, params)
local particle = ParticleManager:CreateParticleForPlayer(
"particles/fish_screen_effect.vpcf",
PATTACH_ABSORIGIN_FOLLOW,
self:GetCaster(),
self:GetCaster():GetPlayerOwner()
)
self.particleId = particle
end
function modifier_blind_debuff.prototype.OnRefresh(self, params)
if self.particleId then
ParticleManager:DestroyParticle(self.particleId, false)
end
self:OnCreated(params)
end
function modifier_blind_debuff.prototype.OnDestroy(self)
if IsClient() then
return
end
if self.particleId then
ParticleManager:DestroyParticle(self.particleId, false)
end
end
function modifier_blind_debuff.prototype.GetTexture(self)
return "templar_assassin_trap"
end
modifier_blind_debuff = __TS__Decorate(
modifier_blind_debuff,
modifier_blind_debuff,
{registerModifier(nil)},
{kind = "class", name = "modifier_blind_debuff"}
)
____exports.modifier_blind_debuff = modifier_blind_debuff
____exports.modifier_bad_cast_debuff = __TS__Class()
local modifier_bad_cast_debuff = ____exports.modifier_bad_cast_debuff
modifier_bad_cast_debuff.name = "modifier_bad_cast_debuff"
modifier_bad_cast_debuff.____file_path = "scripts/vscripts/items/default_items/item_fishing_rod.lua"
__TS__ClassExtends(modifier_bad_cast_debuff, BaseModifier)
function modifier_bad_cast_debuff.prototype.IsHidden(self)
return false
end
function modifier_bad_cast_debuff.prototype.IsDebuff(self)
return true
end
function modifier_bad_cast_debuff.prototype.IsPurgable(self)
return true
end
function modifier_bad_cast_debuff.prototype.GetTexture(self)
return "templar_assassin_trap"
end
modifier_bad_cast_debuff = __TS__Decorate(
modifier_bad_cast_debuff,
modifier_bad_cast_debuff,
{registerModifier(nil)},
{kind = "class", name = "modifier_bad_cast_debuff"}
)
____exports.modifier_bad_cast_debuff = modifier_bad_cast_debuff
return ____exports