//===========================================================================
// Walkability checks
//===========================================================================
function IsTerrainWalkable takes real x, real y returns boolean
local real dX
local real dY
call SetItemVisible(udg_PathingChecker, true)
call SetItemPosition(udg_PathingChecker, x, y)
set dX = GetItemX(udg_PathingChecker)
set dY = GetItemY(udg_PathingChecker)
call SetItemVisible(udg_PathingChecker, false)
return (x - dX) * (x - dX) + (y - dY) * (y - dY) < 10 * 10
endfunction
function IsTerrainWalkableLoc takes location l returns boolean
local real x = GetLocationX(l)
local real y = GetLocationY(l)
local real dX
local real dY
call SetItemVisible(udg_PathingChecker, true)
call SetItemPosition(udg_PathingChecker, x, y)
set dX = GetItemX(udg_PathingChecker)
set dY = GetItemY(udg_PathingChecker)
call SetItemVisible(udg_PathingChecker, false)
return (x - dX) * (x - dX) + (y - dY) * (y - dY) < 10 * 10
endfunction
//===========================================================================
// Spiral search for a walkable point near (x,y)
//===========================================================================
function AI_FindWalkablePoint takes real x, real y, real step, real maxRadius returns nothing
local real angle = 0.0
local real radius = step
local real tx
local real ty
loop
exitwhen radius > maxRadius
set angle = 0.0
loop
exitwhen angle >= 6.28319
set tx = x + radius * Cos(angle)
set ty = y + radius * Sin(angle)
if IsTerrainWalkable(tx, ty) then
set udg_AI_TempTargetX = tx
set udg_AI_TempTargetY = ty
return
endif
set angle = angle + 0.5
endloop
set radius = radius + step
endloop
// fallback: original point
set udg_AI_TempTargetX = x
set udg_AI_TempTargetY = y
endfunction
//===========================================================================
// Smart target selection with focus fire and threat weighting
//===========================================================================
function AI_GetBestTarget takes unit hero, group g returns unit
local unit u
local unit best = null
local real bestScore = 999999.0
local real dx
local real dy
local real dist
local real hp
local real score
local unit lastAttacker
loop
set u = FirstOfGroup(g)
exitwhen u == null
set dx = GetUnitX(hero) - GetUnitX(u)
set dy = GetUnitY(hero) - GetUnitY(u)
set dist = SquareRoot(dx*dx + dy*dy)
set hp = GetUnitLifePercent(u) * 100.0
set score = dist
// Hero priority
if IsUnitType(u, UNIT_TYPE_HERO) then
set score = score * 0.6
endif
// Low HP bonus
set score = score - (100.0 - hp) * 1.5
// Close range bonus
if dist < 300.0 then
set score = score * 0.8
endif
// Focus fire bonus
set lastAttacker = LoadUnitHandle(udg_AI_Hashtable, GetHandleId(hero), 1)
if u == lastAttacker then
set score = score * 0.7
endif
// Threat bonus for dangerous units (adjust rawcodes to your map)
if GetUnitTypeId(u) == 'hcth' or GetUnitTypeId(u) == 'hwat' then
set score = score * 0.8
endif
if score < bestScore then
set bestScore = score
set best = u
endif
call GroupRemoveUnit(g, u)
endloop
return best
endfunction
//===========================================================================
// Per‑unit order gate with priority and cooldown
//===========================================================================
function AI_CanIssueOrder takes unit u, integer orderId, unit target, integer priority returns boolean
local integer id = GetHandleId(u)
local real lastTime = LoadReal(udg_AI_Hashtable, id, 7)
local integer lastPrio = LoadInteger(udg_AI_Hashtable, id, 8)
local real now = TimerGetElapsed(udg_AI_GameTimer)
local real cooldown = 0.35
local integer lastOrder
local unit lastTarget
// Cooldown and priority check
if now - lastTime < cooldown and priority <= lastPrio then
return false
endif
// Same order / same target check
// For move orders (851986) with a target (attack‑move), we DO want to check repetition.
// For pure move orders (target == null), we allow them to be repeated freely.
if orderId != 851986 or target != null then
set lastOrder = LoadInteger(udg_AI_Hashtable, id, 5)
set lastTarget = LoadUnitHandle(udg_AI_Hashtable, id, 6)
if lastOrder == orderId and ((target == null and lastTarget == null) or (target == lastTarget)) then
return false
endif
endif
// Update records
call SaveReal(udg_AI_Hashtable, id, 7, now)
call SaveInteger(udg_AI_Hashtable, id, 8, priority)
call SaveInteger(udg_AI_Hashtable, id, 5, orderId)
if target != null then
call SaveUnitHandle(udg_AI_Hashtable, id, 6, target)
else
call RemoveSavedHandle(udg_AI_Hashtable, id, 6)
endif
return true
endfunction