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