1--[[
2 Task Queues!
3]]--
4
5require "common"
6
7
8local DebugEnabled = false
9
10local function EchoDebug(inStr)
11	if DebugEnabled then
12		game:SendToConsole("Taskqueues: " .. inStr)
13	end
14end
15
16local random = math.random
17math.randomseed( os.time() + game:GetTeamID() )
18random(); random(); random()
19
20local needAA = false
21local needShields = false
22local needAntinuke = false
23local needtorpedo = false
24local needGroundDefense = true
25
26-- do we need siege equipment such as artillery and merl?
27local needSiege = false
28
29local heavyPlasmaLimit = 3 -- changes with CheckForMapControl
30local AAUnitPerTypeLimit = 3 -- changes with CheckForMapControl
31local nukeLimit = 1 -- changes with CheckForMapControl
32local tacticalNukeLimit = 1 -- changes with CheckForMapControl
33
34local lastCheckFrame = 0
35local lastSiegeCheckFrame = 0
36
37-- build ranges to check for things
38local AreaCheckRange = 1500
39
40local tidalPower = 0
41
42local averageWind = 0
43local needWind = false
44local windRatio = 1
45
46local needAmphibiousCons = false
47
48local minDefenseNetworkSize = 100000
49
50local function MapHasWater()
51	return (ai.waterMap or ai.hasUWSpots) or false
52end
53
54local function CheckMySide(self)
55	-- fix: moved here so map object is present when it's accessed
56	ConUnitPerTypeLimit = math.max(map:SpotCount() / 6, 4)
57	ConUnitAdvPerTypeLimit = math.max(map:SpotCount() / 8, 2)
58	EchoDebug("per-type construction unit limit: " .. ConUnitPerTypeLimit)
59	minDefenseNetworkSize = ai.mobilityGridArea / 4
60	-- set the averageWind
61	if averageWind == 0 then
62		averageWind = map:AverageWind()
63		if averageWind > 11 then
64			needWind = true
65		else
66			needWind = false
67		end
68		local minWind = map:MinimumWindSpeed()
69		if minWind < 8 then
70			windRatio = minWind / 8
71		else
72			windRatio = 1
73		end
74		EchoDebug("wind/solar ratio: " .. windRatio)
75	end
76	-- set the tidal strength
77	if MapHasWater() then
78		if tidalPower == 0 then tidalPower = map:TidalStrength() end
79	else
80		tidalPower = 0
81	end
82	if ai.hasUWSpots and ai.mobRating["sub"] > ai.mobRating["bot"] * 0.75 then
83		needAmphibiousCons = true
84	end
85	if self.unit ~= nil then
86		local tmpName = self.unit:Internal():Name()
87		if tmpName == "corcom" then
88			ai.mySide = CORESideName
89			return DummyUnitName
90		end
91		if tmpName == "armcom" then
92			ai.mySide = ARMSideName
93			return DummyUnitName
94		end
95		game:SendToConsole("Unexpected start unit: "..tmpName..", cannot determine it's race. Assuming CORE")
96		ai.mySide = CORESideName
97	else
98		game:SendToConsole("Unexpected start unit: nil, cannot determine it's race. Assuming CORE")
99		ai.mySide = CORESideName
100	end
101	return DummyUnitName
102end
103
104-- this is initialized in maphandler
105local function MapHasUnderwaterMetal()
106	return ai.hasUWSpots or false
107end
108
109-- check if siege units are needed
110-- check if advanced and experimental factories are needed
111-- check if nukes are needed
112-- check if reclaiming is needed
113function CheckForMapControl()
114	local f = game:Frame()
115	if (lastSiegeCheckFrame + 240) < f then
116		ai.haveAdvFactory = false
117		if ai.factoriesAtLevel[3] ~= nil then
118			ai.haveAdvFactory = #ai.factoriesAtLevel[3] ~= 0
119		end
120		ai.haveExpFactory = false
121		if ai.factoriesAtLevel[5] ~= nil then
122			ai.haveExpFactory = #ai.factoriesAtLevel[5] ~= 0
123		end
124
125		lastSiegeCheckFrame = f
126		ai.needToReclaim = ai.Metal.full < 0.5 and ai.wreckCount > 0
127		AAUnitPerTypeLimit = math.ceil(ai.turtlehandler:GetTotalPriority() / 4)
128		heavyPlasmaLimit = math.ceil(ai.combatCount / 10)
129		nukeLimit = math.ceil(ai.combatCount / 50)
130		tacticalNukeLimit = math.ceil(ai.combatCount / 40)
131
132		local attackCounter = ai.attackhandler:GetCounter()
133		local couldAttack = ai.couldAttack >= 1 or ai.couldBomb >= 1
134		local bombingTooExpensive = ai.bomberhandler:GetCounter() == maxBomberCounter
135		local attackTooExpensive = attackCounter == maxAttackCounter
136		local controlMetalSpots = ai.mexCount > #ai.mobNetworkMetals["air"][1] * 0.4
137		local needUpgrade = couldAttack or bombingTooExpensive or attackTooExpensive
138		local lotsOfMetal = ai.Metal.income > 25 or controlMetalSpots
139
140		EchoDebug(ai.totalEnemyThreat .. " " .. ai.totalEnemyImmobileThreat .. " " .. ai.totalEnemyMobileThreat)
141		-- build siege units if the enemy is turtling, if a lot of our attackers are getting destroyed, or if we control over 40% of the metal spots
142		needSiege = (ai.totalEnemyImmobileThreat > ai.totalEnemyMobileThreat * 3.5 and ai.totalEnemyImmobileThreat > 50000) or attackCounter >= siegeAttackCounter or controlMetalSpots
143		ai.needAdvanced = (ai.Metal.income > 10 or controlMetalSpots) and ai.factories > 0 and (needUpgrade or lotsOfMetal)
144		ai.needExperimental = false
145		ai.needNukes = false
146		if ai.Metal.income > 50 and ai.haveAdvFactory and needUpgrade and ai.enemyBasePosition then
147			if not ai.haveExpFactory then
148				for i, factory in pairs(ai.factoriesAtLevel[ai.maxFactoryLevel]) do
149					if ai.maphandler:MobilityNetworkHere("bot", factory.position) == ai.maphandler:MobilityNetworkHere("bot", ai.enemyBasePosition) then
150						ai.needExperimental = true
151						break
152					end
153				end
154			end
155			ai.needNukes = true
156		end
157		EchoDebug("need experimental? " .. tostring(ai.needExperimental) .. ", need nukes? " .. tostring(ai.needNukes) .. ", have advanced? " .. tostring(ai.haveAdvFactory) .. ", need upgrade? " .. tostring(needUpgrade) .. ", have enemy base position? " .. tostring(ai.enemyBasePosition))
158		EchoDebug("metal income: " .. ai.Metal.income .. "  combat units: " .. ai.combatCount)
159		EchoDebug("have advanced? " .. tostring(ai.haveAdvFactory) .. " have experimental? " .. tostring(ai.haveExpFactory))
160		EchoDebug("need advanced? " .. tostring(ai.needAdvanced) .. "  need experimental? " .. tostring(ai.needExperimental))
161		EchoDebug("need advanced? " .. tostring(ai.needAdvanced) .. ", need upgrade? " .. tostring(needUpgrade) .. ", have attacked enough? " .. tostring(couldAttack) .. " (" .. ai.couldAttack .. "), have " .. ai.factories .. " factories, " .. math.floor(ai.Metal.income) .. " metal income")
162	end
163end
164
165function IsSiegeEquipmentNeeded()
166	CheckForMapControl()
167	return needSiege
168end
169
170function IsAANeeded()
171	return ai.needAirDefense
172end
173
174function IsShieldNeeded()
175	return ai.needShields
176end
177
178function IsTorpedoNeeded()
179	return ai.needSubmergedDefense
180end
181
182function IsJammerNeeded()
183	return ai.needJammers
184end
185
186function IsAntinukeNeeded()
187	return ai.needAntinuke
188end
189
190function IsNukeNeeded()
191	local nuke = ai.needNukes and ai.canNuke
192	return nuke
193end
194
195function IsLandAttackNeeded()
196	return ai.areLandTargets or ai.needGroundDefense
197end
198
199function IsWaterAttackNeeded()
200	return ai.areWaterTargets or ai.needSubmergedDefense
201end
202
203function BuildMex()
204	local unitName
205	if ai.mySide == CORESideName then
206		unitName = "cormex"
207	else
208		unitName = "armmex"
209	end
210	return unitName
211end
212
213function BuildUWMex()
214	local unitName
215	if ai.mySide == CORESideName then
216		unitName = "coruwmex"
217	else
218		unitName = "armuwmex"
219	end
220	return unitName
221end
222
223function BuildMohoMex()
224	local unitName
225	if ai.mySide == CORESideName then
226		unitName = "cormoho"
227	else
228		unitName = "armmoho"
229	end
230	return unitName
231end
232
233function BuildUWMohoMex()
234	local unitName
235	if ai.mySide == CORESideName then
236		unitName = "coruwmme"
237	else
238		unitName = "armuwmme"
239	end
240	return unitName
241end
242
243function BuildWindSolarIfNeeded()
244	-- check if we need power
245	if ai.Energy.extra < 0 then
246		retVal = WindSolar
247		EchoDebug("BuildWindSolarIfNeeded: income "..res.income..", usage "..res.usage..", building more energy")
248	else
249		retVal = DummyUnitName
250	end
251
252	return retVal
253end
254
255function TidalIfTidal(self)
256	local unitName = DummyUnitName
257	EchoDebug("tidal power is " .. tidalPower)
258	if tidalPower >= 10 then
259		if ai.mySide == CORESideName then
260			unitName = "cortide"
261		else
262			unitName = "armtide"
263		end
264	end
265	return unitName
266end
267
268-- build conversion or storage
269function DoSomethingForTheEconomy(self)
270	local highEnergy = ai.Energy.full > 0.9
271	local lowEnergy = ai.Energy.full < 0.1
272	local highMetal = ai.Metal.full > 0.9
273	local lowMetal = ai.Metal.full < 0.1
274	local isWater = unitTable[self.unit:Internal():Name()].needsWater
275	local unitName = DummyUnitName
276	-- maybe we need conversion?
277	if ai.Energy.extra > 80 and highEnergy and lowMetal and ai.Metal.extra < 0 and ai.Energy.income > 300 then
278		local converterLimit = math.min(math.floor(ai.Energy.income / 200), 4)
279		if isWater then
280			if ai.mySide == CORESideName then
281				unitName = BuildWithLimitedNumber("corfmkr", converterLimit)
282			else
283				unitName = BuildWithLimitedNumber("armfmkr", converterLimit)
284			end
285		else
286			if ai.mySide == CORESideName then
287				unitName = BuildWithLimitedNumber("cormakr", converterLimit)
288			else
289				unitName = BuildWithLimitedNumber("armmakr", converterLimit)
290			end
291		end
292	end
293	-- maybe we need storage?
294	if unitName == DummyUnitName then
295		-- energy storage
296		if ai.Energy.extra > 150 and highEnergy and not lowMetal then
297			if isWater then
298				if ai.mySide == CORESideName then
299					unitName = BuildWithLimitedNumber("coruwes", 2)
300				else
301					unitName = BuildWithLimitedNumber("armuwes", 2)
302				end
303			else
304				if ai.mySide == CORESideName then
305					unitName = BuildWithLimitedNumber("corestor", 2)
306				else
307					unitName = BuildWithLimitedNumber("armestor", 2)
308				end
309			end
310		end
311	end
312	if unitName == DummyUnitName then
313		-- metal storage
314		if ai.Metal.extra > 5 and highMetal and highEnergy then
315			if isWater then
316				if ai.mySide == CORESideName then
317					unitName = BuildWithLimitedNumber("coruwms", 2)
318				else
319					unitName = BuildWithLimitedNumber("armuwms", 2)
320				end
321			else
322				if ai.mySide == CORESideName then
323					unitName = BuildWithLimitedNumber("cormstor", 2)
324				else
325					unitName = BuildWithLimitedNumber("armmstor", 2)
326				end
327			end
328		end
329	end
330
331	return unitName
332end
333
334
335-- build advanced conversion or storage
336function DoSomethingAdvancedForTheEconomy(self)
337	local highEnergy = ai.Energy.full > 0.9
338	local lowEnergy = ai.Energy.full < 0.1
339	local highMetal = ai.Metal.full > 0.9
340	local lowMetal = ai.Metal.full < 0.1
341	local unitName = self.unit:Internal():Name()
342	local isWater = unitTable[unitName].needsWater or seaplaneConList[unitName]
343	local unitName = DummyUnitName
344	-- maybe we need conversion?
345	if ai.Energy.extra > 800 and highEnergy and lowMetal and ai.Metal.extra < 0 and ai.Energy.income > 2000 then
346		local converterLimit = math.floor(ai.Energy.income / 1000)
347		if isWater then
348			if ai.mySide == CORESideName then
349				unitName = BuildWithLimitedNumber("armuwmmm", converterLimit)
350			else
351				unitName = BuildWithLimitedNumber("armuwmmm", converterLimit)
352			end
353		else
354			if ai.mySide == CORESideName then
355				unitName = BuildWithLimitedNumber("cormmkr", converterLimit)
356			else
357				unitName = BuildWithLimitedNumber("armmmkr", converterLimit)
358			end
359		end
360	end
361	-- building big storage is a waste
362	--[[
363	-- maybe we need storage?
364	if unitName == DummyUnitName then
365		-- energy storage
366		if ai.Energy.extra > 1500 and highEnergy and not lowMetal then
367			if ai.mySide == CORESideName then
368				unitName = BuildWithLimitedNumber("coruwadves", 1)
369			else
370				unitName = BuildWithLimitedNumber("armuwadves", 1)
371			end
372		end
373	end
374	if unitName == DummyUnitName then
375		-- metal storage
376		if ai.Metal.extra > 25 and highMetal and highEnergy then
377			if ai.mySide == CORESideName then
378				unitName = BuildWithLimitedNumber("coruwadvms", 1)
379			else
380				unitName = BuildWithLimitedNumber("armuwadvms", 1)
381			end
382		end
383	end
384	]]--
385
386	return unitName
387end
388
389function BuildAAIfNeeded(unitName)
390	if IsAANeeded() then
391		if not unitTable[unitName].isBuilding then
392			return BuildWithLimitedNumber(unitName, AAUnitPerTypeLimit)
393		else
394			return unitName
395		end
396	else
397		return DummyUnitName
398	end
399end
400
401function BuildTorpedoIfNeeded(unitName)
402	if IsTorpedoNeeded() then
403		return unitName
404	else
405		return DummyUnitName
406	end
407end
408
409function BuildSiegeIfNeeded(unitName)
410	if unitName == DummyUnitName then return DummyUnitName end
411	if IsSiegeEquipmentNeeded() then
412		if ai.siegeCount < (ai.battleCount + ai.breakthroughCount) * 0.35 then
413			return unitName
414		end
415	end
416	return DummyUnitName
417end
418
419function BuildBreakthroughIfNeeded(unitName)
420	if unitName == DummyUnitName or unitName == nil then return DummyUnitName end
421	if IsSiegeEquipmentNeeded() then return unitName end
422	local mtype = unitTable[unitName].mtype
423	if mtype == "air" then
424		local bomberCounter = ai.bomberhandler:GetCounter()
425		if bomberCounter >= breakthroughBomberCounter and bomberCounter < maxBomberCounter then
426			return unitName
427		else
428			return DummyUnitName
429		end
430	else
431		if ai.battleCount <= minBattleCount then return DummyUnitName end
432		local attackCounter = ai.attackhandler:GetCounter(mtype)
433		if attackCounter == maxAttackCounter then
434			return unitName
435		elseif attackCounter >= breakthroughAttackCounter then
436			return unitName
437		else
438			return DummyUnitName
439		end
440	end
441end
442
443function Lvl1VehArty(self)
444	local unitName = ""
445	if ai.mySide == CORESideName then
446		unitName = "corwolv"
447	else
448		unitName = "tawf013"
449	end
450	return BuildSiegeIfNeeded(unitName)
451end
452
453function Lvl1BotBreakthrough(self)
454	local unitName = ""
455	if ai.mySide == CORESideName then
456		unitName = "corthud"
457	else
458		unitName = "armwar"
459	end
460	return BuildBreakthroughIfNeeded(unitName)
461end
462
463function Lvl1VehBreakthrough(self)
464	if ai.mySide == CORESideName then
465		return BuildBreakthroughIfNeeded("corlevlr")
466	else
467		-- armjanus isn't very a very good defense unit by itself
468		local output = BuildSiegeIfNeeded("armjanus")
469		if output == DummyUnitName then
470			output = BuildBreakthroughIfNeeded("armstump")
471		end
472		return output
473	end
474end
475
476function Lvl2VehBreakthrough(self)
477	local unitName = ""
478	if ai.mySide == CORESideName then
479		return BuildBreakthroughIfNeeded("corgol")
480	else
481		-- armmanni isn't very a very good defense unit by itself
482		local output = BuildSiegeIfNeeded("armmanni")
483		if output == DummyUnitName then
484			output = BuildBreakthroughIfNeeded("armbull")
485		end
486		return output
487	end
488end
489
490function Lvl2BotBreakthrough(self)
491	local unitName = ""
492	if ai.mySide == CORESideName then
493		unitName = "corsumo"
494	else
495		unitName = "armfboy"
496	end
497	return BuildBreakthroughIfNeeded(unitName)
498end
499
500function Lvl2BotArty(self)
501	local unitName = ""
502	if ai.mySide == CORESideName then
503		unitName = "cormort"
504	else
505		unitName = "armfido"
506	end
507	return BuildSiegeIfNeeded(unitName)
508end
509
510function Lvl2BotMerl(self)
511	local unitName = ""
512	if ai.mySide == CORESideName then
513		unitName = "corhrk"
514	else
515		return DummyUnitName
516	end
517	return BuildSiegeIfNeeded(unitName)
518end
519
520function Lvl2VehArty(self)
521	local unitName = ""
522	if ai.mySide == CORESideName then
523		unitName = "cormart"
524	else
525		unitName = "armmart"
526	end
527	return BuildSiegeIfNeeded(unitName)
528end
529
530function Lvl2VehMerl(self)
531	local unitName = ""
532	if ai.mySide == CORESideName then
533		unitName = "corvroc"
534	else
535		unitName = "armmerl"
536	end
537	return BuildSiegeIfNeeded(unitName)
538end
539
540function HoverMerl(self)
541	local unitName = ""
542	if ai.mySide == CORESideName then
543		unitName = "cormh"
544	else
545		unitName = "armmh"
546	end
547	return BuildSiegeIfNeeded(unitName)
548end
549
550function Lvl2ShipBreakthrough(self)
551	local unitName = ""
552	if ai.mySide == CORESideName then
553		unitName = "corbats"
554	else
555		unitName = "armbats"
556	end
557	return BuildBreakthroughIfNeeded(unitName)
558end
559
560function Lvl2ShipMerl(self)
561	local unitName = ""
562	if ai.mySide == CORESideName then
563		unitName = "cormship"
564	else
565		unitName = "armmship"
566	end
567	return BuildSiegeIfNeeded(unitName)
568end
569
570function MegaShip()
571	local unitName = ""
572	if ai.mySide == CORESideName then
573		unitName = "corblackhy"
574	else
575		unitName = "aseadragon"
576	end
577	return BuildBreakthroughIfNeeded(BuildWithLimitedNumber(unitName, 1))
578end
579
580function MegaAircraft()
581	if ai.mySide == CORESideName then
582		return BuildBreakthroughIfNeeded("corcrw")
583	else
584		return BuildBreakthroughIfNeeded("armcybr")
585	end
586end
587
588function Lvl3Merl(self)
589	local unitName = ""
590	if ai.mySide == CORESideName then
591		unitName = "armraven"
592	else
593		unitName = DummyUnitName
594	end
595	return BuildSiegeIfNeeded(unitName)
596end
597
598function Lvl3Arty(self)
599	local unitName = ""
600	if ai.mySide == CORESideName then
601		unitName = "shiva"
602	else
603		unitName = "armshock"
604	end
605	return BuildSiegeIfNeeded(unitName)
606end
607
608function Lvl3Breakthrough(self)
609	local unitName = ""
610	if ai.mySide == CORESideName then
611		unitName = BuildWithLimitedNumber("corkrog", 1)
612		if unitName == DummyUnitName then
613			unitName = BuildWithLimitedNumber("gorg", 2)
614		end
615		if unitName == DummyUnitName then
616			unitName = "corkarg"
617		end
618	else
619		unitName = BuildWithLimitedNumber("armbanth", 5)
620		if unitName == DummyUnitName then
621			unitName = "armraz"
622		end
623	end
624	return BuildBreakthroughIfNeeded(unitName)
625end
626
627function BuildRaiderIfNeeded(unitName)
628	if unitName == DummyUnitName or unitName == nil then return DummyUnitName end
629	local mtype = unitTable[unitName].mtype
630	if ai.factoriesAtLevel[3] ~= nil and ai.factoriesAtLevel[3] ~= {} then
631		-- if we have a level 2 factory, don't build raiders until we have some battle units
632		local attackCounter = ai.attackhandler:GetCounter(mtype)
633		if ai.battleCount + ai.breakthroughCount < attackCounter / 2 then
634			return DummyUnitName
635		end
636	end
637	local counter = ai.raidhandler:GetCounter(mtype)
638	if counter == minRaidCounter then return DummyUnitName end
639	if ai.raiderCount[mtype] == nil then
640		-- fine
641	elseif ai.raiderCount[mtype] >= counter then
642		unitName = DummyUnitName
643	end
644	return unitName
645end
646
647function Lvl1VehRaider(self)
648	local unitName = ""
649	if ai.mySide == CORESideName then
650		unitName = "corgator"
651	else
652		unitName = "armflash"
653	end
654	return BuildRaiderIfNeeded(unitName)
655end
656
657-- because core doesn't have a lvl2 vehicle raider or a lvl3 raider
658function Lvl1VehRaiderOutmoded(self)
659	if ai.mySide == CORESideName then
660		return BuildRaiderIfNeeded("corgator")
661	else
662		return DummyUnitName
663	end
664end
665
666
667function Lvl1BotRaider(self)
668	local unitName = ""
669	if ai.mySide == CORESideName then
670		unitName = "corak"
671	else
672		unitName = "armpw"
673	end
674	return BuildRaiderIfNeeded(unitName)
675end
676
677function Lvl1ShipRaider(self)
678	local unitName = ""
679	if ai.mySide == CORESideName then
680		unitName = "corsub"
681	else
682		unitName = "armsub"
683	end
684	return BuildRaiderIfNeeded(unitName)
685end
686
687function Lvl1AirRaider(self)
688	local unitName = ""
689	if ai.mySide == CORESideName then
690		unitName = "bladew"
691	else
692		unitName = "armkam"
693	end
694	return BuildRaiderIfNeeded(unitName)
695end
696
697function Lvl2VehRaider(self)
698	if ai.mySide == CORESideName then
699		return DummyUnitName
700	else
701		return BuildRaiderIfNeeded("armlatnk")
702	end
703end
704
705function Lvl2BotRaider(self)
706	local unitName = ""
707	if ai.mySide == CORESideName then
708		unitName = "corpyro"
709	else
710		unitName = "armfast"
711	end
712	return BuildRaiderIfNeeded(unitName)
713end
714
715function Lvl2ShipRaider(self)
716	local unitName = ""
717	if ai.mySide == CORESideName then
718		unitName = "corshark"
719	else
720		unitName = "armsubk"
721	end
722	return BuildRaiderIfNeeded(unitName)
723end
724
725function Lvl2AirRaider(self)
726	local unitName = ""
727	if ai.mySide == CORESideName then
728		unitName = "corape"
729	else
730		-- spedical case: arm has an ubergunship
731		local raidCounter = ai.raidhandler:GetCounter("air")
732		if raidCounter < baseRaidCounter and raidCounter > minRaidCounter then
733			return "blade"
734		else
735			unitName = "armbrawl"
736		end
737	end
738	return BuildRaiderIfNeeded(unitName)
739end
740
741function HoverRaider(self)
742	local unitName = ""
743	if ai.mySide == CORESideName then
744		unitName = "corsh"
745	else
746		unitName = "armsh"
747	end
748	return BuildRaiderIfNeeded(unitName)
749end
750
751function AmphibiousRaider(self)
752	local unitName = ""
753	if ai.mySide == CORESideName then
754		unitName = "corgarp"
755	else
756		unitName = "armpincer"
757	end
758	return BuildRaiderIfNeeded(unitName)
759end
760
761function Lvl3Raider(self)
762	local unitName = ""
763	if ai.mySide == CORESideName then
764		unitName = DummyUnitName
765	else
766		unitName = "marauder"
767	end
768	return BuildRaiderIfNeeded(unitName)
769end
770
771function BuildBattleIfNeeded(unitName)
772	if unitName == DummyUnitName or unitName == nil then return DummyUnitName end
773	local mtype = unitTable[unitName].mtype
774	local attackCounter = ai.attackhandler:GetCounter(mtype)
775	EchoDebug(mtype .. " " .. attackCounter .. " " .. maxAttackCounter)
776	if attackCounter == maxAttackCounter and ai.battleCount > minBattleCount then return DummyUnitName end
777	if mtype == "veh" and ai.mySide == CORESideName and (ai.factoriesAtLevel[1] == nil or ai.factoriesAtLevel[1] == {}) then
778		-- core only has a lvl1 vehicle raider, so this prevents getting stuck
779		return unitName
780	end
781	if ai.factoriesAtLevel[3] ~= nil and ai.factoriesAtLevel[3] ~= {} then
782		-- if we have a level 2 factory, don't wait to build raiders first
783		return unitName
784	end
785	local raidCounter = ai.raidhandler:GetCounter(mtype)
786	EchoDebug(mtype .. " " .. raidCounter .. " " .. maxRaidCounter)
787	if raidCounter == minRaidCounter then return unitName end
788	EchoDebug(ai.raiderCount[mtype])
789	if ai.raiderCount[mtype] == nil then
790		return unitName
791	elseif ai.raiderCount[mtype] < raidCounter / 2 then
792		return DummyUnitName
793	else
794		return unitName
795	end
796end
797
798function Lvl2BotCorRaiderArmBattle(self)
799	if ai.mySide == CORESideName then
800		return Lvl2BotRaider(self)
801	else
802		return Lvl2BotBattle(self)
803	end
804end
805
806function Lvl1VehBattle(self)
807	local unitName = ""
808	if ai.mySide == CORESideName then
809		unitName = "corraid"
810	else
811		unitName = "armstump"
812	end
813	return BuildBattleIfNeeded(unitName)
814end
815
816function Lvl1BotBattle(self)
817	local unitName = ""
818	local r = math.random(1, 2)
819	if r == 1 then
820		if ai.mySide == CORESideName then
821			unitName = "corthud"
822		else
823			unitName = "armham"
824		end
825	else
826		if ai.mySide == CORESideName then
827			unitName = "corstorm"
828		else
829			unitName = "armrock"
830		end
831	end
832	return BuildBattleIfNeeded(unitName)
833end
834
835function HoverBattle(self)
836	local unitName = ""
837	if ai.mySide == CORESideName then
838		unitName = "corsnap"
839	else
840		unitName = "armanac"
841	end
842	return BuildBattleIfNeeded(unitName)
843end
844
845function HoverBreakthrough(self)
846	local unitName = ""
847	if ai.mySide == CORESideName then
848		unitName = "nsaclash"
849	else
850		unitName = "armanac"
851	end
852	BuildBreakthroughIfNeeded(unitName)
853end
854
855function AmphibiousBattle(self)
856	local unitName = ""
857	if ai.mySide == CORESideName then
858		unitName = "corseal"
859	else
860		unitName = "armcroc"
861	end
862	return BuildBattleIfNeeded(unitName)
863end
864
865function AmphibiousBreakthrough(self)
866	local unitName = ""
867	if ai.mySide == CORESideName then
868		unitName = "corparrow"
869	else
870		unitName = "armcroc"
871	end
872	BuildBreakthroughIfNeeded(unitName)
873end
874
875function Lvl1ShipDestroyerOnly(self)
876	if ai.combatCount > 12 then
877		if ai.mySide == CORESideName then
878			unitName = "corroy"
879		else
880			unitName = "armroy"
881		end
882		return BuildBattleIfNeeded(unitName)
883	end
884end
885
886function Lvl1ShipBattle(self)
887	local unitName = ""
888	local r = 1
889	if ai.combatCount > 12 then r = 2 end -- only build destroyers if you've already got quite a few units (combat = scouts + raiders + battle)
890	if r == 1 then
891		if ai.mySide == CORESideName then
892			unitName = "coresupp"
893		else
894			unitName = "decade"
895		end
896	else
897		if ai.mySide == CORESideName then
898			unitName = "corroy"
899		else
900			unitName = "armroy"
901		end
902	end
903	return BuildBattleIfNeeded(unitName)
904end
905
906function Lvl2VehBattle(self)
907	local unitName = ""
908	if ai.mySide == CORESideName then
909		unitName = "correap"
910	else
911		unitName = "armbull"
912	end
913	return BuildBattleIfNeeded(unitName)
914end
915
916function Lvl2BotBattle(self)
917	local unitName = ""
918	if ai.mySide == CORESideName then
919		unitName = "corcan"
920	else
921		unitName = "armzeus"
922	end
923	return BuildBattleIfNeeded(unitName)
924end
925
926function Lvl2ShipBattle(self)
927	local unitName = ""
928	if ai.mySide == CORESideName then
929		unitName = "corcrus"
930	else
931		unitName = "armcrus"
932	end
933	return BuildBattleIfNeeded(unitName)
934end
935
936function Lvl3Battle(self)
937	local unitName = ""
938	if ai.mySide == CORESideName then
939		unitName = "corkarg"
940	else
941		unitName = "armraz"
942	end
943	return BuildBattleIfNeeded(unitName)
944end
945
946function WindSolar()
947	local wind = false
948	if needWind then
949		if windRatio == 1 then
950			wind = true
951		else
952			local r = math.random()
953			if r < windRatio then wind = true end
954		end
955	end
956	if wind then
957		if ai.mySide == CORESideName then
958			return "corwin"
959		else
960			return "armwin"
961		end
962	else
963		if ai.mySide == CORESideName then
964			return "corsolar"
965		else
966			return "armsolar"
967		end
968	end
969end
970
971function Solar()
972	if ai.mySide == CORESideName then
973		return "corsolar"
974	else
975		return "armsolar"
976	end
977end
978
979local function WindSolarTidal(self)
980	LandOrWater(self, WindSolar(), TidalIfTidal())
981end
982
983function CountOwnUnits(tmpUnitName)
984	if tmpUnitName == DummyUnitName then return 0 end -- don't count no-units
985	if ai.nameCount[tmpUnitName] == nil then return 0 end
986	return ai.nameCount[tmpUnitName]
987end
988
989function BuildWithLimitedNumber(tmpUnitName, minNumber)
990	if tmpUnitName == DummyUnitName then return DummyUnitName end
991	if minNumber == 0 then return DummyUnitName end
992	if ai.nameCount[tmpUnitName] == nil then
993		return tmpUnitName
994	else
995		if ai.nameCount[tmpUnitName] == 0 or ai.nameCount[tmpUnitName] < minNumber then
996			return tmpUnitName
997		else
998			return DummyUnitName
999		end
1000	end
1001end
1002
1003local function SolarAdv()
1004	if ai.mySide == CORESideName then
1005		return "coradvsol"
1006	else
1007		return "armadvsol"
1008	end
1009end
1010
1011local function BuildGeo()
1012	-- don't attempt if there are no spots on the map
1013	if not ai.mapHasGeothermal then
1014		return DummyUnitName
1015	end
1016	if ai.mySide == CORESideName then
1017		return "corgeo"
1018	else
1019		return "armgeo"
1020	end
1021end
1022
1023local function BuildMohoGeo(self)
1024	-- don't attempt if there are no spots on the map
1025	if not ai.mapHasGeothermal then
1026		return DummyUnitName
1027	end
1028	if ai.mySide == CORESideName then
1029		return "cmgeo"
1030	else
1031		return "amgeo"
1032	end
1033	-- will turn into a safe geothermal or a geothermal plasma battery if too close to a factory
1034end
1035
1036local function BuildFusion()
1037	if ai.mySide == CORESideName then
1038		return "corfus"
1039	else
1040		return "armfus"
1041	end
1042	-- will become cafus and aafus in CategoryEconFilter in TaskQueueBehaviour if energy income is higher than 4000
1043end
1044
1045local function BuildUWFusion()
1046	if ai.mySide == CORESideName then
1047		return "coruwfus"
1048	else
1049		return "armuwfus"
1050	end
1051end
1052
1053local function Lvl1AABot()
1054	if ai.mySide == CORESideName then
1055		return BuildAAIfNeeded("corcrash")
1056	else
1057		return BuildAAIfNeeded("armjeth")
1058	end
1059end
1060
1061local function Lvl2AABot()
1062	if ai.mySide == CORESideName then
1063		return BuildAAIfNeeded("coraak")
1064	else
1065		return BuildAAIfNeeded("armaak")
1066	end
1067end
1068
1069local function Lvl1AAVeh()
1070	if ai.mySide == CORESideName then
1071		return BuildAAIfNeeded("cormist")
1072	else
1073		return BuildAAIfNeeded("armsam")
1074	end
1075end
1076
1077local function Lvl2AAVeh()
1078	if ai.mySide == CORESideName then
1079		return BuildAAIfNeeded("corsent")
1080	else
1081		return BuildAAIfNeeded("armyork")
1082	end
1083end
1084
1085local function Lvl2AAShip()
1086	if ai.mySide == CORESideName then
1087		return BuildAAIfNeeded("corarch")
1088	else
1089		return BuildAAIfNeeded("armaas")
1090	end
1091end
1092
1093local function AAHover()
1094	if ai.mySide == CORESideName then
1095		return BuildAAIfNeeded("corah")
1096	else
1097		return BuildAAIfNeeded("armah")
1098	end
1099end
1100
1101local function ConVehicle()
1102	local unitName
1103	if needAmphibiousCons then
1104		if ai.mySide == CORESideName then
1105			unitName = "cormuskrat"
1106		else
1107			unitName = "armbeaver"
1108		end
1109	else
1110		if ai.mySide == CORESideName then
1111			unitName = "corcv"
1112		else
1113			unitName = "armcv"
1114		end
1115	end
1116	return BuildWithLimitedNumber(unitName, ConUnitPerTypeLimit)
1117end
1118
1119local function ConVehicleAmphibious()
1120	if ai.mySide == CORESideName then
1121		unitName = "cormuskrat"
1122	else
1123		unitName = "armbeaver"
1124	end
1125	return BuildWithLimitedNumber(unitName, ConUnitAdvPerTypeLimit)
1126end
1127
1128local function ConAdvVehicle()
1129	if ai.mySide == CORESideName then
1130		return BuildWithLimitedNumber("coracv", ConUnitAdvPerTypeLimit)
1131	else
1132		return BuildWithLimitedNumber("armacv", ConUnitAdvPerTypeLimit)
1133	end
1134end
1135
1136local function ConBot()
1137	if ai.mySide == CORESideName then
1138		return BuildWithLimitedNumber("corck", ConUnitPerTypeLimit)
1139	else
1140		return BuildWithLimitedNumber("armck", ConUnitPerTypeLimit)
1141	end
1142end
1143
1144local function ConCoreBotArmVehicle()
1145	if ai.mySide == CORESideName then
1146		return BuildWithLimitedNumber("corck", ConUnitPerTypeLimit)
1147	else
1148		return BuildWithLimitedNumber("armcv", ConUnitPerTypeLimit)
1149	end
1150end
1151
1152local function ConAdvBot()
1153	if ai.mySide == CORESideName then
1154		return BuildWithLimitedNumber("corack", ConUnitAdvPerTypeLimit)
1155	else
1156		return BuildWithLimitedNumber("armack", ConUnitAdvPerTypeLimit)
1157	end
1158end
1159
1160local function ConAir()
1161	if ai.mySide == CORESideName then
1162		return BuildWithLimitedNumber("corca", ConUnitPerTypeLimit)
1163	else
1164		return BuildWithLimitedNumber("armca", ConUnitPerTypeLimit)
1165	end
1166end
1167
1168local function ConAdvAir()
1169	if ai.mySide == CORESideName then
1170		return BuildWithLimitedNumber("coraca", ConUnitAdvPerTypeLimit)
1171	else
1172		return BuildWithLimitedNumber("armaca", ConUnitAdvPerTypeLimit)
1173	end
1174end
1175
1176local function ConSeaAir()
1177	if ai.mySide == CORESideName then
1178		return BuildWithLimitedNumber("corcsa", ConUnitPerTypeLimit)
1179	else
1180		return BuildWithLimitedNumber("armcsa", ConUnitPerTypeLimit)
1181	end
1182end
1183
1184local function ConShip()
1185	if ai.mySide == CORESideName then
1186		return BuildWithLimitedNumber("corcs", ConUnitPerTypeLimit)
1187	else
1188		return BuildWithLimitedNumber("armcs", ConUnitPerTypeLimit)
1189	end
1190end
1191
1192local function ConAdvSub()
1193	if ai.mySide == CORESideName then
1194		return BuildWithLimitedNumber("coracsub", ConUnitAdvPerTypeLimit)
1195	else
1196		return BuildWithLimitedNumber("armacsub", ConUnitAdvPerTypeLimit)
1197	end
1198end
1199
1200local function ConHover()
1201	if ai.mySide == CORESideName then
1202		return BuildWithLimitedNumber("corch", ConUnitPerTypeLimit)
1203	else
1204		return BuildWithLimitedNumber("armch", ConUnitPerTypeLimit)
1205	end
1206end
1207
1208local function GroundDefenseIfNeeded(unitName, builder)
1209	if not ai.needGroundDefense then
1210		return DummyUnitName
1211	else
1212		return unitName
1213	end
1214end
1215
1216function BuildShield()
1217	if IsShieldNeeded() then
1218		local unitName = ""
1219		if ai.mySide == CORESideName then
1220			unitName = "corgate"
1221		else
1222			unitName = "armgate"
1223		end
1224		return unitName
1225	end
1226	return DummyUnitName
1227end
1228
1229function BuildAntinuke()
1230	if IsAntinukeNeeded() then
1231		local unitName = ""
1232		if ai.mySide == CORESideName then
1233			unitName = "corfmd"
1234		else
1235			unitName = "armamd"
1236		end
1237		return unitName
1238	end
1239	return DummyUnitName
1240end
1241
1242function BuildNuke()
1243	local unitName = ""
1244	if ai.mySide == CORESideName then
1245		unitName = "corsilo"
1246	else
1247		unitName = "armsilo"
1248	end
1249	return BuildWithLimitedNumber(unitName, nukeLimit)
1250end
1251
1252function BuildNukeIfNeeded()
1253	if IsNukeNeeded() then
1254		return BuildNuke()
1255	end
1256end
1257
1258function BuildTacticalNuke()
1259	local unitName = ""
1260	if ai.mySide == CORESideName then
1261		unitName = "cortron"
1262	else
1263		unitName = "armemp"
1264	end
1265	return BuildWithLimitedNumber(unitName, tacticalNukeLimit)
1266end
1267
1268local function BuildLvl1Plasma()
1269	local unitName = ""
1270	if ai.mySide == CORESideName then
1271		unitName = "corpun"
1272	else
1273		unitName = "armguard"
1274	end
1275	return unitName
1276end
1277
1278local function BuildLvl2Plasma()
1279	local unitName = ""
1280	if ai.mySide == CORESideName then
1281		unitName = "cortoast"
1282	else
1283		unitName = "armamb"
1284	end
1285	return unitName
1286end
1287
1288local function BuildHeavyPlasma()
1289	local unitName = ""
1290	if ai.mySide == CORESideName then
1291		unitName = "corint"
1292	else
1293		unitName = "armbrtha"
1294	end
1295	return BuildWithLimitedNumber(unitName, heavyPlasmaLimit)
1296end
1297
1298local function BuildLLT(self)
1299	if self.unit == nil then
1300		return DummyUnitName
1301	end
1302	local unitName = ""
1303	if ai.mySide == CORESideName then
1304		unitName = "corllt"
1305	else
1306		unitName = "armllt"
1307	end
1308	local unit = self.unit:Internal()
1309	return GroundDefenseIfNeeded(unitName, unit)
1310end
1311
1312local function BuildSpecialLT(self)
1313	if self.unit == nil then
1314		return DummyUnitName
1315	end
1316	local unitName = ""
1317	if IsAANeeded() then
1318		-- pop-up turrets are protected against bombs
1319		if ai.mySide == CORESideName then
1320			unitName = "cormaw"
1321		else
1322			unitName = "armclaw"
1323		end
1324	else
1325		if ai.mySide == CORESideName then
1326			unitName = "hllt"
1327		else
1328			unitName = "tawf001"
1329		end
1330	end
1331	local unit = self.unit:Internal()
1332	return GroundDefenseIfNeeded(unitName, unit)
1333end
1334
1335local function BuildSpecialLTOnly(self)
1336	if self.unit == nil then
1337		return DummyUnitName
1338	end
1339	local unitName = ""
1340	if ai.mySide == CORESideName then
1341		unitName = "hllt"
1342	else
1343		unitName = "tawf001"
1344	end
1345	local unit = self.unit:Internal()
1346	return GroundDefenseIfNeeded(unitName, unit)
1347end
1348
1349local function BuildFloatHLT(self)
1350	if self.unit == nil then
1351		return DummyUnitName
1352	end
1353	local unitName = ""
1354	if ai.mySide == CORESideName then
1355		unitName = "corfhlt"
1356	else
1357		unitName = "armfhlt"
1358	end
1359	local unit = self.unit:Internal()
1360	return GroundDefenseIfNeeded(unitName, unit)
1361end
1362
1363local function BuildHLT(self)
1364	if self.unit == nil then
1365		return DummyUnitName
1366	end
1367	local unitName = ""
1368	if ai.mySide == CORESideName then
1369		unitName = "corhlt"
1370	else
1371		unitName = "armhlt"
1372	end
1373	local unit = self.unit:Internal()
1374	return GroundDefenseIfNeeded(unitName, unit)
1375end
1376
1377local function BuildLvl2PopUp(self)
1378	if self.unit == nil then
1379		return DummyUnitName
1380	end
1381	local unitName = ""
1382	if ai.mySide == CORESideName then
1383		unitName = "corvipe"
1384	else
1385		unitName = "armpb"
1386	end
1387	local unit = self.unit:Internal()
1388	return GroundDefenseIfNeeded(unitName, unit)
1389end
1390
1391local function BuildTachyon(self)
1392	if self.unit == nil then
1393		return DummyUnitName
1394	end
1395	local unitName = ""
1396	if ai.mySide == CORESideName then
1397		unitName = "cordoom"
1398	else
1399		unitName = "armanni"
1400	end
1401	local unit = self.unit:Internal()
1402	return GroundDefenseIfNeeded(unitName, unit)
1403end
1404
1405local function BuildDepthCharge(self)
1406	if self.unit == nil then
1407		return DummyUnitName
1408	end
1409	local unitName = ""
1410	if ai.mySide == CORESideName then
1411		unitName = "cordl"
1412	else
1413		unitName = "armdl"
1414	end
1415	return BuildTorpedoIfNeeded(unitName)
1416end
1417
1418
1419local function BuildLightTorpedo(self)
1420	if self.unit == nil then
1421		return DummyUnitName
1422	end
1423	local unitName = ""
1424	if ai.mySide == CORESideName then
1425		unitName = "cortl"
1426	else
1427		unitName = "armtl"
1428	end
1429	return BuildTorpedoIfNeeded(unitName)
1430end
1431
1432local function BuildPopTorpedo(self)
1433	if self.unit == nil then
1434		return DummyUnitName
1435	end
1436	local unitName = ""
1437	if ai.mySide == CORESideName then
1438		unitName = "corptl"
1439	else
1440		unitName = "armptl"
1441	end
1442	return BuildTorpedoIfNeeded(unitName)
1443end
1444
1445local function BuildHeavyTorpedo(self)
1446	if self.unit == nil then
1447		return DummyUnitName
1448	end
1449	local unitName = ""
1450	if ai.mySide == CORESideName then
1451		unitName = "coratl"
1452	else
1453		unitName = "armatl"
1454	end
1455	return BuildTorpedoIfNeeded(unitName)
1456end
1457
1458
1459-- build AA in area only if there's not enough of it there already
1460local function BuildLightAA(self)
1461	if self.unit == nil then
1462		return DummyUnitName
1463	end
1464	local unitName = ""
1465	if ai.mySide == CORESideName then
1466		unitName = BuildAAIfNeeded("corrl")
1467	else
1468		unitName = BuildAAIfNeeded("armrl")
1469	end
1470	return unitName
1471end
1472
1473local function BuildFloatLightAA(self)
1474	if self.unit == nil then
1475		return DummyUnitName
1476	end
1477	local unitName = ""
1478	if ai.mySide == CORESideName then
1479		unitName = BuildAAIfNeeded("corfrt")
1480	else
1481		unitName = BuildAAIfNeeded("armfrt")
1482	end
1483	return unitName
1484end
1485
1486local function BuildMediumAA(self)
1487	if self.unit == nil then
1488		return DummyUnitName
1489	end
1490	local unitName = ""
1491	if ai.mySide == CORESideName then
1492		unitName = BuildAAIfNeeded("madsam")
1493	else
1494		unitName = BuildAAIfNeeded("packo")
1495	end
1496	return unitName
1497end
1498
1499local function BuildHeavyishAA(self)
1500	if self.unit == nil then
1501		return DummyUnitName
1502	end
1503	local unitName = ""
1504	if ai.mySide == CORESideName then
1505		unitName = BuildAAIfNeeded("corerad")
1506	else
1507		unitName = BuildAAIfNeeded("armcir")
1508	end
1509	return unitName
1510end
1511
1512local function BuildHeavyAA(self)
1513	if self.unit == nil then
1514		return DummyUnitName
1515	end
1516	local unitName = ""
1517	if ai.mySide == CORESideName then
1518		unitName = BuildAAIfNeeded("corflak")
1519	else
1520		unitName = BuildAAIfNeeded("armflak")
1521	end
1522	return unitName
1523end
1524
1525local function BuildFloatHeavyAA(self)
1526	if self.unit == nil then
1527		return DummyUnitName
1528	end
1529	local unitName = ""
1530	if ai.mySide == CORESideName then
1531		unitName = BuildAAIfNeeded("corenaa")
1532	else
1533		unitName = BuildAAIfNeeded("armfflak")
1534	end
1535	return unitName
1536end
1537
1538local function BuildExtraHeavyAA(self)
1539	if self.unit == nil then
1540		return DummyUnitName
1541	end
1542	local unitName = ""
1543	if ai.mySide == CORESideName then
1544		unitName = BuildAAIfNeeded("screamer")
1545	else
1546		unitName = BuildAAIfNeeded("mercury")
1547	end
1548	return unitName
1549end
1550
1551local function BuildSonar()
1552	local unitName = ""
1553	if ai.mySide == CORESideName then
1554		unitName = "corsonar"
1555	else
1556		unitName = "armsonar"
1557	end
1558	return unitName
1559end
1560
1561local function BuildAdvancedSonar()
1562	local unitName = ""
1563	if ai.mySide == CORESideName then
1564		unitName = "corason"
1565	else
1566		unitName = "armason"
1567	end
1568	return unitName
1569end
1570
1571
1572local function BuildRadar()
1573	local unitName = ""
1574	if ai.mySide == CORESideName then
1575		unitName = "corrad"
1576	else
1577		unitName = "armrad"
1578	end
1579	return unitName
1580end
1581
1582local function BuildFloatRadar()
1583	local unitName = ""
1584	if ai.mySide == CORESideName then
1585		unitName = "corfrad"
1586	else
1587		unitName = "armfrad"
1588	end
1589	return unitName
1590end
1591
1592local function BuildAdvancedRadar()
1593	local unitName = ""
1594	if ai.mySide == CORESideName then
1595		unitName = "corarad"
1596	else
1597		unitName = "armarad"
1598	end
1599	return unitName
1600end
1601
1602local function BuildLvl1Jammer()
1603	if not IsJammerNeeded() then return DummyUnitName end
1604	if ai.mySide == CORESideName then
1605		return "corjamt"
1606	else
1607		return "armjamt"
1608	end
1609end
1610
1611local function BuildLvl2Jammer()
1612	if not IsJammerNeeded() then return DummyUnitName end
1613	if ai.mySide == CORESideName then
1614		return "corshroud"
1615	else
1616		return "armveil"
1617	end
1618end
1619
1620local function NanoTurret()
1621	local unitName = ""
1622	if ai.mySide == CORESideName then
1623		unitName = "cornanotc"
1624	else
1625		unitName = "armnanotc"
1626	end
1627	return BuildWithLimitedNumber(unitName, ai.factories * 12)
1628end
1629
1630local function AirRepairPadIfNeeded()
1631	local tmpUnitName = DummyUnitName
1632
1633	-- only make air pads if the team has at least 1 air fac
1634	if CountOwnUnits("corap") > 0 or CountOwnUnits("armap") > 0 or CountOwnUnits("coraap") > 0 or CountOwnUnits("armaap") > 0 then
1635		if ai.mySide == CORESideName then
1636			tmpUnitName = "corasp"
1637		else
1638			tmpUnitName = "armasp"
1639		end
1640	end
1641
1642	return BuildWithLimitedNumber(tmpUnitName, ConUnitPerTypeLimit)
1643end
1644
1645local function corDebug(self)
1646	game:SendToConsole("d")
1647	return "corwin"
1648end
1649
1650function BuildBomberIfNeeded(unitName)
1651	if not IsLandAttackNeeded() then return DummyUnitName end
1652	if unitName == DummyUnitName or unitName == nil then return DummyUnitName end
1653	if ai.bomberhandler:GetCounter() == maxBomberCounter then
1654		return DummyUnitName
1655	else
1656		return unitName
1657	end
1658end
1659
1660function BuildTorpedoBomberIfNeeded(unitName)
1661	if not IsWaterAttackNeeded() then return DummyUnitName end
1662	if unitName == DummyUnitName or unitName == nil then return DummyUnitName end
1663	if ai.bomberhandler:GetCounter() == maxBomberCounter then
1664		return DummyUnitName
1665	else
1666		return unitName
1667	end
1668end
1669
1670function Lvl1Bomber()
1671	local unitName
1672	if ai.mySide == CORESideName then
1673		unitName = "corshad"
1674	else
1675		unitName = "armthund"
1676	end
1677	return BuildBomberIfNeeded(unitName)
1678end
1679
1680function Lvl1Fighter()
1681	local unitName
1682	if ai.mySide == CORESideName then
1683		unitName = "corveng"
1684	else
1685		unitName = "armfig"
1686	end
1687	return BuildAAIfNeeded(unitName)
1688end
1689
1690function Lvl2Bomber()
1691	local unitName
1692	if ai.mySide == CORESideName then
1693		unitName = "corhurc"
1694	else
1695		unitName = "armpnix"
1696	end
1697	return BuildBomberIfNeeded(unitName)
1698end
1699
1700function Lvl2TorpedoBomber()
1701	local unitName
1702	if ai.mySide == CORESideName then
1703		unitName = "cortitan"
1704	else
1705		unitName = "armlance"
1706	end
1707	return BuildTorpedoBomberIfNeeded(unitName)
1708end
1709
1710function Lvl2Fighter()
1711	local unitName
1712	if ai.mySide == CORESideName then
1713		unitName = "corvamp"
1714	else
1715		unitName = "armhawk"
1716	end
1717	return BuildAAIfNeeded(unitName)
1718end
1719
1720function SeaBomber()
1721	local unitName
1722	if ai.mySide == CORESideName then
1723		unitName = "corsb"
1724	else
1725		unitName = "armsb"
1726	end
1727	return BuildBomberIfNeeded(unitName)
1728end
1729
1730function SeaTorpedoBomber()
1731	local unitName
1732	if ai.mySide == CORESideName then
1733		unitName = "corseap"
1734	else
1735		unitName = "armseap"
1736	end
1737	return BuildTorpedoBomberIfNeeded(unitName)
1738end
1739
1740function SeaFighter()
1741	local unitName
1742	if ai.mySide == CORESideName then
1743		unitName = "corsfig"
1744	else
1745		unitName = "armsfig"
1746	end
1747	return BuildAAIfNeeded(unitName)
1748end
1749
1750function SeaAirRaider(self)
1751	local unitName = ""
1752	if ai.mySide == CORESideName then
1753		unitName = "corcut"
1754	else
1755		unitName = "armsaber"
1756	end
1757	return BuildRaiderIfNeeded(unitName)
1758end
1759
1760local function CheckMySideIfNeeded()
1761	if ai.mySide == nil then
1762		EchoDebug("commander: checkmyside")
1763		return CheckMySide
1764	else
1765		return DummyUnitName
1766	end
1767end
1768
1769local function BuildAppropriateFactory()
1770	return FactoryUnitName
1771end
1772
1773local function FactoryOrNano(self)
1774	CheckForMapControl()
1775	if ai.factories == 0 then return BuildAppropriateFactory() end
1776	EchoDebug("factories: " .. ai.factories .. "  combat units: " .. ai.combatCount)
1777	local unitName = DummyUnitName
1778	local attackCounter = ai.attackhandler:GetCounter()
1779	local couldAttack = ai.couldAttack >= 2 or ai.couldBomb >= 2
1780	if (ai.combatCount > attackCounter * 0.5 and couldAttack) or ai.needAdvanced then
1781		unitName = BuildAppropriateFactory()
1782	end
1783	if unitName == DummyUnitName and ai.combatCount > attackCounter * 0.2 then
1784		unitName = NanoTurret()
1785	end
1786	return unitName
1787end
1788
1789local function LandOrWater(self, landName, waterName)
1790	local builder = self.unit:Internal()
1791	local bpos = builder:GetPosition()
1792	local waterNet = ai.maphandler:MobilityNetworkSizeHere("shp", bpos)
1793	if waterNet ~= nil then
1794		return waterName
1795	else
1796		return landName
1797	end
1798end
1799
1800local function RezBot1()
1801	local unitName
1802	if ai.mySide == CORESideName then
1803		unitName = "cornecro"
1804	else
1805		unitName = "armrectr"
1806	end
1807	return BuildWithLimitedNumber(unitName, 1)
1808end
1809
1810local function ScoutBot()
1811	local unitName
1812	if ai.mySide == CORESideName then
1813		return DummyUnitName
1814	else
1815		unitName = "armflea"
1816	end
1817	return BuildWithLimitedNumber(unitName, 1)
1818end
1819
1820local function ScoutVeh()
1821	local unitName
1822	if ai.mySide == CORESideName then
1823		unitName = "corfav"
1824	else
1825		unitName = "armfav"
1826	end
1827	return BuildWithLimitedNumber(unitName, 1)
1828end
1829
1830local function ScoutAir()
1831	local unitName
1832	if ai.mySide == CORESideName then
1833		unitName = "corfink"
1834	else
1835		unitName = "armpeep"
1836	end
1837	return BuildWithLimitedNumber(unitName, 1)
1838end
1839
1840local function ScoutShip()
1841	local unitName
1842	if ai.mySide == CORESideName then
1843		unitName = "corpt"
1844	else
1845		unitName = "armpt"
1846	end
1847	local scout = BuildWithLimitedNumber(unitName, 1)
1848	if scout == DummyUnitName then
1849		return BuildAAIfNeeded(unitName)
1850	else
1851		return unitName
1852	end
1853end
1854
1855local function ScoutAdvAir()
1856	local unitName
1857	if ai.mySide == CORESideName then
1858		unitName = "corawac"
1859	else
1860		unitName = "armawac"
1861	end
1862	return BuildWithLimitedNumber(unitName, 1)
1863end
1864
1865local function ScoutSeaAir()
1866	local unitName
1867	if ai.mySide == CORESideName then
1868		unitName = "corhunt"
1869	else
1870		unitName = "armsehak"
1871	end
1872	return BuildWithLimitedNumber(unitName, 1)
1873end
1874
1875local function Lvl2BotAssist()
1876	if ai.mySide == CORESideName then
1877		return "corfast"
1878	else
1879		return "armfark"
1880	end
1881end
1882
1883local function Lvl2VehAssist()
1884	if ai.mySide == CORESideName then
1885		return DummyUnitName
1886	else
1887		return "consul"
1888	end
1889end
1890
1891local function Lvl2ShipAssist()
1892	if ai.mySide == CORESideName then
1893		return "cormls"
1894	else
1895		return "armmls"
1896	end
1897end
1898
1899-- end of functions
1900
1901
1902-- mobile construction units:
1903
1904local anyCommander = {
1905	CheckMySideIfNeeded,
1906	BuildMex,
1907	BuildAppropriateFactory,
1908	WindSolar,
1909	BuildLLT,
1910	BuildRadar,
1911	BuildLightAA,
1912	DoSomethingForTheEconomy,
1913	TidalIfTidal,
1914	BuildPopTorpedo,
1915	BuildSonar,
1916}
1917
1918local anyConUnit = {
1919	BuildAppropriateFactory,
1920	NanoTurret,
1921	BuildLLT,
1922	BuildSpecialLT,
1923	BuildMediumAA,
1924	BuildRadar,
1925	BuildLvl1Jammer,
1926	WindSolar,
1927	BuildGeo,
1928	SolarAdv,
1929	BuildHLT,
1930	BuildLvl1Plasma,
1931	DoSomethingForTheEconomy,
1932	BuildHeavyishAA,
1933	BuildMex,
1934}
1935
1936local anyConAmphibious = {
1937	BuildGeo,
1938	BuildSpecialLT,
1939	BuildMediumAA,
1940	BuildRadar,
1941	BuildLvl1Jammer,
1942	WindSolar,
1943	SolarAdv,
1944	FactoryOrNano,
1945	BuildHLT,
1946	BuildLvl1Plasma,
1947	DoSomethingForTheEconomy,
1948	BuildHeavyishAA,
1949	BuildMex,
1950	BuildPopTorpedo,
1951	BuildFloatLightAA,
1952	BuildSonar,
1953	BuildFloatRadar,
1954	TidalIfTidal,
1955	BuildFloatHLT,
1956	DoSomethingForTheEconomy,
1957}
1958
1959local anyConShip = {
1960	BuildUWMex,
1961	BuildFloatLightAA,
1962	BuildSonar,
1963	BuildLightTorpedo,
1964	BuildFloatRadar,
1965	TidalIfTidal,
1966	BuildAppropriateFactory,
1967	BuildFloatHLT,
1968	DoSomethingForTheEconomy,
1969}
1970
1971local anyAdvConUnit = {
1972	BuildAppropriateFactory,
1973	BuildFusion,
1974	BuildNukeIfNeeded,
1975	BuildAdvancedRadar,
1976	BuildHeavyPlasma,
1977	BuildAntinuke,
1978	BuildLvl2PopUp,
1979	BuildHeavyAA,
1980	BuildLvl2Plasma,
1981	BuildTachyon,
1982	-- BuildTacticalNuke,
1983	BuildExtraHeavyAA,
1984	BuildLvl2Jammer,
1985	BuildMohoGeo,
1986	BuildMohoMex,
1987	-- DoSomethingAdvancedForTheEconomy,
1988}
1989
1990local anyConSeaplane = {
1991	BuildUWMohoMex,
1992	BuildFloatHeavyAA,
1993	BuildUWFusion,
1994	BuildAdvancedSonar,
1995	BuildHeavyTorpedo,
1996	BuildAppropriateFactory,
1997	-- DoSomethingAdvancedForTheEconomy,
1998}
1999
2000local anyAdvConSub = {
2001	BuildUWMohoMex,
2002	BuildFloatHeavyAA,
2003	BuildUWFusion,
2004	BuildAdvancedSonar,
2005	BuildHeavyTorpedo,
2006	-- DoSomethingAdvancedForTheEconomy,
2007}
2008
2009local anyNavalEngineer = {
2010	BuildFloatHLT,
2011	BuildFloatLightAA,
2012	BuildAppropriateFactory,
2013	Lvl1ShipBattle,
2014	BuildFloatRadar,
2015	TidalIfTidal,
2016	BuildUWMex,
2017	BuildSonar,
2018	Lvl1ShipRaider,
2019	Conship,
2020	ScoutShip,
2021	BuildLightTorpedo,
2022}
2023
2024local anyCombatEngineer = {
2025	BuildAppropriateFactory,
2026	NanoTurret,
2027	Solar,
2028	BuildMediumAA,
2029	BuildAdvancedRadar,
2030	BuildLvl2Jammer,
2031	BuildLvl2PopUp,
2032	BuildHeavyAA,
2033	BuildSpecialLTOnly,
2034	BuildLvl2Plasma,
2035	ConCoreBotArmVehicle,
2036	Lvl2BotCorRaiderArmBattle,
2037	Lvl1AABot,
2038	ConShip,
2039	Lvl1ShipDestroyerOnly,
2040	BuildMex,
2041}
2042
2043
2044-- factories:
2045
2046local anyLvl1AirPlant = {
2047	ScoutAir,
2048	Lvl1Bomber,
2049	Lvl1AirRaider,
2050	ConAir,
2051	Lvl1Fighter,
2052}
2053
2054local anyLvl1VehPlant = {
2055	ScoutVeh,
2056	ConVehicle,
2057	Lvl1VehRaider,
2058	Lvl1VehBattle,
2059	Lvl1AAVeh,
2060	Lvl1VehArty,
2061	Lvl1VehBreakthrough,
2062}
2063
2064local anyLvl1BotLab = {
2065	ScoutBot,
2066	ConBot,
2067	Lvl1BotRaider,
2068	Lvl1BotBattle,
2069	Lvl1AABot,
2070	Lvl1BotBreakthrough,
2071	RezBot1,
2072}
2073
2074local anyLvl1ShipYard = {
2075	ScoutShip,
2076	ConShip,
2077	Lvl1ShipBattle,
2078	Lvl1ShipRaider,
2079}
2080
2081local anyHoverPlatform = {
2082	HoverRaider,
2083	ConHover,
2084	HoverBattle,
2085	HoverBreakthrough,
2086	HoverMerl,
2087	AAHover,
2088}
2089
2090local anyAmphibiousComplex = {
2091	AmphibiousRaider,
2092	ConVehicleAmphibious,
2093	AmphibiousBattle,
2094	Lvl1ShipRaider,
2095	Lvl1AABot,
2096	Lvl2AABot,
2097}
2098
2099local anyLvl2VehPlant = {
2100	Lvl2VehRaider,
2101	ConAdvVehicle,
2102	Lvl2VehBattle,
2103	Lvl2VehBreakthrough,
2104	Lvl2VehArty,
2105	Lvl2VehMerl,
2106	Lvl2AAVeh,
2107	Lvl2VehAssist,
2108}
2109
2110local anyLvl2BotLab = {
2111	Lvl2BotRaider,
2112	ConAdvBot,
2113	Lvl2BotBattle,
2114	Lvl2BotBreakthrough,
2115	Lvl2BotArty,
2116	Lvl2BotMerl,
2117	Lvl2AABot,
2118	Lvl2BotAssist,
2119}
2120
2121local anyLvl2AirPlant = {
2122	Lvl2Bomber,
2123	Lvl2TorpedoBomber,
2124	ConAdvAir,
2125	ScoutAdvAir,
2126	Lvl2Fighter,
2127	Lvl2AirRaider,
2128	MegaAircraft,
2129}
2130
2131local anySeaplanePlatform = {
2132	SeaBomber,
2133	SeaTorpedoBomber,
2134	ConSeaAir,
2135	ScoutSeaAir,
2136	SeaFighter,
2137	SeaAirRaider,
2138}
2139
2140local anyLvl2ShipYard = {
2141	Lvl2ShipRaider,
2142	ConAdvSub,
2143	Lvl2ShipBattle,
2144	Lvl2AAShip,
2145	Lvl2ShipBreakthrough,
2146	Lvl2ShipMerl,
2147	Lvl2ShipAssist,
2148	MegaShip,
2149}
2150
2151local anyExperimental = {
2152	Lvl3Raider,
2153	Lvl3Battle,
2154	Lvl3Merl,
2155	Lvl3Arty,
2156	Lvl3Breakthrough,
2157}
2158
2159local anyOutmodedLvl1BotLab = {
2160	ConBot,
2161	RezBot1,
2162	ScoutBot,
2163	Lvl1AABot,
2164}
2165
2166local anyOutmodedLvl1VehPlant = {
2167	Lvl1VehRaiderOutmoded,
2168	ConVehicle,
2169	ScoutVeh,
2170	Lvl1AAVeh,
2171}
2172
2173local anyOutmodedLvl1AirPlant = {
2174	ConAir,
2175	ScoutAir,
2176	Lvl1Fighter,
2177}
2178
2179local anyOutmodedLvl1ShipYard = {
2180	ConShip,
2181	ScoutShip,
2182}
2183
2184local anyOutmodedLvl2BotLab = {
2185	-- Lvl2BotRaider,
2186	ConAdvBot,
2187	Lvl2AABot,
2188	Lvl2BotAssist,
2189}
2190
2191local anyOutmodedLvl2VehPlant = {
2192	-- Lvl2VehRaider,
2193	Lvl2VehAssist,
2194	ConAdvVehicle,
2195	Lvl2AAVeh,
2196}
2197
2198local anyLvl1VehPlantForWater = {
2199	ScoutVeh,
2200	AmphibiousRaider,
2201	ConVehicleAmphibious,
2202	Lvl1AAVeh,
2203}
2204
2205-- use these if it's a watery map
2206wateryTaskqueues = {
2207	armvp = anyLvl1VehPlantForWater,
2208	corvp = anyLvl1VehPlantForWater,
2209}
2210
2211-- fall back to these when a level 2 factory exists
2212outmodedTaskqueues = {
2213	corlab = anyOutmodedLvl1BotLab,
2214	armlab = anyOutmodedLvl1BotLab,
2215	corvp = anyOutmodedLvl1VehPlant,
2216	armvp = anyOutmodedLvl1VehPlant,
2217	corap = anyOutmodedLvl1AirPlant,
2218	armap = anyOutmodedLvl1AirPlant,
2219	corsy = anyOutmodedLvl1ShipYard,
2220	armsy = anyOutmodedLvl1ShipYard,
2221	coralab = anyOutmodedLvl2BotLab,
2222	armalab = anyOutmodedLvl2BotLab,
2223	coravp = anyOutmodedLvl2VehPlant,
2224	armavp = anyOutmodedLvl2VehPlant,
2225}
2226
2227-- finally, the taskqueue definitions
2228taskqueues = {
2229	corcom = anyCommander,
2230	armcom = anyCommander,
2231	corcv = anyConUnit,
2232	armcv = anyConUnit,
2233	corck = anyConUnit,
2234	armck = anyConUnit,
2235	cormuskrat = anyConAmphibious,
2236	armbeaver = anyConAmphibious,
2237	corch = anyConAmphibious,
2238	armch = anyConAmphibious,
2239	corca = anyConUnit,
2240	armca = anyConUnit,
2241	corack = anyAdvConUnit,
2242	armack = anyAdvConUnit,
2243	coracv = anyAdvConUnit,
2244	armacv = anyAdvConUnit,
2245	coraca = anyAdvConUnit,
2246	armaca = anyAdvConUnit,
2247	corcsa = anyConSeaplane,
2248	armcsa = anyConSeaplane,
2249	corcs = anyConShip,
2250	armcs = anyConShip,
2251	coracsub = anyAdvConSub,
2252	armacsub = anyAdvConSub,
2253	cormls = anyNavalEngineer,
2254	armmls = anyNavalEngineer,
2255	consul = anyCombatEngineer,
2256	corfast = anyCombatEngineer,
2257	corap = anyLvl1AirPlant,
2258	armap = anyLvl1AirPlant,
2259	corlab = anyLvl1BotLab,
2260	armlab = anyLvl1BotLab,
2261	corvp = anyLvl1VehPlant,
2262	armvp = anyLvl1VehPlant,
2263	coralab = anyLvl2BotLab,
2264	coravp = anyLvl2VehPlant,
2265	corhp = anyHoverPlatform,
2266	armhp = anyHoverPlatform,
2267	corfhp = anyHoverPlatform,
2268	armfhp = anyHoverPlatform,
2269	csubpen = anyAmphibiousComplex,
2270	asubpen = anyAmphibiousComplex,
2271	armalab = anyLvl2BotLab,
2272	armavp = anyLvl2VehPlant,
2273	coraap = anyLvl2AirPlant,
2274	armaap = anyLvl2AirPlant,
2275	corplat = anySeaplanePlatform,
2276	armplat = anySeaplanePlatform,
2277	corsy = anyLvl1ShipYard,
2278	armsy = anyLvl1ShipYard,
2279	corasy = anyLvl2ShipYard,
2280	armasy = anyLvl2ShipYard,
2281	corgant = anyExperimental,
2282	armshltx = anyExperimental,
2283	armfark = {
2284		WindSolar,
2285		BuildMex,
2286	}
2287}