- Joined
- Sep 18, 2009
- Messages
- 195
Ashes of Stratholme
Everything started with the fact that when I played WoW, I always liked that simple idea of enemies moving around the map — some wandering randomly, others following certain paths. When I began creating my campaign, I wanted to simulate something similar, and that’s where the problem with the default enemy AI showed up. How do you tell a unit that it’s in combat and should ignore the script that makes it wander randomly around the region…
So maybe I overthought it, and there’s a much better way to do what I want, but I ended up disabled the default enemy behavior by setting their classification to Worker and turning off the fleeing options. On top of that, in the game constants I set Call for Help to 0 and gave Guard Distance an absurdly high value.
Everything looked great — except now the unit doesn’t participate in combat at all
So after a few more iterations I ended up overriding the default enemy behavior. At the start of the map, the whole area gets split into regions, and each region knows about its neighbors — so up to 8 of them. Whenever a unit enters a region, a dynamic event fires, the unit gets assigned to the group that matches that region’s ID, and it’s removed from the previous group it belonged to.
Now, every 0.05 seconds the script cycles through X regions (some of them are “asleep” if nothing is happening there) and checks whether a fight can start. It takes the group of units in that region plus the groups from neighboring regions, and for each unit it tries to find the best possible enemy target. If a unit finds a target, it also has a script that lets it call for help.
Potential opponent collection script for region id:
So what can I actually do with this system? Well, every unit has its own:
Fight ending script:
I can create more or less aggressive units. For example, zombies that only attack when you get close, but have a huge call‑for‑help range, so others will rush in as soon as one of them spots you.
So maybe I overthought it, and there’s a much better way to do what I want, but I ended up disabled the default enemy behavior by setting their classification to Worker and turning off the fleeing options. On top of that, in the game constants I set Call for Help to 0 and gave Guard Distance an absurdly high value.
Everything looked great — except now the unit doesn’t participate in combat at all
So after a few more iterations I ended up overriding the default enemy behavior. At the start of the map, the whole area gets split into regions, and each region knows about its neighbors — so up to 8 of them. Whenever a unit enters a region, a dynamic event fires, the unit gets assigned to the group that matches that region’s ID, and it’s removed from the previous group it belonged to.
Now, every 0.05 seconds the script cycles through X regions (some of them are “asleep” if nothing is happening there) and checks whether a fight can start. It takes the group of units in that region plus the groups from neighboring regions, and for each unit it tries to find the best possible enemy target. If a unit finds a target, it also has a script that lets it call for help.
Potential opponent collection script for region id:
JASS:
static method Gather takes integer id, Unit hero returns integer
local unit enumUnit = null
local integer size = 0
local integer i = 0
local integer j = 0
local integer k = 0
local integer neighborId = 0
local Unit u = 0
if SkipEnemyDetection[id] and heroIsNotCloseToArea(id, hero) then
return 0
endif
loop
set enumUnit = BlzGroupUnitAt(CombatGroups[id], i)
exitwhen enumUnit == null
set u = Unit.findUnit(enumUnit)
if u != 0 and u.isAlive and BlzIsUnitInvulnerable(u.unit) == false and u.isInAction == false then
set CombatDetectionGroup[size] = u
set size = size + 1
endif
set enumUnit = null
set u = 0
set i = i + 1
endloop
if size == 0 then
return size
endif
loop
exitwhen j >= NeighborCount[id]
set k = 0
set neighborId = Neighbors[id][j]
set j = j + 1
loop
set enumUnit = BlzGroupUnitAt(CombatGroups[neighborId], k)
exitwhen enumUnit == null
set u = Unit.findUnit(enumUnit)
if u != 0 and u.isAlive and BlzIsUnitInvulnerable(u.unit) == false and u.isInAction == false then
set CombatDetectionGroup[size] = u
set size = size + 1
endif
set enumUnit = null
set u = 0
set k = k + 1
endloop
endloop
set hero = 0
return size
endmethod
So what can I actually do with this system? Well, every unit has its own:
- enemy detection range
- chase range
- maximum combat duration
- call‑for‑help range
Fight ending script:
JASS:
static method leaveCombat takes nothing returns nothing
local thistype node = base.next
local boolean isAlive = false
local boolean inCombat = false
loop
exitwhen node == base
set isAlive = node.unit.isAlive
set inCombat = node.unit.isInCombat
if udg_CinematicOn == false and node.unit.isInCombat then
set node.unit.increaseInCombatTime = 0.05
set node.unit.decreaseRemainingInCombatTime = 0.05
endif
if(node.unit.unit == udg_Hero and isAlive and inCombat and node.fightIsOver) then
set node.unit.state = Unit.UNIT_STATE_OUTSITE_COMBAT
call node.clear()
set udg_InCombatUnit = udg_Hero
set udg_InCombatEvent = 2.00
set udg_InCombatEvent = 0.00
set udg_InCombatUnit = null
endif
if(isAlive and inCombat and node.unit.isPlayerUnit == false and (node.outOfRange or node.fightIsOver)) then
call node.unit.orderToReturn()
endif
set node = node.next
endloop
endmethod
I can create more or less aggressive units. For example, zombies that only attack when you get close, but have a huge call‑for‑help range, so others will rush in as soon as one of them spots you.