1-- require "taskqueues"
2require "common"
3
4local DebugEnabled = false
5
6local function EchoDebug(inStr)
7	if DebugEnabled then
8		game:SendToConsole("AssistBehaviour: " .. inStr)
9	end
10end
11
12local CMD_GUARD = 25
13local CMD_PATROL = 15
14
15AssistBehaviour = class(Behaviour)
16
17function AssistBehaviour:DoIAssist()
18	if ai.nonAssistant[self.id] ~= true or self.isNanoTurret then
19		return true
20	else
21		return false
22	end
23end
24
25function AssistBehaviour:Init()
26	self.active = false
27	self.target = nil
28	-- keeping track of how many of each type of unit
29	local uname = self.unit:Internal():Name()
30	self.name = uname
31	if nanoTurretList[uname] then self.isNanoTurret = true end
32	if commanderList[uname] then self.isCommander = true end
33	self.id = self.unit:Internal():ID()
34	ai.assisthandler:AssignIDByName(self)
35	EchoDebug(uname .. " " .. ai.IDByName[self.id])
36	EchoDebug("AssistBehaviour: added to unit "..uname)
37end
38
39function AssistBehaviour:UnitBuilt(unit)
40	if unit.engineID == self.unit.engineID then
41		if self.isNanoTurret then
42			-- set nano turrets to patrol
43			local upos = RandomAway(self.unit:Internal():GetPosition(), 50)
44			local floats = api.vectorFloat()
45			-- populate with x, y, z of the position
46			floats:push_back(upos.x)
47			floats:push_back(upos.y)
48			floats:push_back(upos.z)
49			self.unit:Internal():ExecuteCustomCommand(CMD_PATROL, floats)
50		end
51	end
52end
53
54function AssistBehaviour:UnitIdle(unit)
55	if unit.engineID == self.unit.engineID then
56		self.patroling = false
57		self.assisting = nil
58	end
59end
60
61function AssistBehaviour:Update()
62	-- nano turrets don't need updating, they already have a patrol order
63	if self.isNanoTurret then return end
64
65	local f = game:Frame()
66
67	if math.mod(f,180) == 0 then
68		local unit = self.unit:Internal()
69		local uname = self.name
70		if self.isCommander then
71			-- turn commander into build assister if you control more than half the mexes or if it's damaged
72			if ai.nonAssistant[self.id] then
73				if (IsSiegeEquipmentNeeded() or unit:GetHealth() < unit:GetMaxHealth() * 0.9) and ai.factories ~= 0 and ai.conCount > 2 then
74					ai.nonAssistant[self.id] = nil
75					self.unit:ElectBehaviour()
76				end
77			else
78				-- switch commander back to building
79				if (not IsSiegeEquipmentNeeded() and unit:GetHealth() >= unit:GetMaxHealth() * 0.9) or ai.factories == 0 or ai.conCount <= 2 then
80					ai.nonAssistant[self.id] = true
81					self.unit:ElectBehaviour()
82				end
83			end
84		else
85			-- fill empty spots after con units die
86			if ai.IDByName[self.id] > ai.nameCount[uname] then
87				EchoDebug("filling empty spots with " .. uname .. " " .. ai.IDByName[self.id])
88				ai.assisthandler:AssignIDByName(self)
89				EchoDebug("ID now: " .. ai.IDByName[self.id])
90				self.unit:ElectBehaviour()
91			end
92		end
93	end
94
95	if math.mod(f,60) == 0 then
96		if self.active then
97			if self.target ~= nil then
98				if self.assisting ~= self.target then
99					local floats = api.vectorFloat()
100					floats:push_back(self.target)
101					self.unit:Internal():ExecuteCustomCommand(CMD_GUARD, floats)
102					self.assisting = self.target
103					self.patroling = false
104				end
105			elseif not self.patroling then
106				local patrolPos = self.fallbackPos or self.unit:Internal():GetPosition()
107				local pos = RandomAway(patrolPos, 200)
108				local floats = api.vectorFloat()
109				-- populate with x, y, z of the position
110				floats:push_back(pos.x)
111				floats:push_back(pos.y)
112				floats:push_back(pos.z)
113				self.unit:Internal():ExecuteCustomCommand(CMD_PATROL, floats)
114				ai.assisthandler:AddFree(self)
115				self.patroling = true
116			end
117		end
118	end
119end
120
121function AssistBehaviour:Activate()
122	EchoDebug("AssistBehaviour: activated on unit "..self.name)
123	if self:DoIAssist() then
124		ai.assisthandler:Release(self.unit:Internal())
125		ai.assisthandler:AddFree(self)
126	end
127	self.active = true
128	self.target = nil
129end
130
131function AssistBehaviour:Deactivate()
132	EchoDebug("AssistBehaviour: deactivated on unit "..self.name)
133	ai.assisthandler:RemoveWorking(self)
134	ai.assisthandler:RemoveFree(self)
135	self.active = false
136	self.target = nil
137	self.assisting = nil
138end
139
140function AssistBehaviour:Priority()
141	if self:DoIAssist() then
142		return 100
143	else
144		return 0
145	end
146end
147
148function AssistBehaviour:UnitDead(unit)
149	if unit.engineID == self.unit.engineID then
150		ai.assisthandler:RemoveAssistant(self)
151	end
152end
153
154function AssistBehaviour:Assign(builderID)
155	self.target = builderID
156	self.lastAssignFrame = game:Frame()
157end
158
159function AssistBehaviour:SetFallback(position)
160	self.fallbackPos = position
161end
162
163-- assign if not busy (used by factories to occupy idle assistants)
164function AssistBehaviour:SoftAssign(builderID)
165	if self.target == nil then
166		self.target = builderID
167	else
168		if self.lastAssignFrame == nil then
169			self.target = builderID
170		else
171			local f = game:Frame()
172			if f > self.lastAssignFrame + 900 then
173				self.target = builderID
174			end
175		end
176	end
177end