1require "common"
2
3
4local DebugEnabled = false
5
6local function EchoDebug(inStr)
7	if DebugEnabled then
8		game:SendToConsole("ScoutBehaviour: " .. inStr)
9	end
10end
11
12function IsScout(unit)
13	local unitName = unit:Internal():Name()
14	if scoutList[unitName] then
15		return true
16	else
17		return false
18	end
19end
20
21ScoutBehaviour = class(Behaviour)
22
23function ScoutBehaviour:Init()
24	self.evading = false
25	self.active = false
26	local mtype, network = ai.maphandler:MobilityOfUnit(self.unit:Internal())
27	self.mtype = mtype
28	self.name = self.unit:Internal():Name()
29	self.armed = unitTable[self.name].isWeapon
30	self.keepYourDistance = unitTable[self.name].losRadius * 16
31	if mtype == "air" then
32		self.airDistance = unitTable[self.name].losRadius * 48
33		self.lastCircleFrame = game:Frame()
34	end
35	self.lastUpdateFrame = game:Frame()
36end
37
38function ScoutBehaviour:UnitBuilt(unit)
39
40end
41
42function ScoutBehaviour:UnitIdle(unit)
43
44end
45
46function ScoutBehaviour:Priority()
47	return 50
48end
49
50function ScoutBehaviour:Activate()
51	EchoDebug("activated on " .. self.name)
52	self.active = true
53end
54
55function ScoutBehaviour:Deactivate()
56	EchoDebug("deactivated on " .. self.name)
57	self.active = false
58	self.target = nil
59	self.evading = false
60	self.attacking = false
61end
62
63
64function ScoutBehaviour:Update()
65	if self.active then
66		local f = game:Frame()
67		if f > self.lastUpdateFrame + 30 then
68			local unit = self.unit:Internal()
69			-- reset target if it's in sight
70			if self.target ~= nil then
71				local los = ai.scouthandler:ScoutLos(self, self.target)
72				EchoDebug("target los: " .. los)
73				if los == 2 or los == 3 then
74					self.target = nil
75				end
76			end
77			-- attack small targets along the way if the scout is armed
78			local attackTarget
79			if self.armed then
80				if ai.targethandler:IsSafePosition(unit:GetPosition(), unit, 1) then
81					attackTarget = ai.targethandler:NearbyVulnerable(unit)
82				end
83			end
84			if attackTarget and not self.attacking then
85				CustomCommand(unit, CMD_ATTACK, {attackTarget.unitID})
86				self.target = nil
87				self.evading = false
88				self.attacking = true
89			elseif self.target ~= nil then
90				-- evade enemies along the way if possible
91				local newPos, arrived = ai.targethandler:BestAdjacentPosition(unit, self.target)
92				if newPos then
93					unit:Move(newPos)
94					self.evading = true
95					self.attacking = false
96				elseif arrived then
97					-- if we're at the target, find a new target
98					self.target = nil
99					self.evading = false
100				elseif self.evading then
101					-- return to course to target after evading
102					unit:Move(self.target)
103					self.evading = false
104					self.attacking = false
105				end
106			end
107			-- find new scout spot if none and not attacking
108			if self.target == nil and attackTarget == nil then
109				local topos = ai.scouthandler:ClosestSpot(self) -- first look for closest metal/geo spot that hasn't been seen recently
110				if topos ~= nil then
111					EchoDebug("scouting spot at " .. topos.x .. "," .. topos.z)
112					self.target = RandomAway(topos, self.keepYourDistance) -- don't move directly onto the spot
113					unit:Move(self.target)
114					self.attacking = false
115				else
116					EchoDebug("nothing to scout!")
117				end
118			end
119			self.lastUpdateFrame = f
120		end
121	end
122
123	-- keep air units circling
124	if self.mtype == "air" and self.active then
125		local f = game:Frame()
126		if f > self.lastCircleFrame + 60 then
127			local unit = self.unit:Internal()
128			local upos = unit:GetPosition()
129			if self.target then
130				local dist = Distance(upos, self.target)
131				if dist < self.airDistance then
132					unit:Move(RandomAway(self.target, 100))
133				end
134			else
135				unit:Move(RandomAway(upos, 500))
136			end
137			self.lastCircleFrame = f
138		end
139	end
140end