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