local ____lualib = require("lualib_bundle") local __TS__Class = ____lualib.__TS__Class local __TS__ClassExtends = ____lualib.__TS__ClassExtends local __TS__Decorate = ____lualib.__TS__Decorate local ____exports = {} local ____difficulty_manager = require("difficulty_manager") local Difficulty = ____difficulty_manager.Difficulty 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 --- Та же физика дуги, что у `frogmen_acid_jump` / `modifier_acid_blob_jump`. local CONTRACT_HEAD_LEAP_MIN_HEIGHT_ABOVE_LOWEST = 400 local CONTRACT_HEAD_LEAP_MIN_HEIGHT_ABOVE_HIGHEST = 200 local CONTRACT_HEAD_LEAP_ACCELERATION_Z = 1500 local CONTRACT_HEAD_LEAP_MAX_HORIZONTAL_ACCELERATION = 1500 --- Пассивка контракта: прыжок дугой к точке, где стоял герой; урон только если к моменту приземления он не ушёл из зоны. ____exports.contract_head_leap = __TS__Class() local contract_head_leap = ____exports.contract_head_leap contract_head_leap.name = "contract_head_leap" contract_head_leap.____file_path = "scripts/vscripts/abilities/creep/contract_head_leap.lua" __TS__ClassExtends(contract_head_leap, BaseAbility) function contract_head_leap.prototype.Precache(self, context) PrecacheResource("particle", "particles/generic_gameplay/generic_hit_blood.vpcf", context) PrecacheResource("particle", "particles/econ/events/darkmoon_2017/darkmoon_generic_aoe.vpcf", context) PrecacheResource("particle", "particles/econ/events/darkmoon_2017/darkmoon_calldown_marker_arrows.vpcf", context) end function contract_head_leap.prototype.GetIntrinsicModifierName(self) return "modifier_contract_head_leap_passive" end function contract_head_leap.prototype.showLeapPrecastVisuals(self, caster, strikeGround, damageRadius, warningDuration) if not IsServer() or warningDuration <= 0 then return end local groundPos = GetGroundPosition(strikeGround, nil) local r = math.max(1, damageRadius) local particleAoe = ParticleManager:CreateParticle("particles/econ/events/darkmoon_2017/darkmoon_generic_aoe.vpcf", PATTACH_CUSTOMORIGIN, nil) if particleAoe ~= nil then ParticleManager:SetParticleControl(particleAoe, 0, groundPos) ParticleManager:SetParticleControl( particleAoe, 1, Vector(r, 0, 0) ) ParticleManager:SetParticleControl( particleAoe, 2, Vector(6, 0, 1) ) ParticleManager:SetParticleControl( particleAoe, 3, Vector(200, 0, 0) ) ParticleManager:SetParticleControl(particleAoe, 4, groundPos) Timers:CreateTimer( warningDuration, function() ParticleManager:DestroyParticle(particleAoe, false) ParticleManager:ReleaseParticleIndex(particleAoe) end ) end local casterPos = GetGroundPosition( caster:GetAbsOrigin(), nil ) local particleArrow = ParticleManager:CreateParticle("particles/econ/events/darkmoon_2017/darkmoon_calldown_marker_arrows.vpcf", PATTACH_CUSTOMORIGIN, nil) if particleArrow ~= nil then ParticleManager:SetParticleControl(particleArrow, 0, casterPos) ParticleManager:SetParticleControl(particleArrow, 1, groundPos) ParticleManager:SetParticleControl( particleArrow, 2, Vector(warningDuration, 0, 0) ) Timers:CreateTimer( warningDuration, function() ParticleManager:DestroyParticle(particleArrow, false) ParticleManager:ReleaseParticleIndex(particleArrow) end ) end end function contract_head_leap.prototype.performLeapLanding(self, heroEntIndex, strikeGround) if not IsServer() then return end local creep = self:GetCaster() if not creep or not IsValidEntity(creep) or not creep:IsAlive() then return end local hero = EntIndexToHScript(heroEntIndex) local hitRadius = math.max( 0, self:GetSpecialValueFor("damage_radius") ) local hpos = hero and IsValidEntity(hero) and hero:IsAlive() and hero:GetAbsOrigin() or nil local inZone = false if hpos ~= nil then local dx = hpos.x - strikeGround.x local dy = hpos.y - strikeGround.y inZone = math.sqrt(dx * dx + dy * dy) <= hitRadius end if hero and IsValidEntity(hero) and hero:IsAlive() and not hero:IsInvulnerable() and inZone then local scale = Difficulty:getNpcStatScale() local flatDamage = math.max( 1, math.floor(self:GetSpecialValueFor("leap_damage") * scale + 0.5) ) local pctFromAttack = math.max( 0, self:GetSpecialValueFor("damage_from_attack_pct") ) local avgAtk = creep:GetAverageTrueAttackDamage(hero) local fromAttack = math.floor(avgAtk * (pctFromAttack / 100) * scale + 0.5) local totalDamage = flatDamage + fromAttack ApplyDamage({ victim = hero, attacker = creep, damage = totalDamage, damage_type = DAMAGE_TYPE_PHYSICAL, ability = self }) local hitFx = ParticleManager:CreateParticle("particles/generic_gameplay/generic_hit_blood.vpcf", PATTACH_CUSTOMORIGIN, nil) ParticleManager:SetParticleControlEnt( hitFx, 0, hero, PATTACH_POINT_FOLLOW, "attach_hitloc", hero:GetAbsOrigin(), true ) ParticleManager:ReleaseParticleIndex(hitFx) EmitSoundOn("Hero_MonkeyKing.Spring.Channel", creep) end end contract_head_leap = __TS__Decorate( contract_head_leap, contract_head_leap, {registerAbility(nil)}, {kind = "class", name = "contract_head_leap"} ) ____exports.contract_head_leap = contract_head_leap ____exports.modifier_contract_head_leap_passive = __TS__Class() local modifier_contract_head_leap_passive = ____exports.modifier_contract_head_leap_passive modifier_contract_head_leap_passive.name = "modifier_contract_head_leap_passive" modifier_contract_head_leap_passive.____file_path = "scripts/vscripts/abilities/creep/contract_head_leap.lua" __TS__ClassExtends(modifier_contract_head_leap_passive, BaseModifier) function modifier_contract_head_leap_passive.prototype.____constructor(self, ...) BaseModifier.prototype.____constructor(self, ...) self.precastLockUntil = -1 end function modifier_contract_head_leap_passive.prototype.IsHidden(self) return true end function modifier_contract_head_leap_passive.prototype.IsDebuff(self) return false end function modifier_contract_head_leap_passive.prototype.IsPurgable(self) return false end function modifier_contract_head_leap_passive.prototype.OnCreated(self) if not IsServer() then return end local ability = self:GetAbility() local interval = ability and math.max( 0.35, ability:GetSpecialValueFor("leap_interval") ) or 2.4 self:StartIntervalThink(interval) end function modifier_contract_head_leap_passive.prototype.OnRefresh(self) if not IsServer() then return end local ability = self:GetAbility() local interval = ability and math.max( 0.35, ability:GetSpecialValueFor("leap_interval") ) or 2.4 self:StartIntervalThink(interval) end function modifier_contract_head_leap_passive.prototype.OnIntervalThink(self) if not IsServer() then return end local parent = self:GetParent() local ability = self:GetAbility() if not ability or not IsValidEntity(parent) or not parent:IsAlive() or parent:IsStunned() or parent:IsHexed() then return end local now = GameRules:GetDOTATime(false, false) if now < self.precastLockUntil then return end local primary = parent:FindAbilityByName("contract_head_leap") if not primary or primary ~= ability then return end if parent:HasModifier("modifier_contract_head_leap_arc") then return end local radius = math.max( 50, ability:GetSpecialValueFor("search_radius") ) local heroes = FindUnitsInRadius( parent:GetTeamNumber(), parent:GetAbsOrigin(), nil, radius, DOTA_UNIT_TARGET_TEAM_ENEMY, DOTA_UNIT_TARGET_HERO, DOTA_UNIT_TARGET_FLAG_NONE, FIND_CLOSEST, false ) local hero for ____, u in ipairs(heroes) do do if not u or not IsValidEntity(u) or not u:IsAlive() or not u:IsHero() then goto __continue28 end if not u:IsRealHero() then goto __continue28 end if u:IsInvulnerable() then goto __continue28 end if u:GetUnitName() == "npc_homer" then goto __continue28 end hero = u break end ::__continue28:: end if not hero then return end local height = math.max( 40, ability:GetSpecialValueFor("height_above_hero") ) local strikeGround = GetGroundPosition( hero:GetAbsOrigin(), hero ) local headPos = Vector(strikeGround.x, strikeGround.y, strikeGround.z + height) local warningTime = math.max( 0, ability:GetSpecialValueFor("precast_warning_time") ) local dmgRadius = math.max( 1, ability:GetSpecialValueFor("damage_radius") ) local arcPayload = { vLocX = headPos.x, vLocY = headPos.y, vLocZ = headPos.z, strikeX = strikeGround.x, strikeY = strikeGround.y, strikeZ = strikeGround.z, hero_ei = hero:entindex() } if warningTime > 0 then self.precastLockUntil = now + warningTime ability:showLeapPrecastVisuals(parent, strikeGround, dmgRadius, warningTime) local ____self = self Timers:CreateTimer( warningTime, function() ____self.precastLockUntil = -1 if not IsServer() then return end if not IsValidEntity(parent) or not parent:IsAlive() then return end if parent:HasModifier("modifier_contract_head_leap_arc") then return end local ab = parent:FindAbilityByName("contract_head_leap") if not ab then return end parent:AddNewModifier(parent, ab, "modifier_contract_head_leap_arc", arcPayload) end ) else parent:AddNewModifier(parent, ability, "modifier_contract_head_leap_arc", arcPayload) end end modifier_contract_head_leap_passive = __TS__Decorate( modifier_contract_head_leap_passive, modifier_contract_head_leap_passive, {registerModifier(nil)}, {kind = "class", name = "modifier_contract_head_leap_passive"} ) ____exports.modifier_contract_head_leap_passive = modifier_contract_head_leap_passive ____exports.modifier_contract_head_leap_arc = __TS__Class() local modifier_contract_head_leap_arc = ____exports.modifier_contract_head_leap_arc modifier_contract_head_leap_arc.name = "modifier_contract_head_leap_arc" modifier_contract_head_leap_arc.____file_path = "scripts/vscripts/abilities/creep/contract_head_leap.lua" __TS__ClassExtends(modifier_contract_head_leap_arc, BaseModifierMotionBoth) function modifier_contract_head_leap_arc.prototype.____constructor(self, ...) BaseModifierMotionBoth.prototype.____constructor(self, ...) self.bHorizontalMotionInterrupted = false self.flCurrentTimeHoriz = 0 self.flCurrentTimeVert = 0 self.flInitialVelocityZ = 0 self.flPredictedTotalTime = 0 end function modifier_contract_head_leap_arc.prototype.IsHidden(self) return true end function modifier_contract_head_leap_arc.prototype.IsPurgable(self) return false end function modifier_contract_head_leap_arc.prototype.RemoveOnDeath(self) return true end function modifier_contract_head_leap_arc.prototype.OnCreated(self, params) if not IsServer() then return end local parent = self:GetParent() local ability = self:GetAbility() if not parent or not ability then self:Destroy() return end local hx = params.hero_ei if hx == nil or hx == nil then self:Destroy() return end self.heroEntIndex = hx local sx = params.strikeX or parent:GetOrigin().x local sy = params.strikeY or parent:GetOrigin().y local sz = params.strikeZ or parent:GetOrigin().z self.strikeGround = Vector(sx, sy, sz) self.bHorizontalMotionInterrupted = false if not self:ApplyHorizontalMotionController() or not self:ApplyVerticalMotionController() then self:Destroy() return end self.vStartPosition = GetGroundPosition( parent:GetOrigin(), parent ) self.flCurrentTimeHoriz = 0 self.flCurrentTimeVert = 0 local x = params.vLocX or parent:GetOrigin().x local y = params.vLocY or parent:GetOrigin().y local z = params.vLocZ or parent:GetOrigin().z self.vLoc = Vector(x, y, z) self.vLastKnownTargetPos = self.vLoc local duration = math.max( 0.08, ability:GetSpecialValueFor("jump_duration") ) local flDesiredHeight = CONTRACT_HEAD_LEAP_MIN_HEIGHT_ABOVE_LOWEST * duration * duration local flLowZ = math.min(self.vLastKnownTargetPos.z, self.vStartPosition.z) local flHighZ = math.max(self.vLastKnownTargetPos.z, self.vStartPosition.z) local flArcTopZ = math.max(flLowZ + flDesiredHeight, flHighZ + CONTRACT_HEAD_LEAP_MIN_HEIGHT_ABOVE_HIGHEST) local flArcDeltaZ = flArcTopZ - self.vStartPosition.z self.flInitialVelocityZ = math.sqrt(2 * flArcDeltaZ * CONTRACT_HEAD_LEAP_ACCELERATION_Z) local flDeltaZ = self.vLastKnownTargetPos.z - self.vStartPosition.z local flSqrtDet = math.sqrt(math.max(0, self.flInitialVelocityZ * self.flInitialVelocityZ - 2 * CONTRACT_HEAD_LEAP_ACCELERATION_Z * flDeltaZ)) self.flPredictedTotalTime = math.max((self.flInitialVelocityZ + flSqrtDet) / CONTRACT_HEAD_LEAP_ACCELERATION_Z, (self.flInitialVelocityZ - flSqrtDet) / CONTRACT_HEAD_LEAP_ACCELERATION_Z) self.vHorizontalVelocity = (self.vLastKnownTargetPos - self.vStartPosition) / self.flPredictedTotalTime self.vHorizontalVelocity.z = 0 end function modifier_contract_head_leap_arc.prototype.OnDestroy(self) if not IsServer() then return end local parent = self:GetParent() if parent ~= nil then parent:RemoveHorizontalMotionController(self) parent:RemoveVerticalMotionController(self) end end function modifier_contract_head_leap_arc.prototype.CheckState(self) return {[MODIFIER_STATE_STUNNED] = true, [MODIFIER_STATE_UNSELECTABLE] = true} end function modifier_contract_head_leap_arc.prototype.UpdateHorizontalMotion(self, me, dt) if not IsServer() then return end self.flCurrentTimeHoriz = math.min(self.flCurrentTimeHoriz + dt, self.flPredictedTotalTime) local t = self.flCurrentTimeHoriz / self.flPredictedTotalTime local vStartToTarget = self.vLastKnownTargetPos - self.vStartPosition local vDesiredPos = self.vStartPosition + vStartToTarget * t local vOldPos = me:GetOrigin() local vToDesired = vDesiredPos - vOldPos vToDesired.z = 0 local vDesiredVel = vToDesired / dt local vVelDif = vDesiredVel - self.vHorizontalVelocity local flVelDif = vVelDif:Length2D() vVelDif = vVelDif:Normalized() local flVelDelta = math.min(flVelDif, CONTRACT_HEAD_LEAP_MAX_HORIZONTAL_ACCELERATION) self.vHorizontalVelocity = self.vHorizontalVelocity + vVelDif * flVelDelta * dt local vNewPos = vOldPos + self.vHorizontalVelocity * dt me:SetOrigin(vNewPos) end function modifier_contract_head_leap_arc.prototype.UpdateVerticalMotion(self, me, dt) if not IsServer() then return end self.flCurrentTimeVert = self.flCurrentTimeVert + dt local bGoingDown = -CONTRACT_HEAD_LEAP_ACCELERATION_Z * self.flCurrentTimeVert + self.flInitialVelocityZ < 0 local vNewPos = me:GetOrigin() vNewPos.z = self.vStartPosition.z + (-0.5 * CONTRACT_HEAD_LEAP_ACCELERATION_Z * self.flCurrentTimeVert * self.flCurrentTimeVert + self.flInitialVelocityZ * self.flCurrentTimeVert) local flGroundHeight = GetGroundHeight( vNewPos, self:GetParent() ) local bLanded = false if vNewPos.z < flGroundHeight and bGoingDown then vNewPos.z = flGroundHeight bLanded = true end me:SetOrigin(vNewPos) if bLanded then if not self.bHorizontalMotionInterrupted then local ability = self:GetAbility() if ability then ability:performLeapLanding(self.heroEntIndex, self.strikeGround) end end self:Destroy() end end function modifier_contract_head_leap_arc.prototype.OnHorizontalMotionInterrupted(self) if not IsServer() then return end self.bHorizontalMotionInterrupted = true end function modifier_contract_head_leap_arc.prototype.OnVerticalMotionInterrupted(self) if not IsServer() then return end self:Destroy() end function modifier_contract_head_leap_arc.prototype.DeclareFunctions(self) return {MODIFIER_PROPERTY_OVERRIDE_ANIMATION} end function modifier_contract_head_leap_arc.prototype.GetOverrideAnimation(self) return ACT_DOTA_CAST_ABILITY_1 end modifier_contract_head_leap_arc = __TS__Decorate( modifier_contract_head_leap_arc, modifier_contract_head_leap_arc, {registerModifier(nil)}, {kind = "class", name = "modifier_contract_head_leap_arc"} ) ____exports.modifier_contract_head_leap_arc = modifier_contract_head_leap_arc return ____exports