1 #include "CE323AI.h"
2 
3 #include <string>
4 #include <algorithm>
5 #include <cctype>
6 
7 #include "CAI.h"
8 #include "CRNG.h"
9 #include "CConfigParser.h"
10 #include "GameMap.hpp"
11 #include "CUnitTable.h"
12 #include "CEconomy.h"
13 #include "CWishList.h"
14 #include "CTaskHandler.h"
15 #include "CThreatMap.h"
16 #include "CPathfinder.h"
17 #include "CIntel.h"
18 #include "CMilitary.h"
19 #include "CDefenseMatrix.h"
20 #include "CUnit.h"
21 #include "CGroup.h"
22 #include "CScopedTimer.h"
23 #include "Util.hpp"
24 #include "CCoverageHandler.h"
25 #include "ReusableObjectFactory.hpp"
26 
27 namespace springLegacyAI {
28 	class IGlobalAICallback;
29 }
30 #include "LegacyCpp/IAICallback.h"
31 
32 
CE323AI()33 CE323AI::CE323AI() {
34 	isRunning = false;
35 	attachedAtFrame = -1;
36 }
37 
InitAI(IGlobalAICallback * callback,int team)38 void CE323AI::InitAI(IGlobalAICallback* callback, int team) {
39 	static const char
40 		optionDifficulty[] = "difficulty",
41 		optionLoggingLevel[] = "logging";
42 
43 	CLogger::logLevel loggingLevel = CLogger::VERBOSE;
44 
45 	ai = new AIClasses(callback);
46 
47 	std::map<std::string, std::string> options = ai->cb->GetMyOptionValues();
48 	if (options.find(optionDifficulty) != options.end()) {
49 		ai->difficulty = static_cast<difficultyLevel>(atoi(options[optionDifficulty].c_str()));
50 	}
51 	if (options.find(optionLoggingLevel) != options.end()) {
52 		loggingLevel = static_cast<CLogger::logLevel>(atoi(options[optionLoggingLevel].c_str()));
53 	}
54 
55 	ai->logger = new CLogger(ai, /*CLogger::LOG_STDOUT |*/ CLogger::LOG_FILE, loggingLevel);
56 
57 	LOG_II("CE323AI::InitAI allyIndex = " << ai->allyIndex)
58 
59 	ai->cfgparser = new CConfigParser(ai);
60 	ai->unittable = new CUnitTable(ai);
61 
62 	std::string configfile = ai->cfgparser->getFilename(GET_CFG);
63 	ai->cfgparser->parseConfig(configfile);
64 	if (ai->cfgparser->isUsable()) {
65 		// try loading overload file...
66 		configfile = ai->cfgparser->getFilename(GET_CFG|GET_VER);
67 		if (ai->cfgparser->fileExists(configfile))
68 			ai->cfgparser->parseConfig(configfile);
69 	}
70 	if (!ai->cfgparser->isUsable()) {
71 		ai->cb->SendTextMsg("No usable config file available for this Mod/Game.", 0);
72 		const std::string confFileLine = "A template can be found at: " + configfile;
73 		ai->cb->SendTextMsg(confFileLine.c_str(), 0);
74 		ai->cb->SendTextMsg("Shutting down...", 0);
75 
76 		// we have to cleanup here, as ReleaseAI() will not be called
77 		// in case of an error in InitAI().
78 		delete ai->cfgparser;
79 		delete ai->logger;
80 		delete ai->unittable;
81 		delete ai;
82 		// this will kill this AI instance gracefully
83 		throw 33;
84 	}
85 
86 	ai->gamemap       = new GameMap(ai);
87 	ai->economy       = new CEconomy(ai);
88 	ai->wishlist      = new CWishList(ai);
89 	ai->tasks         = new CTaskHandler(ai);
90 	ai->threatmap     = new CThreatMap(ai);
91 	ai->pathfinder    = new CPathfinder(ai);
92 	ai->intel         = new CIntel(ai);
93 	ai->military      = new CMilitary(ai);
94 	ai->defensematrix = new CDefenseMatrix(ai);
95 	ai->coverage      = new CCoverageHandler(ai);
96 
97 #if !defined(BUILDING_AI_FOR_SPRING_0_81_2)
98 	/* Set the new graph stuff */
99 	ai->cb->DebugDrawerSetGraphPos(-0.4f, -0.4f);
100 	ai->cb->DebugDrawerSetGraphSize(0.8f, 0.6f);
101 #endif
102 }
103 
ReleaseAI()104 void CE323AI::ReleaseAI() {
105 
106 	if (ai->isSole()) {
107 		ReusableObjectFactory<CGroup>::Shutdown();
108 		ReusableObjectFactory<CUnit>::Shutdown();
109 		ReusableObjectFactory<CCoverageCell>::Shutdown();
110 	}
111 
112 	delete ai->coverage;
113 	delete ai->defensematrix;
114 	delete ai->military;
115 	delete ai->intel;
116 	delete ai->pathfinder;
117 	delete ai->threatmap;
118 	delete ai->tasks;
119 	delete ai->wishlist;
120 	delete ai->economy;
121 	delete ai->gamemap;
122 	delete ai->unittable;
123 	delete ai->cfgparser;
124 	delete ai->logger;
125 	delete ai;
126 }
127 
128 /************************
129  * Unit related callins *
130  ************************/
131 
132 /* Called when units are spawned in a factory or when game starts */
UnitCreated(int uid,int bid)133 void CE323AI::UnitCreated(int uid, int bid) {
134 	CUnit* unit = ai->unittable->requestUnit(uid, bid);
135 
136 	LOG_II("CE323AI::UnitCreated " << (*unit))
137 
138 	if ((unit->type->cats&COMMANDER).any() && !ai->economy->isInitialized()) {
139 		ai->economy->init(*unit);
140 	}
141 
142 	// HACK: for metal extractors & geoplants only
143 	ai->economy->addUnitOnCreated(*unit);
144 
145 	ai->coverage->addUnit(unit);
146 
147 	if (bid < 0)
148 		return; // unit was spawned from nowhere (e.g. commander), or given by another player
149 
150 	unitCategory c = unit->type->cats;
151 	if ((c&MOBILE).any()) {
152 		CUnit* builder = ai->unittable->getUnit(bid);
153 		// if builder is a mobile unit (like Consul in BA) then do not
154 		// assign move command...
155 		if ((builder->type->cats&STATIC).any()) {
156 			// NOTE: factories should be already rotated in proper direction
157 			// to prevent units going outside the map
158 			if ((c&AIR).any()) {
159 				if ((c&ANTIAIR).any())
160 					unit->guard(bid, true);
161 				else
162 					unit->moveRandom(450.0f, true);
163 			}
164 			else if ((c&BUILDER).any())
165 				unit->moveForward(200.0f);
166 			else
167 				unit->moveForward(400.0f);
168 		}
169 	}
170 	else {
171 		if ((c&NANOTOWER).any()) {
172 			unit->patrol(unit->getForwardPos(100.0f), true);
173 		}
174 	}
175 
176 	// TODO: check if UnitIdle for factory/builder is called after
177 	// UnitCreated then we do not need "unitsUnderConstruction" map
178 	std::map<int, Wish>::iterator it = ai->unittable->unitsBuilding.find(bid);
179 	if (it != ai->unittable->unitsBuilding.end())
180 		ai->unittable->unitsUnderConstruction[uid] = it->second.goalCats;
181 	else
182 		ai->unittable->unitsUnderConstruction[uid] = 0;
183 }
184 
185 /* Called when units are finished in a factory and able to move */
UnitFinished(int uid)186 void CE323AI::UnitFinished(int uid) {
187 	CUnit* unit = ai->unittable->getUnit(uid);
188 
189 	if(!unit) {
190 		const UnitDef *ud = ai->cb->GetUnitDef(uid);
191 		LOG_EE("CE323AI::UnitFinished unregistered " << (ud ? ud->humanName : std::string("UnknownUnit")) << "(" << uid << ")")
192 		return;
193 	}
194 	else
195 		LOG_II("CE323AI::UnitFinished " << (*unit))
196 
197 	// NOTE: commanders and static units should start actions earlier than
198 	// usual units
199 	if (unit->builtBy == -1 || (unit->type->cats&STATIC).any())
200 		unit->aliveFrames = IDLE_UNIT_TIMEOUT;
201 	else
202 		unit->aliveFrames = 0; // reset time at which unit was building
203 
204 	ai->unittable->idle[uid] = true;
205 
206 	if (unit->builtBy >= 0) {
207 		// mark builder has finished its job
208 		ai->unittable->builders[unit->builtBy] = true;
209 	}
210 
211 	if (unit->isEconomy()) {
212 		ai->economy->addUnitOnFinished(*unit);
213 	}
214 	else if(!ai->military->addUnit(*unit)) {
215 		LOG_WW("CE323AI::UnitFinished unit " << (*unit) << " is NOT under AI control")
216 	}
217 
218 	// NOTE: very important to place this line AFTER registering a unit in
219 	// either economy or military blocks
220 	ai->unittable->unitsUnderConstruction.erase(uid);
221 }
222 
223 /* Called on a destroyed unit */
UnitDestroyed(int uid,int attacker)224 void CE323AI::UnitDestroyed(int uid, int attacker) {
225 	ai->tasks->onUnitDestroyed(uid, attacker);
226 
227 	CUnit *unit = ai->unittable->getUnit(uid);
228 	if (unit) {
229 		LOG_II("CE323AI::UnitDestroyed " << (*unit))
230 		unit->remove();
231 	}
232 }
233 
234 /* Called when unit is idle */
UnitIdle(int uid)235 void CE323AI::UnitIdle(int uid) {
236 	CUnit* unit = ai->unittable->getUnit(uid);
237 
238 	if (unit == NULL) {
239 		const UnitDef *ud = ai->cb->GetUnitDef(uid);
240 		LOG_EE("CE323AI::UnitIdle unregistered " << (ud ? ud->humanName : std::string("UnknownUnit")) << "(" << uid << ")")
241 		return;
242 	}
243 
244 	if (ai->unittable->unitsUnderPlayerControl.find(uid) != ai->unittable->unitsUnderPlayerControl.end()) {
245 		ai->unittable->unitsUnderPlayerControl.erase(uid);
246 		assert(unit->group == NULL);
247 		LOG_II("CE323AI::UnitIdle " << (*unit) << " is under AI control again")
248 		// re-assign unit to appropriate group
249 		UnitFinished(uid);
250 		return;
251 	}
252 
253 	ai->unittable->idle[uid] = true;
254 
255 	if ((unit->type->cats&(BUILDER|FACTORY)).any())
256 		ai->unittable->unitsBuilding.erase(uid);
257 }
258 
259 /* Called when unit is damaged */
UnitDamaged(int damaged,int attacker,float damage,float3 dir)260 void CE323AI::UnitDamaged(int damaged, int attacker, float damage, float3 dir) {
261 	// TODO: introduce quick imrovement for builders to reclaim their attacker
262 	// but next it should return to its current job, so we need to delay
263 	// current task which is impossible while there is no task queue per unit
264 	// group (curently we have a single task per group)
265 
266 	if (ai->cb->UnitBeingBuilt(damaged) || ai->cb->IsUnitParalyzed(damaged) || ai->cb->GetUnitHealth(damaged) < EPS)
267 		return;
268 	if (attacker < 0)
269 		return;
270 	if (ai->cbc->GetUnitHealth(attacker) < EPS)
271 		return;
272 
273 	CUnit* unit = ai->unittable->getUnit(damaged);
274 	if (unit == NULL)
275 		return; // invalid unit
276 	if (unit->group == NULL)
277 		return; // unit is not under AI control
278 
279 	/*
280 	const unsigned int cats = unit->type->cats;
281 	if (cats&MOBILE) {
282 		bool attack = false;
283 		if (cats&(ATTACKER|BUILDER)) {
284 			const UnitDef *eud = ai->cbc->GetUnitDef(attacker);
285 			if (!eud)
286 				return;
287 			const unsigned int ecats = UC(eud->id);
288 			float3 epos = ai->cbc->GetUnitPos(attacker);
289 			float range = cats&BUILDER ? unit->group->buildRange: unit->group->range;
290 			if (unit->group->pos().distance2D(epos) < 1.1f*range) {
291 				// always strikeback if attacker is a scouter or less powerful
292 				if ((ecats&SCOUTER) || ai->threatmap->getThreat(epos, 0.0f) <= unit->group->strength) {
293 					ATask* task = ai->tasks->getTask(*unit->group);
294 					if (!task || (task->t != ATTACK && task->t != FACTORY_BUILD)) {
295 						if (task && task->active)
296 							task->remove();
297 						attack = true;
298 					}
299 				}
300 			}
301 		}
302 
303 		if (attack) {
304 			ai->tasks->addAttackTask(attacker, *unit->group);
305 		} else {
306 			// TODO: run away or continue its task depending on group style
307 		}
308 	}
309 	*/
310 }
311 
312 /* Called on move fail e.g. can't reach the point */
UnitMoveFailed(int uid)313 void CE323AI::UnitMoveFailed(int uid) {
314 	CUnit* unit = ai->unittable->getUnit(uid);
315 	if (unit && (unit->type->cats&(LAND|SEA)).any()) {
316 		// if unit is inside a factory then force moving...
317 		float3 pos = ai->cb->GetUnitPos(unit->key);
318 		std::map<int, CUnit*>::iterator it;
319 		for (it = ai->unittable->factories.begin(); it != ai->unittable->factories.end(); ++it) {
320 			float distance = ai->cb->GetUnitPos(it->first).distance2D(pos);
321 			if (distance < 16.0) {
322 				unit->moveForward(200.0f);
323 				if (!unit->canPerformTasks())
324 					unit->aliveFrames = 0; // prolong idle timeout
325 			}
326 		}
327 	}
328 }
329 
330 
331 /***********************
332  * Enemy related callins *
333  ***********************/
334 
EnemyEnterLOS(int enemy)335 void CE323AI::EnemyEnterLOS(int enemy) {
336 }
337 
EnemyLeaveLOS(int enemy)338 void CE323AI::EnemyLeaveLOS(int enemy) {
339 }
340 
EnemyEnterRadar(int enemy)341 void CE323AI::EnemyEnterRadar(int enemy) {
342 }
343 
EnemyLeaveRadar(int enemy)344 void CE323AI::EnemyLeaveRadar(int enemy) {
345 }
346 
EnemyCreated(int enemy)347 void CE323AI::EnemyCreated(int enemy) {
348 	ai->intel->onEnemyCreated(enemy);
349 }
350 
EnemyDestroyed(int enemy,int attacker)351 void CE323AI::EnemyDestroyed(int enemy, int attacker) {
352 	ai->military->onEnemyDestroyed(enemy, attacker);
353 	ai->tasks->onEnemyDestroyed(enemy, attacker);
354 	ai->intel->onEnemyDestroyed(enemy, attacker);
355 }
356 
EnemyDamaged(int damaged,int attacker,float damage,float3 dir)357 void CE323AI::EnemyDamaged(int damaged, int attacker, float damage, float3 dir) {
358 }
359 
360 
361 /****************
362  * Misc callins *
363  ****************/
364 
GotChatMsg(const char * msg,int player)365 void CE323AI::GotChatMsg(const char* msg, int player) {
366 	static const char
367 		cmdPrefix[] = "!e323ai",
368 		modTM[] = "threatmap",
369 		modMil[] = "military",
370 	//	modEco[] = "economy",
371 		modPF[] = "pathfinder",
372 		modPG[] = "pathgraph",
373 		modDM[] = "defensematrix",
374 		modCL[] = "coverage";
375 
376 	// NOTE: accept AI commands from spectators only
377 	if (ai->cb->GetPlayerTeam(player) >= 0)
378 		return;
379 
380 	std::string line(msg);
381 	std::transform(line.begin(), line.end(), line.begin(), ::tolower);
382 
383 	if (line.find(cmdPrefix) == 0) {
384 		bool isDebugOn = false;
385 		size_t pos = line.find_first_not_of(' ', sizeof(cmdPrefix) - 1);
386 		if (pos == std::string::npos) {
387 			if (ai->isMaster()) {
388 				ai->cb->SendTextMsg("Usage: !e323ai <module>", 0);
389 				ai->cb->SendTextMsg("where", 0);
390 				ai->cb->SendTextMsg("\t<module> ::= pathfinder|pathgraph|military|threatmap|defensematrix|coverage", 0);
391 				ai->cb->SendTextMsg("\t<module> ::= pf|pg|mil|tm|dm|cl", 0);
392 			}
393 			return;
394 		}
395 
396 		std::string cmd = line.substr(pos);
397 		if (cmd == modTM || cmd == "tm") {
398 			isDebugOn = ai->threatmap->switchDebugMode();
399 			cmd.assign(modTM);
400 		}
401 		else if (cmd == modMil || cmd == "mil") {
402 			isDebugOn = ai->military->switchDebugMode();
403 			cmd.assign(modMil);
404 		}
405 		else if (cmd == modPF || cmd == "pf") {
406 			isDebugOn = ai->pathfinder->switchDebugMode(false);
407 			cmd.assign(modPF);
408 		}
409 		else if (cmd == modPG || cmd == "pg") {
410 			isDebugOn = ai->pathfinder->switchDebugMode(true);
411 			cmd.assign(modPG);
412 		}
413 		else if (cmd == modDM || cmd == "dm") {
414 			isDebugOn = ai->defensematrix->switchDebugMode();
415 			cmd.assign(modDM);
416 		}
417 		else if (cmd == modCL || cmd == "cl") {
418 			isDebugOn = ai->coverage->toggleVisualization();
419 			cmd.assign(modCL);
420 		}
421 		else {
422 			if (cmd == "unit") {
423 				// dump selected unit info...
424 				line.clear();
425 
426 				if (ai->cb->GetSelectedUnits(&ai->unitIDs[0], 1) > 0) {
427 					std::stringstream buffer;
428 					CUnit* unit = ai->unittable->getUnit(ai->unitIDs[0]);
429 					if (unit) {
430 						buffer << ".id = " << unit->key << "\n";
431 						buffer << ".humanName = " << unit->def->humanName << "\n";
432 						buffer << ".isMicroing = " << unit->isMicroing() << "\n";
433 						buffer << ".microingFrames = " << unit->microingFrames << "\n";
434 						buffer << ".aliveFrames = " << unit->aliveFrames << "\n";
435 						buffer << ".waiting = " << unit->waiting << "\n";
436 						buffer << ".idle = " << ai->unittable->idle[unit->key] << "\n";
437 
438 						ai->cb->SendTextMsg(buffer.str().c_str(), 0);
439 					}
440 				}
441 			}
442 			else if (cmd == "tmv") {
443 				// dump threat value at mouse cursor...
444 				float value = 0.0f;
445 				float3 pos = ai->cb->GetMousePos();
446 				CUnit* unit = NULL;
447 				if (ai->cb->GetSelectedUnits(&ai->unitIDs[0], 1) > 0) {
448 					unit = ai->unittable->getUnit(ai->unitIDs[0]);
449 					if (unit && unit->group) {
450 						value = ai->threatmap->getThreat(pos, 0.0f, unit->group);
451 					}
452 				}
453 				else {
454 					value = ai->threatmap->getThreat(pos, 0.0f);
455 				}
456 
457 				std::stringstream buffer;
458 				buffer << "Threat value";
459 				if (unit)
460 					buffer << " for " << unit->def->humanName;
461 				buffer << " at position (" << pos.x << "," << pos.z << ") is " << value;
462 
463 				ai->cb->SendTextMsg(buffer.str().c_str(), 0);
464 			}
465 			else {
466 				line.assign("Module \"" + cmd + "\" is unknown or unsupported for visual debugging");
467 
468 			}
469 
470 			if (!line.empty())
471 				ai->cb->SendTextMsg(line.c_str(), 0);
472 
473 			return;
474 		}
475 
476 		line.assign("Debug mode is switched ");
477 		if (isDebugOn)
478 			line += "ON";
479 		else
480 			line += "OFF";
481 		line += " for \"" + cmd + "\" module";
482 
483 		ai->cb->SendTextMsg(line.c_str(), 0);
484 	}
485 }
486 
HandleEvent(int msg,const void * data)487 int CE323AI::HandleEvent(int msg, const void* data) {
488 	const ChangeTeamEvent* cte = (const ChangeTeamEvent*) data;
489 
490 	switch(msg) {
491 		case AI_EVENT_UNITGIVEN:
492 			/* Unit gained */
493 			if ((cte->newteam) == ai->team) {
494 				UnitCreated(cte->unit, -1);
495 				UnitFinished(cte->unit);
496 
497 				// NOTE: getting "unit" for logging only
498 				CUnit *unit = ai->unittable->getUnit(cte->unit);
499 
500 				LOG_II("CE323AI::UnitGiven " << (*unit))
501 			}
502 			break;
503 
504 		case AI_EVENT_UNITCAPTURED:
505 			/* Unit lost */
506 			if ((cte->oldteam) == ai->team) {
507 				// NOTE: getting "unit" for logging only
508 				CUnit *unit = ai->unittable->getUnit(cte->unit);
509 
510 				LOG_II("CE323AI::UnitCaptured " << (*unit))
511 
512 				UnitDestroyed(cte->unit, 0);
513 			}
514 			break;
515 
516 		case AI_EVENT_PLAYER_COMMAND:
517 			/* Player incoming command */
518 			const PlayerCommandEvent* pce = (const PlayerCommandEvent*) data;
519 			bool importantCommand = false;
520 
521 			if(pce->command.id < 0)
522 				importantCommand = true;
523 			else {
524 				switch(pce->command.id)
525 				{
526 					case CMD_MOVE:
527 					case CMD_PATROL:
528 					case CMD_FIGHT:
529 					case CMD_ATTACK:
530 					case CMD_AREA_ATTACK:
531 					case CMD_GUARD:
532 					case CMD_REPAIR:
533 					case CMD_LOAD_UNITS:
534 					case CMD_UNLOAD_UNITS:
535 					case CMD_UNLOAD_UNIT:
536 					case CMD_RECLAIM:
537 					case CMD_DGUN:
538 					case CMD_RESTORE:
539 					case CMD_RESURRECT:
540 					case CMD_CAPTURE:
541 						importantCommand = true;
542 						break;
543 				}
544 			}
545 
546 			if(importantCommand && !pce->units.empty()) {
547 				for(int i = 0; i < pce->units.size(); i++) {
548 					const int uid = pce->units[i];
549 					if(ai->unittable->unitsUnderPlayerControl.find(uid) == ai->unittable->unitsUnderPlayerControl.end()) {
550 						// we have to remove unit from a group, but not
551 						// to emulate unit death
552 						CUnit* unit = ai->unittable->getUnit(uid);
553 
554 						if (unit == NULL)
555 							continue;
556 
557 						// remove unit from group so it will not receive
558 						// AI commands anymore...
559 						if(unit->group) {
560 							unit->group->remove(*unit);
561 						}
562 
563 						unit->micro(false);
564 						ai->unittable->idle[uid] = false; // because player controls it
565 						ai->unittable->unitsUnderPlayerControl[uid] = unit;
566 
567 						LOG_II("CE323AI::PlayerCommand " << (*unit) << " is under human control")
568 					}
569 				}
570 			}
571 			break;
572 	}
573 
574 	return 0;
575 }
576 
577 /* Update AI per logical frame = 1/30 sec on gamespeed 1.0 */
Update()578 void CE323AI::Update() {
579 	const int currentFrame = ai->cb->GetCurrentFrame();
580 
581 	if (currentFrame < 0)
582 		return; // some shit happened with engine? (stolen from AAI)
583 
584 	// NOTE: AI can be attached in mid-game state with /aicontrol command
585 	int localFrame;
586 
587 	if (attachedAtFrame < 0) {
588 		attachedAtFrame = currentFrame - 1;
589 	}
590 
591 	localFrame = currentFrame - attachedAtFrame;
592 
593 	if(localFrame == 1)
594 		ai->intel->init();
595 
596 	if(!ai->economy->isInitialized())
597 		return;
598 
599 	// anyway show AI is loaded even if it is not playing actually...
600 	if (localFrame == 800 && ai->isMaster()) {
601 		LOG_SS("*** " << AI_VERSION << " ***");
602 		LOG_SS("*** " << AI_CREDITS << " ***");
603 		LOG_SS("*** " <<  AI_NOTES  << " ***");
604 	}
605 
606 	/* Make sure we shift the multiplexer for each instance of E323AI */
607 	int aiframe = localFrame + ai->team;
608 
609 	// Make sure we start playing from "eco-incomes" update
610 	if(!isRunning) {
611 		isRunning = aiframe % MULTIPLEXER == 0;
612 	}
613 
614 	if(!isRunning)
615 		return;
616 
617 	/* Rotate through the different update events to distribute computations */
618 	switch(aiframe % MULTIPLEXER) {
619 		case 0: { /* update incomes */
620 			ai->economy->updateIncomes();
621 		}
622 		break;
623 
624 		case 1: { /* update threatmap */
625 			PROFILE(threatmap)
626 			ai->threatmap->update(localFrame);
627 		}
628 		break;
629 
630 		case 2: { /* update the path itself of a group */
631 			PROFILE(A*)
632 			ai->pathfinder->updatePaths();
633 		}
634 		break;
635 
636 		case 3: { /* update the groups following a path */
637 			PROFILE(following)
638 			ai->pathfinder->updateFollowers();
639 		}
640 		break;
641 
642 		case 4: { /* update enemy intel */
643 			PROFILE(intel)
644 			ai->intel->update(localFrame);
645 		}
646 		break;
647 
648 		case 5: { /* update defense matrix */
649 			PROFILE(defensematrix)
650 			ai->defensematrix->update();
651 			ai->coverage->update();
652 		}
653 
654 		case 6: { /* update military */
655 			PROFILE(military)
656 			ai->military->update(localFrame);
657 		}
658 		break;
659 
660 		case 7: { /* update economy */
661 			PROFILE(economy)
662 			ai->economy->update();
663 		}
664 		break;
665 
666 		case 8: { /* update taskhandler */
667 			PROFILE(taskhandler)
668 			ai->tasks->update();
669 		}
670 
671 		case 9: { /* update unit table */
672 			ai->unittable->update();
673 		}
674 		break;
675 	}
676 }
677