507 lines
14 KiB
Lua
Executable File
507 lines
14 KiB
Lua
Executable File
|
|
--[[ ai/rhyzik.lua ]]
|
|
|
|
PHASE_PHYSICAL = 0
|
|
PHASE_SANDSTORM = 1
|
|
PHASE_EPICENTER = 2
|
|
|
|
TRIGGER_PHASE_CD = 10
|
|
PHASE_DURATION = 12
|
|
BURROW_DURATION = 12
|
|
MIN_BURROWSTRIKES = 4
|
|
MAX_BURROWSTRIKES = 7
|
|
|
|
function Spawn( entityKeyValues )
|
|
if IsServer() then
|
|
if thisEntity == nil then
|
|
return
|
|
end
|
|
|
|
TailSwipeLeft = thisEntity:FindAbilityByName( "sand_king_tail_swipe_left" )
|
|
TailSwipeRight = thisEntity:FindAbilityByName( "sand_king_tail_swipe_right" )
|
|
BurrowStrike = thisEntity:FindAbilityByName( "sand_king_boss_burrowstrike" )
|
|
BurrowDown = thisEntity:FindAbilityByName( "sand_king_boss_burrow" )
|
|
BurrowUp = thisEntity:FindAbilityByName( "sand_king_boss_unburrow" )
|
|
SandStorm = thisEntity:FindAbilityByName( "sand_king_boss_sandstorm" )
|
|
Epicenter = thisEntity:FindAbilityByName( "sand_king_boss_epicenter" )
|
|
ForwardTailSwipe = thisEntity:FindAbilityByName( "sand_king_burrowed_forward_strike" )
|
|
BackwardsTailSwipe = thisEntity:FindAbilityByName( "sand_king_burrowed_backward_strike" )
|
|
|
|
DirectionalMoveLeft = thisEntity:FindAbilityByName( "sand_king_boss_move_left" )
|
|
DirectionalMoveRight = thisEntity:FindAbilityByName( "sand_king_boss_move_right" )
|
|
DirectionalMoveBack = thisEntity:FindAbilityByName( "sand_king_boss_move_back" )
|
|
|
|
--thisEntity.PHASE = PHASE_PHYSICAL
|
|
thisEntity.PHASE = PHASE_SANDSTORM
|
|
|
|
Blink = thisEntity:FindItemInInventory( "item_blink" )
|
|
Shivas = thisEntity:FindItemInInventory( "item_shivas_guard" )
|
|
|
|
thisEntity:SetContextThink( "SandKingThink", SandKingThink, 1 )
|
|
thisEntity.flNextPhaseTime = nil
|
|
thisEntity.flPhaseTriggerEndTime = 0
|
|
thisEntity.bBurrowStateQueued = false
|
|
thisEntity.nBurrowStrikesRemaining = MIN_BURROWSTRIKES
|
|
thisEntity.nCurrentBurrowStrikes = MIN_BURROWSTRIKES
|
|
thisEntity.flUnburrowTime = 0
|
|
|
|
thisEntity.BurrowSkill = BurrowStrike
|
|
|
|
--thisEntity.fOrigSpawnPos = Vector( 11136, 12160, 384 ) -- Removed in AghsFort until we figure what this should mean
|
|
--print( string.format( "saved SK's spawn point: %.2f, %.2f, %.2f", thisEntity.fOrigSpawnPos.x, thisEntity.fOrigSpawnPos.y, thisEntity.fOrigSpawnPos.z ) )
|
|
end
|
|
end
|
|
|
|
function GetNumberBurrowStrikes()
|
|
local nHealthPct = thisEntity:GetHealthPercent()
|
|
if nHealthPct > 80 then
|
|
return MIN_BURROWSTRIKES
|
|
end
|
|
|
|
if nHealthPct > 70 then
|
|
return MIN_BURROWSTRIKES + 1
|
|
end
|
|
|
|
if nHealthPct > 50 then
|
|
return MIN_BURROWSTRIKES + 2
|
|
end
|
|
|
|
return MAX_BURROWSTRIKES
|
|
end
|
|
|
|
function TailIsReady()
|
|
if thisEntity:FindModifierByName( "modifier_sand_king_boss_burrow" ) == nil then
|
|
if TailSwipeLeft ~= nil and TailSwipeRight ~= nil and TailSwipeLeft:IsCooldownReady() and TailSwipeRight:IsCooldownReady() then
|
|
return true
|
|
end
|
|
else
|
|
if ForwardTailSwipe ~= nil and BackwardsTailSwipe ~= nil and ForwardTailSwipe:IsCooldownReady() and BackwardsTailSwipe:IsCooldownReady() then
|
|
return true
|
|
end
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
function WalkIsReady()
|
|
if DirectionalMoveLeft ~= nil and DirectionalMoveRight ~= nil and DirectionalMoveBack ~= nil then
|
|
if DirectionalMoveLeft:IsCooldownReady() or DirectionalMoveRight:IsCooldownReady() or DirectionalMoveBack:IsCooldownReady() then
|
|
return true
|
|
end
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
function ChangePhase()
|
|
if thisEntity.flNextPhaseTime > GameRules:GetGameTime() then
|
|
return false
|
|
end
|
|
|
|
if thisEntity:FindModifierByName( "modifier_sand_king_boss_burrow" ) ~= nil then
|
|
return 0.1
|
|
end
|
|
|
|
if thisEntity.bBurrowStateQueued == true then
|
|
return false
|
|
end
|
|
|
|
printf( "Changing Phase " .. GameRules:GetGameTime() )
|
|
|
|
if thisEntity.PHASE == PHASE_EPICENTER then
|
|
thisEntity.PHASE = PHASE_PHYSICAL
|
|
else
|
|
thisEntity.PHASE = thisEntity.PHASE + 1
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
function SandKingThink()
|
|
if GameRules:IsGamePaused() == true then
|
|
return 1
|
|
end
|
|
|
|
if thisEntity == nil then
|
|
return 1
|
|
end
|
|
|
|
if thisEntity:GetTeamNumber() == DOTA_TEAM_GOODGUYS then
|
|
return 1
|
|
end
|
|
|
|
if thisEntity:IsChanneling() == true then
|
|
return 0.1
|
|
end
|
|
|
|
--[[ Removed in Aghanim's Fortress
|
|
if thisEntity.bStarted ~= true then
|
|
return 0.1
|
|
end
|
|
]]
|
|
|
|
--[[ Removed in Aghanim's Fortress until we figure out what fOriginalSpawnPos should mean
|
|
local fDistFromSpawn = ( thisEntity:GetOrigin() - thisEntity.fOrigSpawnPos ):Length2D()
|
|
if fDistFromSpawn > 7000 then
|
|
--print( "teleport SK back to his spawn point" )
|
|
FindClearSpaceForUnit( thisEntity, thisEntity.fOrigSpawnPos, true )
|
|
return 0.1
|
|
end
|
|
]]
|
|
|
|
local hEndCamera = Entities:FindByName( nil, "boss_camera" )
|
|
if hEndCamera ~= nil then
|
|
hEndCamera:SetAbsOrigin( thisEntity:GetAbsOrigin() )
|
|
end
|
|
|
|
if thisEntity.flNextPhaseTime == nil then
|
|
thisEntity.bBurrowStateQueued = true
|
|
thisEntity.flNextPhaseTime = GameRules:GetGameTime() + TRIGGER_PHASE_CD
|
|
-- print( "Setting inital phase time to " .. thisEntity.flNextPhaseTime )
|
|
end
|
|
|
|
if ChangePhase() then
|
|
-- print( "Changing Phase..")
|
|
thisEntity.flPhaseTriggerEndTime = GameRules:GetGameTime() + PHASE_DURATION
|
|
thisEntity.bBurrowStateQueued = true
|
|
if thisEntity.BurrowSkill == BurrowStrike then
|
|
thisEntity.BurrowSkill = BurrowDown
|
|
thisEntity.flUnburrowTime = GameRules:GetGameTime() + PHASE_DURATION + BURROW_DURATION
|
|
thisEntity.flNextPhaseTime = GameRules:GetGameTime() + TRIGGER_PHASE_CD + PHASE_DURATION + BURROW_DURATION
|
|
-- print( "Setting next phase time to " .. thisEntity.flNextPhaseTime )
|
|
else
|
|
thisEntity.BurrowSkill = BurrowStrike
|
|
thisEntity.nBurrowStrikesRemaining = GetNumberBurrowStrikes()
|
|
thisEntity.nCurrentBurrowStrikes = GetNumberBurrowStrikes()
|
|
thisEntity.flNextPhaseTime = GameRules:GetGameTime() + TRIGGER_PHASE_CD + PHASE_DURATION --Burrowstrikes adds to this time later after tracking the duration of strikes
|
|
-- print( "Setting next phase time to " .. thisEntity.flNextPhaseTime )
|
|
end
|
|
end
|
|
|
|
if thisEntity.flPhaseTriggerEndTime > GameRules:GetGameTime() then
|
|
if thisEntity.PHASE == PHASE_SANDSTORM then
|
|
return SandstormThink()
|
|
end
|
|
if thisEntity.PHASE == PHASE_EPICENTER then
|
|
return EpicenterThink()
|
|
end
|
|
end
|
|
|
|
local enemies = FindUnitsInRadius( DOTA_TEAM_BADGUYS, thisEntity:GetOrigin(), nil, 3000, DOTA_UNIT_TARGET_TEAM_ENEMY, DOTA_UNIT_TARGET_HERO, DOTA_UNIT_TARGET_FLAG_NOT_CREEP_HERO + DOTA_UNIT_TARGET_FLAG_MAGIC_IMMUNE_ENEMIES, FIND_CLOSEST, false )
|
|
if #enemies == 0 then
|
|
return 1
|
|
end
|
|
|
|
if thisEntity.bBurrowStateQueued == true then
|
|
return BurrowThink( enemies )
|
|
end
|
|
|
|
return PhysicalThink( enemies )
|
|
end
|
|
|
|
function BurrowThink( enemies )
|
|
if thisEntity.BurrowSkill == BurrowDown then
|
|
if GameRules:GetGameTime() > thisEntity.flUnburrowTime then
|
|
thisEntity.bBurrowStateQueued = false
|
|
return CastBurrowUp()
|
|
end
|
|
if thisEntity:FindModifierByName( "modifier_sand_king_boss_burrow" ) == nil then
|
|
return CastBurrowDown()
|
|
else
|
|
return PhysicalThink( enemies )
|
|
end
|
|
end
|
|
|
|
if thisEntity.BurrowSkill == BurrowStrike then
|
|
if thisEntity:FindModifierByName( "modifier_sand_king_boss_burrowstrike" ) ~= nil or thisEntity:FindModifierByName( "modifier_sand_king_boss_burrowstrike_end" ) ~= nil then
|
|
return 0.1
|
|
end
|
|
|
|
if thisEntity.nBurrowStrikesRemaining == thisEntity.nCurrentBurrowStrikes then
|
|
thisEntity.flBurrowStrikesStartTime = GameRules:GetGameTime()
|
|
end
|
|
|
|
if thisEntity.nBurrowStrikesRemaining <= 0 then
|
|
thisEntity.bBurrowStateQueued = false
|
|
thisEntity.flNextPhaseTime = thisEntity.flNextPhaseTime + ( GameRules:GetGameTime() - thisEntity.flBurrowStrikesStartTime )
|
|
return 0.1
|
|
end
|
|
|
|
thisEntity.nBurrowStrikesRemaining = thisEntity.nBurrowStrikesRemaining - 1
|
|
|
|
for _, enemy in pairs ( enemies ) do
|
|
if enemy ~= nil and enemy:IsInvulnerable() == false then
|
|
return CastBurrowstrike( enemy )
|
|
end
|
|
end
|
|
end
|
|
|
|
return 1
|
|
end
|
|
|
|
function CastBurrowstrike( enemy )
|
|
--print( "SandKingBoss - Cast Burrowstrike" )
|
|
ExecuteOrderFromTable({
|
|
UnitIndex = thisEntity:entindex(),
|
|
OrderType = DOTA_UNIT_ORDER_CAST_POSITION,
|
|
AbilityIndex = BurrowStrike:entindex(),
|
|
Position = enemy:GetOrigin(),
|
|
Queue = false,
|
|
})
|
|
|
|
return 2.5
|
|
end
|
|
|
|
function CastBurrowDown()
|
|
--print( "SandKingBoss - Cast BurrowDown" )
|
|
ExecuteOrderFromTable({
|
|
UnitIndex = thisEntity:entindex(),
|
|
OrderType = DOTA_UNIT_ORDER_CAST_NO_TARGET,
|
|
AbilityIndex = BurrowDown:entindex(),
|
|
Queue = false,
|
|
})
|
|
|
|
return 2.5
|
|
end
|
|
|
|
function CastBurrowUp()
|
|
--print( "SandKingBoss - Cast BurrowUp" )
|
|
ExecuteOrderFromTable({
|
|
UnitIndex = thisEntity:entindex(),
|
|
OrderType = DOTA_UNIT_ORDER_CAST_NO_TARGET,
|
|
AbilityIndex = BurrowUp:entindex(),
|
|
Queue = false,
|
|
})
|
|
|
|
return 2.5
|
|
end
|
|
|
|
function SandstormThink()
|
|
return CastSandstorm()
|
|
end
|
|
|
|
function CastSandstorm()
|
|
ExecuteOrderFromTable({
|
|
UnitIndex = thisEntity:entindex(),
|
|
OrderType = DOTA_UNIT_ORDER_CAST_NO_TARGET,
|
|
AbilityIndex = SandStorm:entindex()
|
|
})
|
|
|
|
return 1.0
|
|
end
|
|
|
|
function EpicenterThink()
|
|
return CastEpicenter()
|
|
end
|
|
|
|
function CastEpicenter()
|
|
ExecuteOrderFromTable({
|
|
UnitIndex = thisEntity:entindex(),
|
|
OrderType = DOTA_UNIT_ORDER_CAST_NO_TARGET,
|
|
AbilityIndex = Epicenter:entindex()
|
|
})
|
|
|
|
return 1.0
|
|
end
|
|
|
|
function PhysicalThink( enemies )
|
|
if thisEntity:FindModifierByName( "modifier_sand_king_boss_directional_move" ) then
|
|
return 0.2
|
|
end
|
|
|
|
local vDirection = thisEntity:GetForwardVector()
|
|
local vRight = thisEntity:GetRightVector()
|
|
local vLeft = -vRight
|
|
local flQuadrantDistance = 200
|
|
local bBurrowed = thisEntity:FindModifierByName( "modifier_sand_king_boss_burrow" ) ~= nil
|
|
|
|
local vFrontRightQuadrant = thisEntity:GetOrigin() + (( vDirection + vRight ) * flQuadrantDistance )
|
|
local vFrontLeftQuadrant = thisEntity:GetOrigin() + (( vDirection + vLeft ) * flQuadrantDistance )
|
|
local vBackRightQuadrant = thisEntity:GetOrigin() + (( -vDirection + vRight ) * flQuadrantDistance )
|
|
local vBackLeftQuadrant = thisEntity:GetOrigin() + (( -vDirection + vLeft ) * flQuadrantDistance )
|
|
local frontRightEnemies = FindUnitsInRadius( DOTA_TEAM_BADGUYS, vFrontRightQuadrant, enemies[1], 450, DOTA_UNIT_TARGET_TEAM_ENEMY, DOTA_UNIT_TARGET_HERO, DOTA_UNIT_TARGET_FLAG_NOT_CREEP_HERO + DOTA_UNIT_TARGET_FLAG_MAGIC_IMMUNE_ENEMIES, 0, false )
|
|
local frontLeftEnemies = FindUnitsInRadius( DOTA_TEAM_BADGUYS, vFrontLeftQuadrant, enemies[1], 450, DOTA_UNIT_TARGET_TEAM_ENEMY, DOTA_UNIT_TARGET_HERO, DOTA_UNIT_TARGET_FLAG_NOT_CREEP_HERO + DOTA_UNIT_TARGET_FLAG_MAGIC_IMMUNE_ENEMIES, 0, false )
|
|
local backRightEnemies = FindUnitsInRadius( DOTA_TEAM_BADGUYS, vBackRightQuadrant, enemies[1], 300, DOTA_UNIT_TARGET_TEAM_ENEMY, DOTA_UNIT_TARGET_HERO, DOTA_UNIT_TARGET_FLAG_NOT_CREEP_HERO + DOTA_UNIT_TARGET_FLAG_MAGIC_IMMUNE_ENEMIES, 0, false )
|
|
local backLeftEnemies = FindUnitsInRadius( DOTA_TEAM_BADGUYS, vBackLeftQuadrant, enemies[1], 300, DOTA_UNIT_TARGET_TEAM_ENEMY, DOTA_UNIT_TARGET_HERO, DOTA_UNIT_TARGET_FLAG_NOT_CREEP_HERO + DOTA_UNIT_TARGET_FLAG_MAGIC_IMMUNE_ENEMIES, 0, false )
|
|
|
|
local bEnemiesBehind = true
|
|
if #backRightEnemies == 0 and #backLeftEnemies == 0 then
|
|
bEnemiesBehind = false
|
|
end
|
|
|
|
local bEnemiesInFront = true
|
|
if #frontRightEnemies == 0 and #frontLeftEnemies == 0 then
|
|
bEnemiesInFront = false
|
|
end
|
|
|
|
if bBurrowed then
|
|
return CastForwardsTailSwipe( enemies[1] )
|
|
end
|
|
|
|
if bBurrowed then
|
|
return 1
|
|
end
|
|
|
|
if Blink ~= nil and Blink:IsFullyCastable() then
|
|
--print ( "Use Blink" )
|
|
return CastBlink( enemies[#enemies]:GetOrigin() )
|
|
else
|
|
if Blink == nil then
|
|
--print ( "blink is nil" )
|
|
Blink = thisEntity:FindItemInInventory( "item_blink" )
|
|
end
|
|
end
|
|
|
|
if Shivas ~= nil and Shivas:IsFullyCastable() then
|
|
return CastShivas()
|
|
else
|
|
if Shivas == nil then
|
|
--print ( "shivas is nil" )
|
|
Shivas = thisEntity:FindItemInInventory( "item_shivas_guard" )
|
|
end
|
|
end
|
|
|
|
if bEnemiesBehind == true and TailIsReady() then
|
|
if #backLeftEnemies > 0 and #backRightEnemies > 0 then
|
|
local nRoll = RandomInt( 0, 1 )
|
|
if nRoll == 0 then
|
|
return CastTailSwipeLeft()
|
|
else
|
|
return CastTailSwipeRight()
|
|
end
|
|
else
|
|
if #backLeftEnemies > 0 then
|
|
return CastTailSwipeLeft()
|
|
end
|
|
if #backRightEnemies > 0 then
|
|
return CastTailSwipeRight()
|
|
end
|
|
end
|
|
end
|
|
|
|
if TailIsReady() == false and WalkIsReady() == true and RandomInt( 0, 3 ) == 0 then
|
|
if enemies[1] ~= nil then
|
|
local flDist = ( enemies[1]:GetOrigin() - thisEntity:GetOrigin() ):Length2D()
|
|
if flDist <= 200 and DirectionalMoveBack:IsCooldownReady() then
|
|
return MoveBack( enemies[1] )
|
|
else
|
|
return MoveSideWays( enemies[1] )
|
|
end
|
|
end
|
|
end
|
|
|
|
return Attack( enemies[1] )
|
|
end
|
|
|
|
function CastBlink( vPos )
|
|
--print ( "Use Blink" )
|
|
ExecuteOrderFromTable({
|
|
UnitIndex = thisEntity:entindex(),
|
|
OrderType = DOTA_UNIT_ORDER_CAST_POSITION,
|
|
Position = vPos,
|
|
AbilityIndex = Blink:entindex()
|
|
})
|
|
|
|
return 0.5
|
|
end
|
|
|
|
function CastShivas()
|
|
--print ( "Use Shivas" )
|
|
ExecuteOrderFromTable({
|
|
UnitIndex = thisEntity:entindex(),
|
|
OrderType = DOTA_UNIT_ORDER_CAST_NO_TARGET,
|
|
AbilityIndex = Shivas:entindex()
|
|
})
|
|
|
|
return 0.25
|
|
end
|
|
|
|
function Attack( enemy )
|
|
ExecuteOrderFromTable({
|
|
UnitIndex = thisEntity:entindex(),
|
|
OrderType = DOTA_UNIT_ORDER_ATTACK_MOVE,
|
|
Position = enemy:GetOrigin(),
|
|
})
|
|
|
|
return 2.0
|
|
end
|
|
|
|
function MoveBack( enemy )
|
|
ExecuteOrderFromTable({
|
|
UnitIndex = thisEntity:entindex(),
|
|
OrderType = DOTA_UNIT_ORDER_CAST_TARGET,
|
|
TargetIndex = enemy:entindex(),
|
|
AbilityIndex = DirectionalMoveBack:entindex(),
|
|
})
|
|
|
|
return 1.0
|
|
end
|
|
|
|
function MoveSideWays( enemy )
|
|
local Ability = DirectionalMoveRight
|
|
if RandomInt( 0, 1 ) == 1 then
|
|
Ability = DirectionalMoveLeft
|
|
end
|
|
ExecuteOrderFromTable({
|
|
UnitIndex = thisEntity:entindex(),
|
|
OrderType = DOTA_UNIT_ORDER_CAST_TARGET,
|
|
TargetIndex = enemy:entindex(),
|
|
AbilityIndex = Ability:entindex(),
|
|
})
|
|
|
|
return 1.0
|
|
end
|
|
|
|
function CastClawAttack( enemy )
|
|
ExecuteOrderFromTable({
|
|
UnitIndex = thisEntity:entindex(),
|
|
OrderType = DOTA_UNIT_ORDER_CAST_TARGET,
|
|
TargetIndex = enemy:entindex(),
|
|
AbilityIndex = ClawAttack:entindex(),
|
|
})
|
|
|
|
return 1.1
|
|
end
|
|
|
|
function CastTailSwipeLeft()
|
|
ExecuteOrderFromTable({
|
|
UnitIndex = thisEntity:entindex(),
|
|
OrderType = DOTA_UNIT_ORDER_CAST_NO_TARGET,
|
|
AbilityIndex = TailSwipeLeft:entindex()
|
|
})
|
|
|
|
return 2.5
|
|
end
|
|
|
|
function CastTailSwipeRight()
|
|
ExecuteOrderFromTable({
|
|
UnitIndex = thisEntity:entindex(),
|
|
OrderType = DOTA_UNIT_ORDER_CAST_NO_TARGET,
|
|
AbilityIndex = TailSwipeRight:entindex()
|
|
})
|
|
|
|
return 2.5
|
|
end
|
|
|
|
|
|
function CastBackwardsTailSwipe()
|
|
ExecuteOrderFromTable({
|
|
UnitIndex = thisEntity:entindex(),
|
|
OrderType = DOTA_UNIT_ORDER_CAST_NO_TARGET,
|
|
AbilityIndex = BackwardsTailSwipe:entindex()
|
|
})
|
|
|
|
return 2.5
|
|
end
|
|
|
|
function CastForwardsTailSwipe( enemy )
|
|
ExecuteOrderFromTable({
|
|
UnitIndex = thisEntity:entindex(),
|
|
OrderType = DOTA_UNIT_ORDER_CAST_TARGET,
|
|
TargetIndex = enemy:entindex(),
|
|
AbilityIndex = ForwardTailSwipe:entindex(),
|
|
})
|
|
|
|
return 2.5
|
|
end
|