1require "common"
2
3local DebugEnabled = false
4
5local function EchoDebug(inStr)
6	if DebugEnabled then
7		game:SendToConsole("RaiderBehaviour: " .. inStr)
8	end
9end
10
11local CMD_IDLEMODE = 145
12local CMD_MOVE_STATE = 50
13local MOVESTATE_ROAM = 2
14
15function IsRaider(unit)
16	for i,name in ipairs(raiderList) do
17		if name == unit:Internal():Name() then
18			return true
19		end
20	end
21	return false
22end
23
24RaiderBehaviour = class(Behaviour)
25
26function RaiderBehaviour:Init()
27	local mtype, network = ai.maphandler:MobilityOfUnit(self.unit:Internal())
28	self.mtype = mtype
29	self.name = self.unit:Internal():Name()
30	local utable = unitTable[self.name]
31	if self.mtype == "sub" then
32		self.range = utable.submergedRange
33	else
34		self.range = utable.groundRange
35	end
36	self.id = self.unit:Internal():ID()
37	self.disarmer = raiderDisarms[self.name]
38	if ai.raiderCount[mtype] == nil then
39		ai.raiderCount[mtype] = 1
40	else
41		ai.raiderCount[mtype] = ai.raiderCount[mtype] + 1
42	end
43end
44
45function RaiderBehaviour:UnitDead(unit)
46	if unit.engineID == self.unit.engineID then
47		-- game:SendToConsole("raider " .. self.name .. " died")
48		if self.target then
49			ai.targethandler:AddBadPosition(self.target, self.mtype)
50		end
51		ai.raidhandler:NeedLess(self.mtype)
52		ai.raiderCount[self.mtype] = ai.raiderCount[self.mtype] - 1
53	end
54end
55
56function RaiderBehaviour:UnitIdle(unit)
57	if unit.engineID == self.unit.engineID then
58		self.target = nil
59		self.evading = false
60		-- keep planes from landing (i'd rather set land state, but how?)
61		if self.mtype == "air" then
62			unit:Internal():Move(RandomAway(unit:Internal():GetPosition(), 500))
63		end
64		self.unit:ElectBehaviour()
65	end
66end
67
68function RaiderBehaviour:RaidCell(cell)
69	EchoDebug(self.name .. " raiding cell...")
70	if self.unit == nil then
71		EchoDebug("no raider unit to raid cell with!")
72		-- ai.raidhandler:RemoveRecruit(self)
73	elseif self.unit:Internal() == nil then
74		EchoDebug("no raider unit internal to raid cell with!")
75		-- ai.raidhandler:RemoveRecruit(self)
76	else
77		if self.buildingIDs ~= nil then
78			ai.raidhandler:IDsWeAreNotRaiding(self.buildingIDs)
79		end
80		ai.raidhandler:IDsWeAreRaiding(cell.buildingIDs, self.mtype)
81		self.buildingIDs = cell.buildingIDs
82		self.target = RandomAway(cell.pos, self.range * 0.5)
83		if self.mtype == "air" then
84			if self.disarmer then
85				self.unitTarget = cell.disarmTarget
86			else
87				self.unitTarget = cell.targets.air.ground
88			end
89			EchoDebug("air raid target: " .. tostring(self.unitTarget.unitName))
90		end
91		if self.active then
92			if self.mtype == "air" then
93				if self.unitTarget ~= nil then
94					CustomCommand(self.unit:Internal(), CMD_ATTACK, {self.unitTarget.unitID})
95				end
96			else
97				self.unit:Internal():Move(self.target)
98			end
99		end
100		self.unit:ElectBehaviour()
101	end
102end
103
104function RaiderBehaviour:Priority()
105	if not self.target then
106		-- revert to scouting
107		return 0
108	else
109		return 100
110	end
111end
112
113function RaiderBehaviour:Activate()
114	EchoDebug(self.name .. " active")
115	self.active = true
116	if self.target then
117		if self.mtype == "air" then
118			if self.unitTarget ~= nil then
119				CustomCommand(self.unit:Internal(), CMD_ATTACK, {self.unitTarget.unitID})
120			end
121		else
122			self.unit:Internal():Move(self.target)
123		end
124	end
125end
126
127function RaiderBehaviour:Deactivate()
128	EchoDebug(self.name .. " inactive")
129	self.active = false
130	self.target = nil
131end
132
133function RaiderBehaviour:Update()
134	local f = game:Frame()
135
136	if not self.active then
137		if math.mod(f, 89) == 0 then
138			local unit = self.unit:Internal()
139			local bestCell = ai.targethandler:GetBestRaidCell(unit)
140			ai.targethandler:RaiderHere(self)
141			EchoDebug(self.name .. " targetting...")
142			if bestCell then
143				EchoDebug(self.name .. " got target")
144				self:RaidCell(bestCell)
145			else
146				self.target = nil
147				self.unit:ElectBehaviour()
148				-- revert to scouting
149			end
150		end
151	else
152		if math.mod(f, 29) == 0 then
153			-- attack nearby vulnerables immediately
154			local unit = self.unit:Internal()
155			local attackTarget
156			if ai.targethandler:IsSafePosition(unit:GetPosition(), unit, 1) then
157				attackTarget = ai.targethandler:NearbyVulnerable(unit)
158			end
159			if attackTarget then
160				CustomCommand(unit, CMD_ATTACK, {attackTarget.unitID})
161			else
162				-- evade enemies on the way to the target, if possible
163				if self.target ~= nil then
164					local newPos, arrived = ai.targethandler:BestAdjacentPosition(unit, self.target)
165					ai.targethandler:RaiderHere(self)
166					if newPos then
167						EchoDebug(self.name .. " evading")
168						unit:Move(newPos)
169						self.evading = true
170					elseif arrived then
171						EchoDebug(self.name .. " arrived")
172						-- if we're at the target
173						self.evading = false
174					elseif self.evading then
175						EchoDebug(self.name .. " setting course to taget")
176						-- return to course to target after evading
177						if self.mtype == "air" then
178							if self.unitTarget ~= nil then
179								CustomCommand(self.unit:Internal(), CMD_ATTACK, {self.unitTarget.unitID})
180							end
181						else
182							self.unit:Internal():Move(self.target)
183						end
184						self.evading = false
185					end
186				end
187			end
188		end
189	end
190end
191
192-- set all raiders to roam
193function RaiderBehaviour:SetMoveState()
194	local thisUnit = self.unit
195	if thisUnit then
196		local floats = api.vectorFloat()
197		floats:push_back(MOVESTATE_ROAM)
198		thisUnit:Internal():ExecuteCustomCommand(CMD_MOVE_STATE, floats)
199		if self.mtype == "air" then
200			local floats = api.vectorFloat()
201			floats:push_back(1)
202			thisUnit:Internal():ExecuteCustomCommand(CMD_IDLEMODE, floats)
203		end
204	end
205end