--[[ Generated with https://github.com/TypeScriptToLua/TypeScriptToLua ]] local ____exports = {} local ____dota_ts_adapter = require("lib.dota_ts_adapter") local registerEntityFunction = ____dota_ts_adapter.registerEntityFunction local THINK_INTERVAL = 0.25 local TARGET_SEARCH_RADIUS = 2600 local FEAR_CAST_RADIUS = 900 local GRAB_CAST_RANGE = 170 local FACE_CAST_RANGE = 420 local BACK_CAST_RANGE = 720 local abilityGrab local abilityFear local abilityBackStun local abilityFaceStun local lastOrderGameTime = -999 local lastAttackOrderGameTime = -999 local lastAttackTargetIndex = -1 local function refreshAbilities(self) if not thisEntity or thisEntity:IsNull() then return end abilityGrab = thisEntity:FindAbilityByName("ability_grab") or nil abilityFear = thisEntity:FindAbilityByName("fear") or nil abilityBackStun = thisEntity:FindAbilityByName("back_stun") or nil abilityFaceStun = thisEntity:FindAbilityByName("face_stun") or nil end local function findClosestEnemy(self) local enemies = FindUnitsInRadius( thisEntity:GetTeamNumber(), thisEntity:GetAbsOrigin(), nil, TARGET_SEARCH_RADIUS, DOTA_UNIT_TARGET_TEAM_ENEMY, bit.bor(DOTA_UNIT_TARGET_HERO, DOTA_UNIT_TARGET_BASIC), DOTA_UNIT_TARGET_FLAG_FOW_VISIBLE, FIND_CLOSEST, false ) for ____, unit in ipairs(enemies) do do if not unit or unit:IsNull() or not unit:IsAlive() then goto __continue5 end return unit end ::__continue5:: end return nil end local function getDistance2D(self, from, to) return (to - from):Length2D() end local function isTargetBehind(self, target) local toTarget = (target:GetAbsOrigin() - thisEntity:GetAbsOrigin()):Normalized() local forward = thisEntity:GetForwardVector() return forward:Dot(toTarget) < -0.25 end local function isTargetInFront(self, target) local toTarget = (target:GetAbsOrigin() - thisEntity:GetAbsOrigin()):Normalized() local forward = thisEntity:GetForwardVector() return forward:Dot(toTarget) > 0.25 end local function isPointInFront(self, point) local toPoint = (point - thisEntity:GetAbsOrigin()):Normalized() local forward = thisEntity:GetForwardVector() return forward:Dot(toPoint) > 0.25 end local function isPointBehind(self, point) local toPoint = (point - thisEntity:GetAbsOrigin()):Normalized() local forward = thisEntity:GetForwardVector() return forward:Dot(toPoint) < -0.25 end local function issueCastPosition(self, ability, position) local now = GameRules:GetGameTime() if now - lastOrderGameTime < 0.15 then return false end ExecuteOrderFromTable({ UnitIndex = thisEntity:entindex(), OrderType = DOTA_UNIT_ORDER_CAST_POSITION, AbilityIndex = ability:entindex(), Position = position, Queue = false }) lastOrderGameTime = now return true end local function issueCastNoTarget(self, ability) local now = GameRules:GetGameTime() if now - lastOrderGameTime < 0.15 then return false end ExecuteOrderFromTable({ UnitIndex = thisEntity:entindex(), OrderType = DOTA_UNIT_ORDER_CAST_NO_TARGET, AbilityIndex = ability:entindex(), Queue = false }) lastOrderGameTime = now return true end local function issueMove(self, target) local now = GameRules:GetGameTime() if now - lastOrderGameTime < 0.25 then return end if not target or target:IsNull() or not target:IsAlive() then return end local targetPosition = target:GetAbsOrigin() ExecuteOrderFromTable({ UnitIndex = thisEntity:entindex(), OrderType = DOTA_UNIT_ORDER_MOVE_TO_POSITION, Position = targetPosition, Queue = false }) lastOrderGameTime = now end local function issueAttackTarget(self, target) local now = GameRules:GetGameTime() if now - lastOrderGameTime < 0.25 then return end if not target or target:IsNull() or not target:IsAlive() then return end local targetIndex = target:entindex() if targetIndex == nil or targetIndex < 0 then return end if lastAttackTargetIndex == targetIndex and now - lastAttackOrderGameTime < 1 then return end ExecuteOrderFromTable({ UnitIndex = thisEntity:entindex(), OrderType = DOTA_UNIT_ORDER_ATTACK_TARGET, TargetIndex = targetIndex, Queue = false }) lastOrderGameTime = now lastAttackOrderGameTime = now lastAttackTargetIndex = targetIndex end local function areAllAbilitiesOnCooldown(self) if not abilityGrab or not abilityFear or not abilityBackStun or not abilityFaceStun then return false end return abilityGrab:GetCooldownTimeRemaining() > 0 and abilityFear:GetCooldownTimeRemaining() > 0 and abilityBackStun:GetCooldownTimeRemaining() > 0 and abilityFaceStun:GetCooldownTimeRemaining() > 0 end local function findStunnedEnemyPoint(self) local enemies = FindUnitsInRadius( thisEntity:GetTeamNumber(), thisEntity:GetAbsOrigin(), nil, BACK_CAST_RANGE, DOTA_UNIT_TARGET_TEAM_ENEMY, bit.bor(DOTA_UNIT_TARGET_HERO, DOTA_UNIT_TARGET_BASIC), DOTA_UNIT_TARGET_FLAG_FOW_VISIBLE, FIND_CLOSEST, false ) for ____, enemy in ipairs(enemies) do do if not enemy or enemy:IsNull() or not enemy:IsAlive() then goto __continue28 end if enemy:HasModifier("modifier_face_stunned") or enemy:HasModifier("modifier_back_stunned") then return enemy:GetAbsOrigin() end end ::__continue28:: end return nil end local function tryChainStunAtPoint(self, point) local distance = getDistance2D( nil, thisEntity:GetAbsOrigin(), point ) if abilityFaceStun and not abilityFaceStun:IsNull() and abilityFaceStun:IsFullyCastable() and distance <= FACE_CAST_RANGE then thisEntity:FaceTowards(point) return issueCastPosition(nil, abilityFaceStun, point) end if abilityBackStun and not abilityBackStun:IsNull() and abilityBackStun:IsFullyCastable() and distance <= BACK_CAST_RANGE then local casterOrigin = thisEntity:GetAbsOrigin() local faceAwayPoint = casterOrigin:__mul(2):__sub(point) thisEntity:FaceTowards(faceAwayPoint) return issueCastNoTarget(nil, abilityBackStun) end return false end local function tryCastFear(self) if not abilityFear or abilityFear:IsNull() or not abilityFear:IsFullyCastable() then return false end local enemies = FindUnitsInRadius( thisEntity:GetTeamNumber(), thisEntity:GetAbsOrigin(), nil, FEAR_CAST_RADIUS, DOTA_UNIT_TARGET_TEAM_ENEMY, bit.bor(DOTA_UNIT_TARGET_HERO, DOTA_UNIT_TARGET_BASIC), DOTA_UNIT_TARGET_FLAG_NONE, FIND_ANY_ORDER, false ) if #enemies < 2 then return false end return issueCastNoTarget(nil, abilityFear) end local function tryCastGrab(self, target, distance) if not abilityGrab or abilityGrab:IsNull() or not abilityGrab:IsFullyCastable() then return false end if distance > GRAB_CAST_RANGE then return false end return issueCastPosition( nil, abilityGrab, target:GetAbsOrigin() ) end local function tryCastFaceStun(self, target, distance) if not abilityFaceStun or abilityFaceStun:IsNull() or not abilityFaceStun:IsFullyCastable() then return false end if distance > FACE_CAST_RANGE or not isTargetInFront(nil, target) then return false end return issueCastPosition( nil, abilityFaceStun, target:GetAbsOrigin() ) end local function tryCastBackStun(self, target, distance) if not abilityBackStun or abilityBackStun:IsNull() or not abilityBackStun:IsFullyCastable() then return false end if distance > BACK_CAST_RANGE or not isTargetBehind(nil, target) then return false end return issueCastNoTarget(nil, abilityBackStun) end local function DemonDragonSatyrThink(self) if not IsServer() or not thisEntity or thisEntity:IsNull() or not thisEntity:IsAlive() then return THINK_INTERVAL end if GameRules:IsGamePaused() or thisEntity:IsChanneling() then return THINK_INTERVAL end if not abilityGrab or not abilityFear or not abilityBackStun or not abilityFaceStun then refreshAbilities(nil) end local target = findClosestEnemy(nil) if not target then return THINK_INTERVAL end local distance = getDistance2D( nil, thisEntity:GetAbsOrigin(), target:GetAbsOrigin() ) local stunnedEnemyPoint = findStunnedEnemyPoint(nil) if stunnedEnemyPoint then if tryChainStunAtPoint(nil, stunnedEnemyPoint) then return THINK_INTERVAL end if isPointInFront(nil, stunnedEnemyPoint) and tryCastFaceStun(nil, target, distance) then return THINK_INTERVAL end if isPointBehind(nil, stunnedEnemyPoint) and tryCastBackStun(nil, target, distance) then return THINK_INTERVAL end end if tryCastFear(nil) then return THINK_INTERVAL end if tryCastGrab(nil, target, distance) then return THINK_INTERVAL end if tryCastFaceStun(nil, target, distance) then return THINK_INTERVAL end if tryCastBackStun(nil, target, distance) then return THINK_INTERVAL end if areAllAbilitiesOnCooldown(nil) then issueAttackTarget(nil, target) return THINK_INTERVAL end issueMove(nil, target) return THINK_INTERVAL end registerEntityFunction( nil, "Spawn", function() if not IsServer() or not thisEntity or thisEntity:IsNull() then return end refreshAbilities(nil) thisEntity:SetContextThink("DemonDragonSatyrThink", DemonDragonSatyrThink, THINK_INTERVAL) end ) return ____exports