310 lines
10 KiB
Lua
310 lines
10 KiB
Lua
--[[ 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
|