1 // -------------------------------------------------------------------------
2 // AAI
3 //
4 // A skirmish AI for the Spring engine.
5 // Copyright Alexander Seizinger
6 //
7 // Released under GPL license: see LICENSE.html for more information.
8 // -------------------------------------------------------------------------
9 
10 #include "AAI.h"
11 #include "AAIUnitTable.h"
12 #include "AAIExecute.h"
13 #include "AAIConstructor.h"
14 #include "AAIBuildTable.h"
15 #include "AAIAirForceManager.h"
16 #include "AAIConfig.h"
17 #include "AAIMap.h"
18 #include "AAIGroup.h"
19 #include "AAIConstructor.h"
20 
21 #include "LegacyCpp/UnitDef.h"
22 using namespace springLegacyAI;
23 
24 
AAIUnitTable(AAI * ai)25 AAIUnitTable::AAIUnitTable(AAI *ai)
26 {
27 	this->ai = ai;
28 
29 	units.resize(cfg->MAX_UNITS);
30 
31 	// fill buildtable
32 	for(int i = 0; i < cfg->MAX_UNITS; ++i)
33 	{
34 		units[i].unit_id = -1;
35 		units[i].def_id = 0;
36 		units[i].group = 0;
37 		units[i].cons = 0;
38 		units[i].status = UNIT_KILLED;
39 		units[i].last_order = 0;
40 	}
41 
42 	for(int i = 0; i <= MOBILE_CONSTRUCTOR; ++i)
43 	{
44 		activeUnits[i] = 0;
45 		futureUnits[i] = 0;
46 		requestedUnits[i] = 0;
47 	}
48 
49 	activeBuilders = futureBuilders = 0;
50 	activeFactories = futureFactories = 0;
51 
52 	cmdr = -1;
53 }
54 
~AAIUnitTable(void)55 AAIUnitTable::~AAIUnitTable(void)
56 {
57 	// delete constructors
58 	for(set<int>::iterator cons = constructors.begin(); cons != constructors.end(); ++cons)
59 	{
60 		delete units[*cons].cons;
61 	}
62 }
63 
64 
AddUnit(int unit_id,int def_id,AAIGroup * group,AAIConstructor * cons)65 bool AAIUnitTable::AddUnit(int unit_id, int def_id, AAIGroup *group, AAIConstructor *cons)
66 {
67 	if(unit_id <= cfg->MAX_UNITS)
68 	{
69 		// clear possible enemies that are still listed (since they had been killed outside of los)
70 		if(units[unit_id].status == ENEMY_UNIT)
71 		{
72 			if(units[unit_id].group)
73 				units[unit_id].group->TargetUnitKilled();
74 		}
75 		else if(units[unit_id].status == BOMB_TARGET)
76 		{
77 			ai->Getaf()->RemoveTarget(unit_id);
78 
79 			if(units[unit_id].group)
80 				units[unit_id].group->TargetUnitKilled();
81 		}
82 
83 		units[unit_id].unit_id = unit_id;
84 		units[unit_id].def_id = def_id;
85 		units[unit_id].group = group;
86 		units[unit_id].cons = cons;
87 		units[unit_id].status = UNIT_IDLE;
88 		return true;
89 	}
90 	else
91 	{
92 		ai->Log("ERROR: AAIUnitTable::AddUnit() index %i out of range", unit_id);
93 		return false;
94 	}
95 }
96 
RemoveUnit(int unit_id)97 void AAIUnitTable::RemoveUnit(int unit_id)
98 {
99 	if(unit_id <= cfg->MAX_UNITS)
100 	{
101 		units[unit_id].unit_id = -1;
102 		units[unit_id].def_id = 0;
103 		units[unit_id].group = 0;
104 		units[unit_id].cons = 0;
105 		units[unit_id].status = UNIT_KILLED;
106 	}
107 	else
108 	{
109 		ai->Log("ERROR: AAIUnitTable::RemoveUnit() index %i out of range", unit_id);
110 	}
111 }
112 
AddConstructor(int unit_id,int def_id)113 void AAIUnitTable::AddConstructor(int unit_id, int def_id)
114 {
115 	bool factory;
116 	bool builder;
117 	bool assister;
118 
119 	if(ai->Getbt()->units_static[def_id].unit_type & UNIT_TYPE_FACTORY)
120 		factory = true;
121 	else
122 		factory = false;
123 
124 	if(ai->Getbt()->units_static[def_id].unit_type & UNIT_TYPE_BUILDER)
125 		builder = true;
126 	else
127 		builder = false;
128 
129 	if(ai->Getbt()->units_static[def_id].unit_type & UNIT_TYPE_ASSISTER)
130 		assister = true;
131 	else
132 		assister = false;
133 
134 	AAIConstructor *cons = new AAIConstructor(ai, unit_id, def_id, factory, builder, assister);
135 
136 	constructors.insert(unit_id);
137 	units[unit_id].cons = cons;
138 
139 	// increase/decrease number of available/requested builders for all buildoptions of the builder
140 	for(list<int>::iterator unit = ai->Getbt()->units_static[def_id].canBuildList.begin();  unit != ai->Getbt()->units_static[def_id].canBuildList.end(); ++unit)
141 	{
142 		ai->Getbt()->units_dynamic[*unit].constructorsAvailable += 1;
143 		ai->Getbt()->units_dynamic[*unit].constructorsRequested -= 1;
144 	}
145 
146 	if(builder)
147 	{
148 		--futureBuilders;
149 		++activeBuilders;
150 	}
151 
152 	if(factory && ai->Getbt()->IsStatic(def_id))
153 	{
154 		--futureFactories;
155 		++activeFactories;
156 
157 		// remove future ressource demand now factory has been finished
158 		ai->Getexecute()->futureRequestedMetal -= ai->Getbt()->units_static[def_id].efficiency[0];
159 		ai->Getexecute()->futureRequestedEnergy -= ai->Getbt()->units_static[def_id].efficiency[1];
160 	}
161 }
162 
RemoveConstructor(int unit_id,int def_id)163 void AAIUnitTable::RemoveConstructor(int unit_id, int def_id)
164 {
165 	if(units[unit_id].cons->builder)
166 		activeBuilders -= 1;
167 
168 	if(units[unit_id].cons->factory && ai->Getbt()->IsStatic(def_id))
169 		activeFactories -= 1;
170 
171 	// decrease number of available builders for all buildoptions of the builder
172 	for(list<int>::iterator unit = ai->Getbt()->units_static[def_id].canBuildList.begin();  unit != ai->Getbt()->units_static[def_id].canBuildList.end(); ++unit)
173 		ai->Getbt()->units_dynamic[*unit].constructorsAvailable -= 1;
174 
175 	// erase from builders list
176 	constructors.erase(unit_id);
177 
178 	// clean up memory
179 	units[unit_id].cons->Killed();
180 	delete units[unit_id].cons;
181 	units[unit_id].cons = 0;
182 }
183 
AddCommander(int unit_id,int def_id)184 void AAIUnitTable::AddCommander(int unit_id, int def_id)
185 {
186 	bool factory;
187 	bool builder;
188 	bool assister;
189 
190 	if(ai->Getbt()->units_static[def_id].unit_type & UNIT_TYPE_FACTORY)
191 		factory = true;
192 	else
193 		factory = false;
194 
195 	if(ai->Getbt()->units_static[def_id].unit_type & UNIT_TYPE_BUILDER)
196 		builder = true;
197 	else
198 		builder = false;
199 
200 	if(ai->Getbt()->units_static[def_id].unit_type & UNIT_TYPE_ASSISTER)
201 		assister = true;
202 	else
203 		assister = false;
204 
205 	AAIConstructor *cons = new AAIConstructor(ai, unit_id, def_id, factory, builder, assister);
206 	constructors.insert(unit_id);
207 	units[unit_id].cons = cons;
208 
209 	cmdr = unit_id;
210 
211 	// increase number of builders for all buildoptions of the commander
212 	for(list<int>::iterator unit = ai->Getbt()->units_static[def_id].canBuildList.begin();  unit != ai->Getbt()->units_static[def_id].canBuildList.end(); ++unit)
213 		++ai->Getbt()->units_dynamic[*unit].constructorsAvailable;
214 
215 }
216 
RemoveCommander(int unit_id,int def_id)217 void AAIUnitTable::RemoveCommander(int unit_id, int def_id)
218 {
219 	// decrease number of builders for all buildoptions of the commander
220 	for(list<int>::iterator unit = ai->Getbt()->units_static[def_id].canBuildList.begin();  unit != ai->Getbt()->units_static[def_id].canBuildList.end(); ++unit)
221 		--ai->Getbt()->units_dynamic[*unit].constructorsAvailable;
222 
223 	// erase from builders list
224 	constructors.erase(unit_id);
225 
226 	// clean up memory
227 	units[unit_id].cons->Killed();
228 	delete units[unit_id].cons;
229 	units[unit_id].cons = 0;
230 
231 	// commander has been destroyed, set pointer to zero
232 	if(unit_id == cmdr)
233 		cmdr = -1;
234 }
235 
AddExtractor(int unit_id)236 void AAIUnitTable::AddExtractor(int unit_id)
237 {
238 	extractors.insert(unit_id);
239 }
240 
RemoveExtractor(int unit_id)241 void AAIUnitTable::RemoveExtractor(int unit_id)
242 {
243 	extractors.erase(unit_id);
244 }
245 
AddScout(int unit_id)246 void AAIUnitTable::AddScout(int unit_id)
247 {
248 	scouts.insert(unit_id);
249 }
250 
RemoveScout(int unit_id)251 void AAIUnitTable::RemoveScout(int unit_id)
252 {
253 	scouts.erase(unit_id);
254 }
255 
AddPowerPlant(int unit_id,int def_id)256 void AAIUnitTable::AddPowerPlant(int unit_id, int def_id)
257 {
258 	power_plants.insert(unit_id);
259 	ai->Getexecute()->futureAvailableEnergy -= ai->Getbt()->units_static[def_id].efficiency[0];
260 }
261 
RemovePowerPlant(int unit_id)262 void AAIUnitTable::RemovePowerPlant(int unit_id)
263 {
264 	power_plants.erase(unit_id);
265 }
266 
AddMetalMaker(int unit_id,int def_id)267 void AAIUnitTable::AddMetalMaker(int unit_id, int def_id)
268 {
269 	metal_makers.insert(unit_id);
270 	ai->Getexecute()->futureRequestedEnergy -= ai->Getbt()->GetUnitDef(def_id).energyUpkeep;
271 }
272 
RemoveMetalMaker(int unit_id)273 void AAIUnitTable::RemoveMetalMaker(int unit_id)
274 {
275 	if(!ai->Getcb()->IsUnitActivated(unit_id))
276 		--ai->Getexecute()->disabledMMakers;
277 
278 	metal_makers.erase(unit_id);
279 }
280 
AddRecon(int unit_id,int def_id)281 void AAIUnitTable::AddRecon(int unit_id, int def_id)
282 {
283 	recon.insert(unit_id);
284 
285 	ai->Getexecute()->futureRequestedEnergy -= ai->Getbt()->units_static[def_id].efficiency[0];
286 }
287 
RemoveRecon(int unit_id)288 void AAIUnitTable::RemoveRecon(int unit_id)
289 {
290 	recon.erase(unit_id);
291 }
292 
AddJammer(int unit_id,int def_id)293 void AAIUnitTable::AddJammer(int unit_id, int def_id)
294 {
295 	jammers.insert(unit_id);
296 
297 	ai->Getexecute()->futureRequestedEnergy -= ai->Getbt()->units_static[def_id].efficiency[0];
298 }
299 
RemoveJammer(int unit_id)300 void AAIUnitTable::RemoveJammer(int unit_id)
301 {
302 	jammers.erase(unit_id);
303 }
304 
AddStationaryArty(int unit_id,int)305 void AAIUnitTable::AddStationaryArty(int unit_id, int /*def_id*/)
306 {
307 	stationary_arty.insert(unit_id);
308 }
309 
RemoveStationaryArty(int unit_id)310 void AAIUnitTable::RemoveStationaryArty(int unit_id)
311 {
312 	stationary_arty.erase(unit_id);
313 }
314 
FindBuilder(int building,bool commander)315 AAIConstructor* AAIUnitTable::FindBuilder(int building, bool commander)
316 {
317 	//ai->Log("constructor for %s\n", ai->Getbt()->GetCategoryString(building));
318 	AAIConstructor *builder;
319 
320 	// look for idle builder
321 	for(set<int>::iterator i = constructors.begin(); i != constructors.end(); ++i)
322 	{
323 		// check all builders
324 		if(units[*i].cons->builder)
325 		{
326 			builder = units[*i].cons;
327 
328 			// find unit that can directly build that building
329 			if(builder->task != BUILDING && ai->Getbt()->CanBuildUnit(builder->def_id, building))
330 			{
331 				//if(ai->Getbt()->units_static[building].category == STATIONARY_JAMMER)
332 				//	ai->Log("%s can build %s\n", ai->Getbt()->GetUnitDef(builder->def_id-1).humanName.c_str(), ai->Getbt()->GetUnitDef(building-1).humanName.c_str());
333 
334 				// filter out commander (if not allowed)
335 				if(! (!commander &&  ai->Getbt()->IsCommander(builder->def_id)) )
336 					return builder;
337 			}
338 		}
339 	}
340 
341 	// no builder found
342 	return 0;
343 }
344 
FindClosestBuilder(int building,float3 * pos,bool commander,float * min_dist)345 AAIConstructor* AAIUnitTable::FindClosestBuilder(int building, float3 *pos, bool commander, float *min_dist)
346 {
347 	float my_dist;
348 	AAIConstructor *best_builder = 0, *builder;
349 	float3 builder_pos;
350 	bool suitable;
351 
352 	int continent = ai->Getmap()->GetContinentID(pos);
353 	*min_dist = 100000.0f;
354 
355 	// look for idle builder
356 	for(set<int>::iterator i = constructors.begin(); i != constructors.end(); ++i)
357 	{
358 		// check all builders
359 		if(units[*i].cons->builder)
360 		{
361 			builder = units[*i].cons;
362 
363 			// find idle or assisting builder, who can build this building
364 			if(  builder->task != BUILDING && ai->Getbt()->CanBuildUnit(builder->def_id, building))
365 			{
366 				builder_pos = ai->Getcb()->GetUnitPos(builder->unit_id);
367 
368 				// check continent if necessary
369 				if(ai->Getbt()->units_static[builder->def_id].movement_type & MOVE_TYPE_CONTINENT_BOUND)
370 				{
371 					if(ai->Getmap()->GetContinentID(&builder_pos) == continent)
372 						suitable = true;
373 					else
374 						suitable = false;
375 				}
376 				else
377 					suitable = true;
378 
379 				// filter out commander
380 				if(suitable && ( commander || !ai->Getbt()->IsCommander(builder->def_id) ) )
381 				{
382 					my_dist = fastmath::apxsqrt( (builder_pos.x - pos->x) * (builder_pos.x - pos->x) + (builder_pos.z - pos->z) * (builder_pos.z - pos->z) );
383 
384 					if(ai->Getbt()->GetUnitDef(builder->def_id).speed > 0)
385 						my_dist /= ai->Getbt()->GetUnitDef(builder->def_id).speed;
386 
387 					if(my_dist < *min_dist)
388 					{
389 						best_builder = builder;
390 						*min_dist = my_dist;
391 					}
392 				}
393 			}
394 		}
395 	}
396 
397 	return best_builder;
398 }
399 
FindClosestAssistant(float3 pos,int,bool commander)400 AAIConstructor* AAIUnitTable::FindClosestAssistant(float3 pos, int /*importance*/, bool commander)
401 {
402 	AAIConstructor *best_assistant = 0, *assistant;
403 	float best_rating = 0, my_rating, dist;
404 	float3 assistant_pos;
405 	bool suitable;
406 
407 	int continent = ai->Getmap()->GetContinentID(&pos);
408 
409 	// find idle builder
410 	for(set<int>::iterator i = constructors.begin(); i != constructors.end(); ++i)
411 	{
412 		// check all assisters
413 		if(units[*i].cons->assistant)
414 		{
415 			assistant = units[*i].cons;
416 
417 			// find idle assister
418 			if(assistant->task == UNIT_IDLE)
419 			{
420 				assistant_pos = ai->Getcb()->GetUnitPos(assistant->unit_id);
421 
422 				// check continent if necessary
423 				if(ai->Getbt()->units_static[assistant->def_id].movement_type & MOVE_TYPE_CONTINENT_BOUND)
424 				{
425 					if(ai->Getmap()->GetContinentID(&assistant_pos) == continent)
426 						suitable = true;
427 					else
428 						suitable = false;
429 				}
430 				else
431 					suitable = true;
432 
433 				// filter out commander
434 				if(suitable && ( commander || !ai->Getbt()->IsCommander(assistant->def_id) ) )
435 				{
436 					dist = (pos.x - assistant_pos.x) * (pos.x - assistant_pos.x) + (pos.z - assistant_pos.z) * (pos.z - assistant_pos.z);
437 
438 					if(dist > 0)
439 						my_rating = assistant->buildspeed / fastmath::apxsqrt(dist);
440 					else
441 						my_rating = 1;
442 
443 					if(my_rating > best_rating)
444 					{
445 						best_rating = my_rating;
446 						best_assistant = assistant;
447 					}
448 				}
449 			}
450 		}
451 	}
452 
453 	// no assister found -> request one
454 	if(!best_assistant)
455 	{
456 		unsigned int allowed_movement_types = 22;
457 
458 		if(ai->Getcb()->GetElevation(pos.x, pos.z) < 0)
459 			allowed_movement_types |= MOVE_TYPE_SEA;
460 		else
461 			allowed_movement_types |= MOVE_TYPE_GROUND;
462 
463 		ai->Getbt()->AddAssistant(allowed_movement_types, true);
464 	}
465 
466 	return best_assistant;
467 }
468 
IsUnitCommander(int unit_id)469 bool AAIUnitTable::IsUnitCommander(int unit_id)
470 {
471 	if(cmdr != -1)
472 		return false;
473 	else if(cmdr == unit_id)
474 		return true;
475 	else
476 		return false;
477 }
478 
IsDefCommander(int def_id)479 bool AAIUnitTable::IsDefCommander(int def_id)
480 {
481 	for(int s = 0; s < cfg->SIDES; ++s)
482 	{
483 		if(ai->Getbt()->startUnits[s] == def_id)
484 			return true;
485 	}
486 
487 	return false;
488 }
489 
EnemyKilled(int unit)490 void AAIUnitTable::EnemyKilled(int unit)
491 {
492 	if(units[unit].status == BOMB_TARGET)
493 		ai->Getaf()->RemoveTarget(unit);
494 
495 
496 	if(units[unit].group)
497 		units[unit].group->TargetUnitKilled();
498 
499 	RemoveUnit(unit);
500 }
501 
AssignGroupToEnemy(int unit,AAIGroup * group)502 void AAIUnitTable::AssignGroupToEnemy(int unit, AAIGroup *group)
503 {
504 	units[unit].unit_id = unit;
505 	units[unit].group = group;
506 	units[unit].status = ENEMY_UNIT;
507 }
508 
509 
SetUnitStatus(int unit,UnitTask status)510 void AAIUnitTable::SetUnitStatus(int unit, UnitTask status)
511 {
512 	units[unit].status = status;
513 }
514 
IsBuilder(int unit_id)515 bool AAIUnitTable::IsBuilder(int unit_id)
516 {
517 	if(units[unit_id].cons && units[unit_id].cons->builder)
518 		return true;
519 	else
520 		return false;
521 }
522 
ActiveUnitKilled(UnitCategory category)523 void AAIUnitTable::ActiveUnitKilled(UnitCategory category)
524 {
525 	--activeUnits[category];
526 }
527 
FutureUnitKilled(UnitCategory category)528 void AAIUnitTable::FutureUnitKilled(UnitCategory category)
529 {
530 	--futureUnits[category];
531 }
532 
UnitCreated(UnitCategory category)533 void AAIUnitTable::UnitCreated(UnitCategory category)
534 {
535 	--requestedUnits[category];
536 	++futureUnits[category];
537 }
538 
UnitFinished(UnitCategory category)539 void AAIUnitTable::UnitFinished(UnitCategory category)
540 {
541 	--futureUnits[category];
542 	++activeUnits[category];
543 }
544 
UnitRequestFailed(UnitCategory category)545 void AAIUnitTable::UnitRequestFailed(UnitCategory category)
546 {
547 	--requestedUnits[category];
548 }
549 
UnitRequested(UnitCategory category,int number)550 void AAIUnitTable::UnitRequested(UnitCategory category, int number)
551 {
552 	requestedUnits[category] += number;
553 }
554