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
11 #include "AAI.h"
12 #include "AAIExecute.h"
13 #include "AAIBuildTable.h"
14 #include "AAIBrain.h"
15 #include "AAIUnitTable.h"
16 #include "AAIConstructor.h"
17 #include "AAIBuildTask.h"
18 #include "AAIConfig.h"
19 #include "AAIMap.h"
20 #include "AAIGroup.h"
21 #include "AAISector.h"
22
23 #include "LegacyCpp/UnitDef.h"
24 #include "LegacyCpp/CommandQueue.h"
25 using namespace springLegacyAI;
26
27
28 // all the static vars
29 float AAIExecute::current = 0.5;
30 float AAIExecute::learned = 2.5;
31
32
AAIExecute(AAI * ai)33 AAIExecute::AAIExecute(AAI *ai)
34 {
35 issued_orders = 0;
36
37 this->ai = ai;
38
39 unitProductionRate = 1;
40
41 futureRequestedMetal = 0;
42 futureRequestedEnergy = 0;
43 futureAvailableMetal = 0;
44 futureAvailableEnergy = 0;
45 futureStoredMetal = 0;
46 futureStoredEnergy = 0;
47 averageMetalUsage = 0;
48 averageEnergyUsage = 0;
49 averageMetalSurplus = 0;
50 averageEnergySurplus = 0;
51 disabledMMakers = 0;
52
53 next_defence = 0;
54 def_category = UNKNOWN;
55
56 for(int i = 0; i <= METAL_MAKER; ++i)
57 urgency[i] = 0;
58
59 for(int i = 0; i < 8; i++)
60 {
61 metalSurplus[i] = 0;
62 energySurplus[i] = 0;
63 }
64
65 counter = 0;
66 }
67
~AAIExecute(void)68 AAIExecute::~AAIExecute(void)
69 {
70 // if(buildques)
71 // {
72 // for(int i = 0; i < numOfFactories; ++i)
73 // buildques[i].clear();
74
75 // SafeDeleteArray(buildques);
76 // }
77
78 // if(factory_table)
79 // SafeDeleteArray(factory_table);
80 }
81
82
InitAI(int commander_unit_id,const UnitDef * commander_def)83 void AAIExecute::InitAI(int commander_unit_id, const UnitDef* commander_def)
84 {
85 //debug
86 ai->Log("Playing as %s\n", ai->Getbt()->sideNames[ai->Getside()].c_str());
87
88 if(ai->Getside() < 1 || ai->Getside() > ai->Getbt()->numOfSides)
89 {
90 ai->LogConsole("ERROR: invalid side id %i\n", ai->Getside());
91 return;
92 }
93
94 // tell the brain about the starting sector
95 float3 pos = ai->Getcb()->GetUnitPos(commander_unit_id);
96 int x = pos.x/ai->Getmap()->xSectorSize;
97 int y = pos.z/ai->Getmap()->ySectorSize;
98
99 if(x < 0)
100 x = 0;
101 if(y < 0 )
102 y = 0;
103 if(x >= ai->Getmap()->xSectors)
104 x = ai->Getmap()->xSectors-1;
105 if(y >= ai->Getmap()->ySectors)
106 y = ai->Getmap()->ySectors-1;
107
108 // set sector as part of the base
109 if(ai->Getmap()->team_sector_map[x][y] < 0)
110 {
111 ai->Getbrain()->AddSector(&ai->Getmap()->sector[x][y]);
112 ai->Getbrain()->start_pos = pos;
113 ai->Getbrain()->UpdateNeighbouringSectors();
114 ai->Getbrain()->UpdateBaseCenter();
115 }
116 else
117 {
118 // sector already occupied by another aai team (coms starting too close to each other)
119 // choose next free sector
120 ChooseDifferentStartingSector(x, y);
121 }
122
123 if(ai->Getmap()->map_type == WATER_MAP)
124 ai->Getbrain()->ExpandBase(WATER_SECTOR);
125 else if(ai->Getmap()->map_type == LAND_MAP)
126 ai->Getbrain()->ExpandBase(LAND_SECTOR);
127 else
128 ai->Getbrain()->ExpandBase(LAND_WATER_SECTOR);
129
130 // now that we know the side, init buildques
131 InitBuildques();
132
133 ai->Getbt()->InitCombatEffCache(ai->Getside());
134
135 ai->Getut()->AddCommander(commander_unit_id, commander_def->id);
136
137 // add the highest rated, buildable factory
138 AddStartFactory();
139
140 // get economy working
141 CheckRessources();
142 }
143
CreateBuildTask(int unit,const UnitDef * def,float3 * pos)144 void AAIExecute::CreateBuildTask(int unit, const UnitDef *def, float3 *pos)
145 {
146 AAIBuildTask *task = new AAIBuildTask(ai, unit, def->id, pos, ai->Getcb()->GetCurrentFrame());
147 ai->Getbuild_tasks().push_back(task);
148
149 // find builder and associate building with that builder
150 task->builder_id = -1;
151
152 for(set<int>::iterator i = ai->Getut()->constructors.begin(); i != ai->Getut()->constructors.end(); ++i)
153 {
154 if(ai->Getut()->units[*i].cons->build_pos.x == pos->x && ai->Getut()->units[*i].cons->build_pos.z == pos->z)
155 {
156 ai->Getut()->units[*i].cons->construction_unit_id = unit;
157 task->builder_id = ai->Getut()->units[*i].cons->unit_id;
158 ai->Getut()->units[*i].cons->build_task = task;
159 ai->Getut()->units[*i].cons->CheckAssistance();
160 break;
161 }
162 }
163 }
164
InitBuildingAt(const UnitDef * def,float3 * pos,bool water)165 bool AAIExecute::InitBuildingAt(const UnitDef *def, float3 *pos, bool water)
166 {
167 // determine target sector
168 int x = pos->x / ai->Getmap()->xSectorSize;
169 int y = pos->z / ai->Getmap()->ySectorSize;
170
171 // update buildmap
172 ai->Getmap()->UpdateBuildMap(*pos, def, true, water, ai->Getbt()->IsFactory(def->id));
173
174 // update defence map (if necessary)
175 if(ai->Getbt()->units_static[def->id].category == STATIONARY_DEF)
176 ai->Getmap()->AddDefence(pos, def->id);
177
178 // drop bad sectors (should only happen when defending mexes at the edge of the map)
179 if(x >= 0 && y >= 0 && x < ai->Getmap()->xSectors && y < ai->Getmap()->ySectors)
180 {
181 // increase number of units of that category in the target sector
182 ai->Getmap()->sector[x][y].my_buildings[ai->Getbt()->units_static[def->id].category] += 1;
183
184 return true;
185 }
186 else
187 return false;
188 }
189
MoveUnitTo(int unit,float3 * position)190 void AAIExecute::MoveUnitTo(int unit, float3 *position)
191 {
192 Command c;
193
194 c.id = CMD_MOVE;
195
196 c.params.resize(3);
197 c.params[0] = position->x;
198 c.params[1] = position->y;
199 c.params[2] = position->z;
200
201 //ai->Getcb()->GiveOrder(unit, &c);
202 GiveOrder(&c, unit, "MoveUnitTo");
203 ai->Getut()->SetUnitStatus(unit, MOVING);
204 }
205
stopUnit(int unit)206 void AAIExecute::stopUnit(int unit)
207 {
208 Command c;
209 c.id = CMD_STOP;
210
211 //ai->Getcb()->GiveOrder(unit, &c);
212 GiveOrder(&c, unit, "StopUnit");
213 ai->Getut()->SetUnitStatus(unit, UNIT_IDLE);
214 }
215
216 // returns true if unit is busy
IsBusy(int unit)217 bool AAIExecute::IsBusy(int unit)
218 {
219 const CCommandQueue* commands = ai->Getcb()->GetCurrentUnitCommands(unit);
220
221 if(commands->empty())
222 return false;
223 return true;
224 }
225
226 // adds a unit to the group of attackers
AddUnitToGroup(int unit_id,int def_id,UnitCategory category)227 void AAIExecute::AddUnitToGroup(int unit_id, int def_id, UnitCategory category)
228 {
229 UnitType unit_type = ai->Getbt()->GetUnitType(def_id);
230
231 // determine continent if necessary
232 int continent_id = -1;
233
234 if(ai->Getbt()->units_static[def_id].movement_type & MOVE_TYPE_CONTINENT_BOUND)
235 {
236 float3 unitPos = ai->Getcb()->GetUnitPos(unit_id);
237 continent_id = ai->Getmap()->GetContinentID(&unitPos);
238 }
239
240 // try to add unit to an existing group
241 for(list<AAIGroup*>::iterator group = ai->Getgroup_list()[category].begin(); group != ai->Getgroup_list()[category].end(); ++group)
242 {
243 if((*group)->AddUnit(unit_id, def_id, unit_type, continent_id))
244 {
245 ai->Getut()->units[unit_id].group = *group;
246 return;
247 }
248 }
249
250 // end of grouplist has been reached and unit has not been assigned to any group
251 // -> create new one
252
253 // get continent for ground assault units, even if they are amphibious (otherwise non amphib ground units will be added no matter which continent they are on)
254 if(category == GROUND_ASSAULT && continent_id == -1) {
255 float3 pos = ai->Getcb()->GetUnitPos(unit_id);
256 continent_id = ai->Getmap()->GetContinentID(&pos);
257 }
258 AAIGroup *new_group = new AAIGroup(ai, &ai->Getbt()->GetUnitDef(def_id), unit_type, continent_id);
259
260 ai->Getgroup_list()[category].push_back(new_group);
261 new_group->AddUnit(unit_id, def_id, unit_type, continent_id);
262 ai->Getut()->units[unit_id].group = new_group;
263 }
264
BuildScouts()265 void AAIExecute::BuildScouts()
266 {
267 // check number of scouts and order new ones if necessary
268 if(ai->Getut()->activeUnits[SCOUT] + ai->Getut()->futureUnits[SCOUT] + ai->Getut()->requestedUnits[SCOUT] < cfg->MAX_SCOUTS)
269 {
270 int scout = 0;
271
272 float cost;
273 float los;
274
275 int period = ai->Getbrain()->GetGamePeriod();
276
277 if(period == 0)
278 {
279 cost = 2.0f;
280 los = 0.5f;
281 }
282 else if(period == 1)
283 {
284 cost = 1.0f;
285 los = 2.0f;
286 }
287 else
288 {
289 cost = 0.5f;
290 los = 4.0f;
291 }
292
293 // determine movement type of scout based on map
294 // always: MOVE_TYPE_AIR, MOVE_TYPE_HOVER, MOVE_TYPE_AMPHIB
295 unsigned int allowed_movement_types = 22;
296
297 if(ai->Getmap()->map_type == LAND_MAP)
298 allowed_movement_types |= MOVE_TYPE_GROUND;
299 else if(ai->Getmap()->map_type == LAND_WATER_MAP)
300 {
301 allowed_movement_types |= MOVE_TYPE_GROUND;
302 allowed_movement_types |= MOVE_TYPE_SEA;
303 }
304 else if(ai->Getmap()->map_type == WATER_MAP)
305 allowed_movement_types |= MOVE_TYPE_SEA;
306
307
308 // request cloakable scouts from time to time
309 if(rand()%5 == 1)
310 scout = ai->Getbt()->GetScout(ai->Getside(), los, cost, allowed_movement_types, 10, true, true);
311 else
312 scout = ai->Getbt()->GetScout(ai->Getside(), los, cost, allowed_movement_types, 10, false, true);
313
314 if(scout)
315 {
316 bool urgent = true;
317
318 if(ai->Getut()->activeUnits[SCOUT] >= 2)
319 urgent = false;
320
321 if(AddUnitToBuildqueue(scout, 1, urgent))
322 {
323 ai->Getut()->UnitRequested(SCOUT);
324 ++ai->Getbt()->units_dynamic[scout].requested;
325 }
326 }
327 }
328 }
329
SendScoutToNewDest(int scout)330 void AAIExecute::SendScoutToNewDest(int scout)
331 {
332 float3 pos = ZeroVector;
333
334 // get scout dest
335 ai->Getbrain()->GetNewScoutDest(&pos, scout);
336
337 if(pos.x > 0)
338 MoveUnitTo(scout, &pos);
339 }
340
GetBuildsite(int builder,int building,UnitCategory)341 float3 AAIExecute::GetBuildsite(int builder, int building, UnitCategory /*category*/)
342 {
343 float3 pos;
344 float3 builder_pos;
345 //const UnitDef *def = ai->Getbt()->GetUnitDef(building);
346
347 // check the sector of the builder
348 builder_pos = ai->Getcb()->GetUnitPos(builder);
349 // look in the builders sector first
350 int x = builder_pos.x/ai->Getmap()->xSectorSize;
351 int y = builder_pos.z/ai->Getmap()->ySectorSize;
352
353 if(ai->Getmap()->sector[x][y].distance_to_base == 0)
354 {
355 pos = ai->Getmap()->sector[x][y].GetBuildsite(building);
356
357 // if suitable location found, return pos...
358 if(pos.x)
359 return pos;
360 }
361
362 // look in any of the base sectors
363 for(list<AAISector*>::iterator s = ai->Getbrain()->sectors[0].begin(); s != ai->Getbrain()->sectors[0].end(); ++s)
364 {
365 pos = (*s)->GetBuildsite(building);
366
367 // if suitable location found, return pos...
368 if(pos.x)
369 return pos;
370 }
371
372 pos.x = pos.y = pos.z = 0;
373 return pos;
374 }
375
GetUnitBuildsite(int,int unit)376 float3 AAIExecute::GetUnitBuildsite(int /*builder*/, int unit)
377 {
378 // float3 builder_pos = ai->Getcb()->GetUnitPos(builder);
379 float3 pos = ZeroVector, best_pos = ZeroVector;
380 float min_dist = 1000000, dist;
381
382 for(list<AAISector*>::iterator s = ai->Getbrain()->sectors[1].begin(); s != ai->Getbrain()->sectors[1].end(); ++s)
383 {
384 bool water = false;
385
386 if(ai->Getbt()->IsSea(unit))
387 water = true;
388
389 pos = (*s)->GetBuildsite(unit, water);
390
391 if(pos.x)
392 {
393 dist = sqrt( pow(pos.x - best_pos.x ,2.0f) + pow(pos.z - best_pos.z, 2.0f) );
394
395 if(dist < min_dist)
396 {
397 min_dist = dist;
398 best_pos = pos;
399 }
400 }
401 }
402
403 return best_pos;
404 }
405
GetTotalGroundPower()406 float AAIExecute::GetTotalGroundPower()
407 {
408 float power = 0;
409
410 // get ground power of all ground assault units
411 for(list<AAIGroup*>::iterator group = ai->Getgroup_list()[GROUND_ASSAULT].begin(); group != ai->Getgroup_list()[GROUND_ASSAULT].end(); ++group)
412 power += (*group)->GetCombatPowerVsCategory(0);
413
414 return power;
415 }
416
GetTotalAirPower()417 float AAIExecute::GetTotalAirPower()
418 {
419 float power = 0;
420
421 for(list<AAIGroup*>::iterator group = ai->Getgroup_list()[GROUND_ASSAULT].begin(); group != ai->Getgroup_list()[GROUND_ASSAULT].end(); ++group)
422 {
423 power += (*group)->GetCombatPowerVsCategory(1);
424 }
425
426 return power;
427 }
428
GetBuildqueueOfFactory(int def_id)429 list<int>* AAIExecute::GetBuildqueueOfFactory(int def_id)
430 {
431 for(int i = 0; i < numOfFactories; ++i)
432 {
433 if(factory_table[i] == def_id)
434 return &buildques[i];
435 }
436
437 return 0;
438 }
439
AddUnitToBuildqueue(int def_id,int number,bool urgent)440 bool AAIExecute::AddUnitToBuildqueue(int def_id, int number, bool urgent)
441 {
442 urgent = false;
443
444 //UnitCategory category = ai->Getbt()->units_static[def_id].category;
445
446 list<int> *buildqueue = 0, *temp_buildqueue = 0;
447
448 float my_rating, best_rating = 0;
449
450 for(list<int>::iterator fac = ai->Getbt()->units_static[def_id].builtByList.begin(); fac != ai->Getbt()->units_static[def_id].builtByList.end(); ++fac)
451 {
452 if(ai->Getbt()->units_dynamic[*fac].active > 0)
453 {
454 temp_buildqueue = GetBuildqueueOfFactory(*fac);
455
456 if(temp_buildqueue)
457 {
458 my_rating = (1 + 2 * (float) ai->Getbt()->units_dynamic[*fac].active) / (temp_buildqueue->size() + 3);
459
460 if(ai->Getmap()->map_type == WATER_MAP && !ai->Getbt()->CanPlacedWater(*fac))
461 my_rating /= 10.0;
462 }
463 else
464 my_rating = 0;
465 }
466 else
467 my_rating = 0;
468
469 if(my_rating > best_rating)
470 {
471 best_rating = my_rating;
472 buildqueue = temp_buildqueue;
473 }
474 }
475
476 // determine position
477 if(buildqueue)
478 {
479 if(urgent)
480 {
481 buildqueue->insert(buildqueue->begin(), number, def_id);
482 return true;
483 }
484 else if(buildqueue->size() < cfg->MAX_BUILDQUE_SIZE)
485 {
486 buildqueue->insert(buildqueue->end(), number, def_id);
487 return true;
488 }
489 }
490
491 return false;
492 }
493
InitBuildques()494 void AAIExecute::InitBuildques()
495 {
496 // determine number of factories first
497 numOfFactories = 0;
498
499 // stationary factories
500 for(list<int>::iterator cons = ai->Getbt()->units_of_category[STATIONARY_CONSTRUCTOR][ai->Getside()-1].begin(); cons != ai->Getbt()->units_of_category[STATIONARY_CONSTRUCTOR][ai->Getside()-1].end(); ++cons)
501 {
502 if(ai->Getbt()->units_static[*cons].unit_type & UNIT_TYPE_FACTORY)
503 ++numOfFactories;
504 }
505 // and look for all mobile factories
506 for(list<int>::iterator cons = ai->Getbt()->units_of_category[MOBILE_CONSTRUCTOR][ai->Getside()-1].begin(); cons != ai->Getbt()->units_of_category[MOBILE_CONSTRUCTOR][ai->Getside()-1].end(); ++cons)
507 {
508 if(ai->Getbt()->units_static[*cons].unit_type & UNIT_TYPE_FACTORY)
509 ++numOfFactories;
510 }
511 // and add com
512 for(list<int>::iterator cons = ai->Getbt()->units_of_category[COMMANDER][ai->Getside()-1].begin(); cons != ai->Getbt()->units_of_category[COMMANDER][ai->Getside()-1].end(); ++cons)
513 {
514 if(ai->Getbt()->units_static[*cons].unit_type & UNIT_TYPE_FACTORY)
515 ++numOfFactories;
516 }
517
518 // buildques = new list<int>[numOfFactories];
519 buildques.resize(numOfFactories);
520
521 // set up factory buildque identification
522 // factory_table = new int[numOfFactories];
523 factory_table.resize(numOfFactories);
524
525 int i = 0;
526
527 for(list<int>::iterator cons = ai->Getbt()->units_of_category[STATIONARY_CONSTRUCTOR][ai->Getside()-1].begin(); cons != ai->Getbt()->units_of_category[STATIONARY_CONSTRUCTOR][ai->Getside()-1].end(); ++cons)
528 {
529 if(ai->Getbt()->units_static[*cons].unit_type & UNIT_TYPE_FACTORY)
530 {
531 factory_table[i] = *cons;
532 ++i;
533 }
534 }
535
536 for(list<int>::iterator cons = ai->Getbt()->units_of_category[MOBILE_CONSTRUCTOR][ai->Getside()-1].begin(); cons != ai->Getbt()->units_of_category[MOBILE_CONSTRUCTOR][ai->Getside()-1].end(); ++cons)
537 {
538 if(ai->Getbt()->units_static[*cons].unit_type & UNIT_TYPE_FACTORY)
539 {
540 factory_table[i] = *cons;
541 ++i;
542 }
543 }
544
545 for(list<int>::iterator cons = ai->Getbt()->units_of_category[COMMANDER][ai->Getside()-1].begin(); cons != ai->Getbt()->units_of_category[COMMANDER][ai->Getside()-1].end(); ++cons)
546 {
547 if(ai->Getbt()->units_static[*cons].unit_type & UNIT_TYPE_FACTORY)
548 {
549 factory_table[i] = *cons;
550 ++i;
551 }
552 }
553 }
554
GetRallyPoint(unsigned int unit_movement_type,int continent_id,int min_dist,int max_dist)555 float3 AAIExecute::GetRallyPoint(unsigned int unit_movement_type, int continent_id, int min_dist, int max_dist)
556 {
557 float3 pos;
558
559 // continent bound units must get a rally point on their current continent
560 if(unit_movement_type & MOVE_TYPE_CONTINENT_BOUND)
561 {
562 // check neighbouring sectors
563 for(int i = min_dist; i <= max_dist; ++i)
564 {
565 if(unit_movement_type & MOVE_TYPE_GROUND)
566 ai->Getbrain()->sectors[i].sort(suitable_for_ground_rallypoint);
567 else if(unit_movement_type & MOVE_TYPE_SEA)
568 ai->Getbrain()->sectors[i].sort(suitable_for_sea_rallypoint);
569
570 for(list<AAISector*>::iterator sector = ai->Getbrain()->sectors[i].begin(); sector != ai->Getbrain()->sectors[i].end(); ++sector)
571 {
572 (*sector)->GetMovePosOnContinent(&pos, unit_movement_type, continent_id);
573
574 if(pos.x > 0)
575 return pos;
576 }
577 }
578 }
579 else // non continent bound units may get rally points at any pos (sea or ground)
580 {
581 // check neighbouring sectors
582 for(int i = min_dist; i <= max_dist; ++i)
583 {
584 ai->Getbrain()->sectors[i].sort(suitable_for_all_rallypoint);
585
586 for(list<AAISector*>::iterator sector = ai->Getbrain()->sectors[i].begin(); sector != ai->Getbrain()->sectors[i].end(); ++sector)
587 {
588 (*sector)->GetMovePos(&pos);
589
590 if(pos.x > 0)
591 return pos;
592 }
593 }
594 }
595
596 return ZeroVector;
597 }
598
GetRallyPointCloseTo(UnitCategory,unsigned int unit_movement_type,int continent_id,float3,int min_dist,int max_dist)599 float3 AAIExecute::GetRallyPointCloseTo(UnitCategory /*category*/, unsigned int unit_movement_type, int continent_id, float3 /*pos*/, int min_dist, int max_dist)
600 {
601 float3 move_pos;
602
603
604 if(unit_movement_type & MOVE_TYPE_CONTINENT_BOUND)
605 {
606 for(int i = min_dist; i <= max_dist; ++i)
607 {
608 if(unit_movement_type & MOVE_TYPE_GROUND)
609 ai->Getbrain()->sectors[i].sort(suitable_for_ground_rallypoint);
610 else if(unit_movement_type & MOVE_TYPE_SEA)
611 ai->Getbrain()->sectors[i].sort(suitable_for_sea_rallypoint);
612
613 for(list<AAISector*>::iterator sector = ai->Getbrain()->sectors[i].begin(); sector != ai->Getbrain()->sectors[i].end(); ++sector)
614 {
615 (*sector)->GetMovePosOnContinent(&move_pos, unit_movement_type, continent_id);
616
617 if(move_pos.x > 0)
618 return move_pos;
619 }
620 }
621 }
622 else
623 {
624 for(int i = min_dist; i <= max_dist; ++i)
625 {
626 ai->Getbrain()->sectors[i].sort(suitable_for_all_rallypoint);
627
628 for(list<AAISector*>::iterator sector = ai->Getbrain()->sectors[i].begin(); sector != ai->Getbrain()->sectors[i].end(); ++sector)
629 {
630 (*sector)->GetMovePos(&move_pos);
631
632 if(move_pos.x > 0)
633 return move_pos;
634 }
635 }
636 }
637
638
639 return ZeroVector;
640
641 /*AAISector* best_sector = 0;
642 float best_rating = 0, my_rating;
643 float3 center;
644
645 // check neighbouring sectors
646 for(int i = min_dist; i <= max_dist; i++)
647 {
648 for(list<AAISector*>::iterator sector = ai->Getbrain()->sectors[i].begin(); sector != ai->Getbrain()->sectors[i].end(); sector++)
649 {
650 my_rating = 24.0f / ((*sector)->mobile_combat_power[combat_cat] + 4.0f);
651
652 if(land)
653 my_rating += 8 * pow((*sector)->flat_ratio, 2.0f);
654
655 if(water)
656 my_rating += 8 * pow((*sector)->water_ratio, 2.0f);
657
658 center = (*sector)->GetCenter();
659
660 my_rating /= sqrt( sqrt( pow( pos.x - center.x , 2.0f) + pow( pos.z - center.z , 2.0f) ) );
661
662 if(my_rating > best_rating)
663 {
664 best_rating = my_rating;
665 best_sector = *sector;
666 }
667 }
668 }
669
670 if(best_sector)
671 return best_sector->GetMovePos(unit_movement_type);
672 else
673 return ZeroVector; */
674 }
675
676
677 // ****************************************************************************************************
678 // all building functions
679 // ****************************************************************************************************
680
BuildExtractor()681 bool AAIExecute::BuildExtractor()
682 {
683 AAIConstructor *builder, *land_builder = 0, *water_builder = 0;
684 float3 pos;
685 int land_mex = 0, water_mex = 0;
686 float min_dist;
687
688 float cost = 0.25f + ai->Getbrain()->Affordable() / 6.0f;
689 float efficiency = 6.0 / (cost + 0.75f);
690
691 // check if metal map
692 if(ai->Getmap()->metalMap)
693 {
694 // get id of an extractor and look for suitable builder
695 land_mex = ai->Getbt()->GetMex(ai->Getside(), cost, efficiency, false, false, false);
696
697 if(land_mex && ai->Getbt()->units_dynamic[land_mex].constructorsAvailable <= 0 && ai->Getbt()->units_dynamic[land_mex].constructorsRequested <= 0)
698 {
699 ai->Getbt()->BuildBuilderFor(land_mex);
700 land_mex = ai->Getbt()->GetMex(ai->Getside(), cost, efficiency, false, false, true);
701 }
702
703 land_builder = ai->Getut()->FindBuilder(land_mex, true);
704
705 if(land_builder)
706 {
707 pos = GetBuildsite(land_builder->unit_id, land_mex, EXTRACTOR);
708
709 if(pos.x != 0)
710 land_builder->GiveConstructionOrder(land_mex, pos, false);
711
712 return true;
713 }
714 else
715 {
716 ai->Getbt()->BuildBuilderFor(land_mex);
717 return false;
718 }
719 }
720
721 // normal map
722
723 // select a land/water mex
724 if(ai->Getmap()->land_metal_spots > 0)
725 {
726 land_mex = ai->Getbt()->GetMex(ai->Getside(), cost, efficiency, false, false, false);
727
728 if(land_mex && ai->Getbt()->units_dynamic[land_mex].constructorsAvailable + ai->Getbt()->units_dynamic[land_mex].constructorsRequested <= 0)
729 {
730 ai->Getbt()->BuildBuilderFor(land_mex);
731 land_mex = ai->Getbt()->GetMex(ai->Getside(), cost, efficiency, false, false, true);
732 }
733
734 land_builder = ai->Getut()->FindBuilder(land_mex, true);
735 }
736
737 if(ai->Getmap()->water_metal_spots > 0)
738 {
739 water_mex = ai->Getbt()->GetMex(ai->Getside(), cost, efficiency, false, true, false);
740
741 if(water_mex && ai->Getbt()->units_dynamic[water_mex].constructorsAvailable + ai->Getbt()->units_dynamic[water_mex].constructorsRequested <= 0)
742 {
743 ai->Getbt()->BuildBuilderFor(water_mex);
744 water_mex = ai->Getbt()->GetMex(ai->Getside(), cost, efficiency, false, true, true);
745 }
746
747 water_builder = ai->Getut()->FindBuilder(water_mex, true);
748 }
749
750 // check if there is any builder for at least one of the selected extractors available
751 if(!land_builder && !water_builder)
752 return false;
753
754 // check the first 10 free spots for the one with least distance to available builder
755 int max_spots = 10;
756 int current_spot = 0;
757 bool free_spot_found = false;
758
759 vector<AAIMetalSpot*> spots;
760 spots.resize(max_spots);
761 vector<AAIConstructor*> builders;
762 builders.resize(max_spots, 0);
763
764 vector<float> dist_to_builder;
765
766 // determine max search dist - prevent crashes on smaller maps
767 int max_search_dist = min(cfg->MAX_MEX_DISTANCE, ai->Getbrain()->max_distance);
768
769 for(int sector_dist = 0; sector_dist < max_search_dist; ++sector_dist)
770 {
771 if(sector_dist == 1)
772 ai->Getbrain()->freeBaseSpots = false;
773
774 for(list<AAISector*>::iterator sector = ai->Getbrain()->sectors[sector_dist].begin(); sector != ai->Getbrain()->sectors[sector_dist].end(); ++sector)
775 {
776 if(ai->Getbrain()->MexConstructionAllowedInSector(*sector))
777 {
778 for(list<AAIMetalSpot*>::iterator spot = (*sector)->metalSpots.begin(); spot != (*sector)->metalSpots.end(); ++spot)
779 {
780 if(!(*spot)->occupied)
781 {
782 //
783 if((*spot)->pos.y >= 0 && land_builder)
784 {
785 free_spot_found = true;
786
787 builder = ai->Getut()->FindClosestBuilder(land_mex, &(*spot)->pos, ai->Getbrain()->CommanderAllowedForConstructionAt(*sector, &(*spot)->pos), &min_dist);
788
789 if(builder)
790 {
791 dist_to_builder.push_back(min_dist);
792 spots[current_spot] = *spot;
793 builders[current_spot] = builder;
794
795 ++current_spot;
796 }
797 }
798 else if((*spot)->pos.y < 0 && water_builder)
799 {
800 free_spot_found = true;
801
802 builder = ai->Getut()->FindClosestBuilder(water_mex, &(*spot)->pos, ai->Getbrain()->CommanderAllowedForConstructionAt(*sector, &(*spot)->pos), &min_dist);
803
804 if(builder)
805 {
806 dist_to_builder.push_back(min_dist);
807 spots[current_spot] = *spot;
808 builders[current_spot] = builder;
809
810 ++current_spot;
811 }
812 }
813
814 if(current_spot >= max_spots)
815 break;
816 }
817 }
818 }
819
820 if(current_spot >= max_spots)
821 break;
822 }
823
824 if(current_spot >= max_spots)
825 break;
826 }
827
828 // look for spot with minimum dist to available builder
829 int best = -1;
830 min_dist = 1000000.0f;
831
832 for(size_t i = 0; i < dist_to_builder.size(); ++i)
833 {
834 if(dist_to_builder[i] < min_dist)
835 {
836 best = i;
837 min_dist = dist_to_builder[i];
838 }
839 }
840
841 // order mex construction for best spot
842 if(best >= 0)
843 {
844 if(spots[best]->pos.y < 0)
845 builders[best]->GiveConstructionOrder(water_mex, spots[best]->pos, true);
846 else
847 builders[best]->GiveConstructionOrder(land_mex, spots[best]->pos, false);
848
849 spots[best]->occupied = true;
850
851 return true;
852 }
853
854 // dont build other things if construction could not be started due to unavailable builders
855 if(free_spot_found)
856 return false;
857 else
858 return true;
859 }
860
BuildPowerPlant()861 bool AAIExecute::BuildPowerPlant()
862 {
863 if(ai->Getut()->futureUnits[POWER_PLANT] + ai->Getut()->requestedUnits[POWER_PLANT] > 1)
864 return true;
865 else if(ai->Getut()->futureUnits[POWER_PLANT] <= 0 && ai->Getut()->requestedUnits[POWER_PLANT] > 0)
866 return true;
867 else if(ai->Getut()->futureUnits[POWER_PLANT] > 0)
868 {
869 // try to assist construction of other power plants first
870 AAIConstructor *builder;
871
872 for(list<AAIBuildTask*>::iterator task = ai->Getbuild_tasks().begin(); task != ai->Getbuild_tasks().end(); ++task)
873 {
874 if((*task)->builder_id >= 0)
875 builder = ai->Getut()->units[(*task)->builder_id].cons;
876 else
877 builder = 0;
878
879 // find the power plant that is already under construction
880 if(builder && builder->construction_category == POWER_PLANT)
881 {
882 // dont build further power plants if already building an expensive plant
883 if(ai->Getbt()->units_static[builder->construction_def_id].cost > ai->Getbt()->avg_cost[POWER_PLANT][ai->Getside()-1])
884 return true;
885
886 // try to assist
887 if(builder->assistants.size() < cfg->MAX_ASSISTANTS)
888 {
889 AAIConstructor *assistant = ai->Getut()->FindClosestAssistant(builder->build_pos, 5, true);
890
891 if(assistant)
892 {
893 builder->assistants.insert(assistant->unit_id);
894 assistant->AssistConstruction(builder->unit_id, (*task)->unit_id);
895 return true;
896 }
897 else
898 return false;
899 }
900 }
901 }
902
903 // power plant construction has not started -> builder is still on its way to construction site, wait until starting a new power plant
904 return false;
905 }
906 else if(ai->Getut()->activeFactories < 1 && ai->Getut()->activeUnits[POWER_PLANT] >= 2)
907 return true;
908
909 const float current_energy = ai->Getcb()->GetEnergyIncome();
910
911 // stop building power plants if already to much available energy
912 if(current_energy > 1.5f * ai->Getcb()->GetEnergyUsage() + 200.0f)
913 return true;
914
915 int ground_plant = 0;
916 int water_plant = 0;
917
918 AAIConstructor *builder;
919 float3 pos;
920
921 bool checkWater, checkGround;
922 float urgency;
923 float max_power;
924 float eff;
925 float energy = ai->Getcb()->GetEnergyIncome()+1.0f;
926
927 // check if already one power_plant under construction and energy short
928 if(ai->Getut()->futureUnits[POWER_PLANT] + ai->Getut()->requestedUnits[POWER_PLANT]> 0 && ai->Getut()->activeUnits[POWER_PLANT] > 9 && averageEnergySurplus < 100)
929 {
930 urgency = 0.4f + GetEnergyUrgency();
931 max_power = 0.5f;
932 eff = 2.2f - ai->Getbrain()->Affordable() / 4.0f;
933 }
934 else
935 {
936 max_power = 0.5f + pow((float) ai->Getut()->activeUnits[POWER_PLANT], 0.8f);
937 eff = 0.5 + 1.0f / (ai->Getbrain()->Affordable() + 0.5f);
938 urgency = 0.5f + GetEnergyUrgency();
939 }
940
941 // sort sectors according to threat level
942 learned = 70000.0f / (float)(ai->Getcb()->GetCurrentFrame() + 35000) + 1.0f;
943 current = 2.5f - learned;
944
945 if(ai->Getut()->activeUnits[POWER_PLANT] >= 2)
946 ai->Getbrain()->sectors[0].sort(suitable_for_power_plant);
947
948 // get water and ground plant
949 ground_plant = ai->Getbt()->GetPowerPlant(ai->Getside(), eff, urgency, max_power, energy, false, false, false);
950 // currently aai cannot build this building
951 if(ground_plant && ai->Getbt()->units_dynamic[ground_plant].constructorsAvailable <= 0)
952 {
953 if( ai->Getbt()->units_dynamic[water_plant].constructorsRequested <= 0)
954 ai->Getbt()->BuildBuilderFor(ground_plant);
955
956 ground_plant = ai->Getbt()->GetPowerPlant(ai->Getside(), eff, urgency, max_power, energy, false, false, true);
957 }
958
959 water_plant = ai->Getbt()->GetPowerPlant(ai->Getside(), eff, urgency, max_power, energy, true, false, false);
960 // currently aai cannot build this building
961 if(water_plant && ai->Getbt()->units_dynamic[water_plant].constructorsAvailable <= 0)
962 {
963 if( ai->Getbt()->units_dynamic[water_plant].constructorsRequested <= 0)
964 ai->Getbt()->BuildBuilderFor(water_plant);
965
966 water_plant = ai->Getbt()->GetPowerPlant(ai->Getside(), eff, urgency, max_power, energy, true, false, true);
967 }
968
969 for(list<AAISector*>::iterator sector = ai->Getbrain()->sectors[0].begin(); sector != ai->Getbrain()->sectors[0].end(); ++sector)
970 {
971 if((*sector)->water_ratio < 0.15)
972 {
973 checkWater = false;
974 checkGround = true;
975 }
976 else if((*sector)->water_ratio < 0.85)
977 {
978 checkWater = true;
979 checkGround = true;
980 }
981 else
982 {
983 checkWater = true;
984 checkGround = false;
985 }
986
987 if(checkGround && ground_plant)
988 {
989 pos = (*sector)->GetBuildsite(ground_plant, false);
990
991 if(pos.x > 0)
992 {
993 float min_dist;
994 builder = ai->Getut()->FindClosestBuilder(ground_plant, &pos, true, &min_dist);
995
996 if(builder)
997 {
998 futureAvailableEnergy += ai->Getbt()->units_static[ground_plant].efficiency[0];
999 builder->GiveConstructionOrder(ground_plant, pos, false);
1000 return true;
1001 }
1002 else
1003 {
1004 ai->Getbt()->BuildBuilderFor(ground_plant);
1005 return false;
1006 }
1007 }
1008 else
1009 {
1010 ai->Getbrain()->ExpandBase(LAND_SECTOR);
1011 ai->Log("Base expanded by BuildPowerPlant()\n");
1012 }
1013 }
1014
1015 if(checkWater && water_plant)
1016 {
1017 if(ai->Getut()->constructors.size() > 1 || ai->Getut()->activeUnits[POWER_PLANT] >= 2)
1018 pos = (*sector)->GetBuildsite(water_plant, true);
1019 else
1020 {
1021 builder = ai->Getut()->FindBuilder(water_plant, true);
1022
1023 if(builder)
1024 {
1025 pos = ai->Getmap()->GetClosestBuildsite(&ai->Getbt()->GetUnitDef(water_plant), ai->Getcb()->GetUnitPos(builder->unit_id), 40, true);
1026
1027 if(pos.x <= 0)
1028 pos = (*sector)->GetBuildsite(water_plant, true);
1029 }
1030 else
1031 pos = (*sector)->GetBuildsite(water_plant, true);
1032 }
1033
1034 if(pos.x > 0)
1035 {
1036 float min_dist;
1037 builder = ai->Getut()->FindClosestBuilder(water_plant, &pos, true, &min_dist);
1038
1039 if(builder)
1040 {
1041 futureAvailableEnergy += ai->Getbt()->units_static[water_plant].efficiency[0];
1042 builder->GiveConstructionOrder(water_plant, pos, true);
1043 return true;
1044 }
1045 else
1046 {
1047 ai->Getbt()->BuildBuilderFor(water_plant);
1048 return false;
1049 }
1050 }
1051 else
1052 {
1053 ai->Getbrain()->ExpandBase(WATER_SECTOR);
1054 ai->Log("Base expanded by BuildPowerPlant() (water sector)\n");
1055 }
1056 }
1057 }
1058
1059 return true;
1060 }
1061
BuildMetalMaker()1062 bool AAIExecute::BuildMetalMaker()
1063 {
1064 if(ai->Getut()->activeFactories < 1 && ai->Getut()->activeUnits[EXTRACTOR] >= 2)
1065 return true;
1066
1067 if(ai->Getut()->futureUnits[METAL_MAKER] + ai->Getut()->requestedUnits[METAL_MAKER] > 0 || disabledMMakers >= 1)
1068 return true;
1069
1070 bool checkWater, checkGround;
1071 int maker;
1072 AAIConstructor *builder;
1073 float3 pos;
1074 // urgency < 4
1075
1076 float urgency = GetMetalUrgency() / 2.0f;
1077
1078 float cost = 0.25f + ai->Getbrain()->Affordable() / 2.0f;
1079
1080 float efficiency = 0.25f + ai->Getut()->activeUnits[METAL_MAKER] / 4.0f ;
1081 float metal = efficiency;
1082
1083
1084 // sort sectors according to threat level
1085 learned = 70000.0 / (ai->Getcb()->GetCurrentFrame() + 35000) + 1;
1086 current = 2.5 - learned;
1087
1088 ai->Getbrain()->sectors[0].sort(least_dangerous);
1089
1090 for(list<AAISector*>::iterator sector = ai->Getbrain()->sectors[0].begin(); sector != ai->Getbrain()->sectors[0].end(); ++sector)
1091 {
1092 if((*sector)->water_ratio < 0.15)
1093 {
1094 checkWater = false;
1095 checkGround = true;
1096 }
1097 else if((*sector)->water_ratio < 0.85)
1098 {
1099 checkWater = true;
1100 checkGround = true;
1101 }
1102 else
1103 {
1104 checkWater = true;
1105 checkGround = false;
1106 }
1107
1108 if(checkGround)
1109 {
1110 maker = ai->Getbt()->GetMetalMaker(ai->Getside(), cost, efficiency, metal, urgency, false, false);
1111
1112 // currently aai cannot build this building
1113 if(maker && ai->Getbt()->units_dynamic[maker].constructorsAvailable <= 0)
1114 {
1115 if(ai->Getbt()->units_dynamic[maker].constructorsRequested <= 0)
1116 ai->Getbt()->BuildBuilderFor(maker);
1117
1118 maker = ai->Getbt()->GetMetalMaker(ai->Getside(), cost, efficiency, metal, urgency, false, true);
1119 }
1120
1121 if(maker)
1122 {
1123 pos = (*sector)->GetBuildsite(maker, false);
1124
1125 if(pos.x > 0)
1126 {
1127 float min_dist;
1128 builder = ai->Getut()->FindClosestBuilder(maker, &pos, true, &min_dist);
1129
1130 if(builder)
1131 {
1132 futureRequestedEnergy += ai->Getbt()->GetUnitDef(maker).energyUpkeep;
1133 builder->GiveConstructionOrder(maker, pos, false);
1134 return true;
1135 }
1136 else
1137 {
1138 ai->Getbt()->BuildBuilderFor(maker);
1139 return false;
1140 }
1141 }
1142 else
1143 {
1144 ai->Getbrain()->ExpandBase(LAND_SECTOR);
1145 ai->Log("Base expanded by BuildMetalMaker()\n");
1146 }
1147 }
1148 }
1149
1150 if(checkWater)
1151 {
1152 maker = ai->Getbt()->GetMetalMaker(ai->Getside(), ai->Getbrain()->Affordable(), 8.0/(urgency+2.0), 64.0/(16*urgency+2.0), urgency, true, false);
1153
1154 // currently aai cannot build this building
1155 if(maker && ai->Getbt()->units_dynamic[maker].constructorsAvailable <= 0)
1156 {
1157 if(ai->Getbt()->units_dynamic[maker].constructorsRequested <= 0)
1158 ai->Getbt()->BuildBuilderFor(maker);
1159
1160 maker = ai->Getbt()->GetMetalMaker(ai->Getside(), ai->Getbrain()->Affordable(), 8.0/(urgency+2.0), 64.0/(16*urgency+2.0), urgency, true, true);
1161 }
1162
1163 if(maker)
1164 {
1165 pos = (*sector)->GetBuildsite(maker, true);
1166
1167 if(pos.x > 0)
1168 {
1169 float min_dist;
1170 builder = ai->Getut()->FindClosestBuilder(maker, &pos, true, &min_dist);
1171
1172 if(builder)
1173 {
1174 futureRequestedEnergy += ai->Getbt()->GetUnitDef(maker).energyUpkeep;
1175 builder->GiveConstructionOrder(maker, pos, true);
1176 return true;
1177 }
1178 else
1179 {
1180 ai->Getbt()->BuildBuilderFor(maker);
1181 return false;
1182 }
1183 }
1184 else
1185 {
1186 ai->Getbrain()->ExpandBase(WATER_SECTOR);
1187 ai->Log("Base expanded by BuildMetalMaker() (water sector)\n");
1188 }
1189 }
1190 }
1191 }
1192
1193 return true;
1194 }
1195
BuildStorage()1196 bool AAIExecute::BuildStorage()
1197 {
1198 if(ai->Getut()->futureUnits[STORAGE] + ai->Getut()->requestedUnits[STORAGE]> 0 || ai->Getut()->activeUnits[STORAGE] >= cfg->MAX_STORAGE)
1199 return true;
1200
1201 if(ai->Getut()->activeFactories < 2)
1202 return true;
1203
1204 int storage = 0;
1205 bool checkWater, checkGround;
1206 AAIConstructor *builder;
1207 float3 pos;
1208
1209 float metal = 4 / (ai->Getcb()->GetMetalStorage() + futureStoredMetal - ai->Getcb()->GetMetal() + 1);
1210 float energy = 2 / (ai->Getcb()->GetEnergyStorage() + futureStoredMetal - ai->Getcb()->GetEnergy() + 1);
1211
1212 // urgency < 4
1213 // float urgency = 16.0 / (ai->Getut()->activeUnits[METAL_MAKER] + ai->Getut()->futureUnits[METAL_MAKER] + 4);
1214
1215 for(list<AAISector*>::iterator sector = ai->Getbrain()->sectors[0].begin(); sector != ai->Getbrain()->sectors[0].end(); ++sector)
1216 {
1217 if((*sector)->water_ratio < 0.15)
1218 {
1219 checkWater = false;
1220 checkGround = true;
1221 }
1222 else if((*sector)->water_ratio < 0.85)
1223 {
1224 checkWater = true;
1225 checkGround = true;
1226 }
1227 else
1228 {
1229 checkWater = true;
1230 checkGround = false;
1231 }
1232
1233 if(checkGround)
1234 {
1235 storage = ai->Getbt()->GetStorage(ai->Getside(), ai->Getbrain()->Affordable(), metal, energy, 1, false, false);
1236
1237 if(storage && ai->Getbt()->units_dynamic[storage].constructorsAvailable <= 0)
1238 {
1239 if(ai->Getbt()->units_dynamic[storage].constructorsRequested <= 0)
1240 ai->Getbt()->BuildBuilderFor(storage);
1241
1242 storage = ai->Getbt()->GetStorage(ai->Getside(), ai->Getbrain()->Affordable(), metal, energy, 1, false, true);
1243 }
1244
1245 if(storage)
1246 {
1247 pos = (*sector)->GetBuildsite(storage, false);
1248
1249 if(pos.x > 0)
1250 {
1251 float min_dist;
1252 builder = ai->Getut()->FindClosestBuilder(storage, &pos, true, &min_dist);
1253
1254 if(builder)
1255 {
1256 builder->GiveConstructionOrder(storage, pos, false);
1257 return true;
1258 }
1259 else
1260 {
1261 ai->Getbt()->BuildBuilderFor(storage);
1262 return false;
1263 }
1264 }
1265 else
1266 {
1267 ai->Getbrain()->ExpandBase(LAND_SECTOR);
1268 ai->Log("Base expanded by BuildStorage()\n");
1269 }
1270 }
1271 }
1272
1273 if(checkWater)
1274 {
1275 storage = ai->Getbt()->GetStorage(ai->Getside(), ai->Getbrain()->Affordable(), metal, energy, 1, true, false);
1276
1277 if(storage && ai->Getbt()->units_dynamic[storage].constructorsAvailable <= 0)
1278 {
1279 if( ai->Getbt()->units_dynamic[storage].constructorsRequested <= 0)
1280 ai->Getbt()->BuildBuilderFor(storage);
1281
1282 storage = ai->Getbt()->GetStorage(ai->Getside(), ai->Getbrain()->Affordable(), metal, energy, 1, true, true);
1283 }
1284
1285 if(storage)
1286 {
1287 pos = (*sector)->GetBuildsite(storage, true);
1288
1289 if(pos.x > 0)
1290 {
1291 float min_dist;
1292 builder = ai->Getut()->FindClosestBuilder(storage, &pos, true, &min_dist);
1293
1294 if(builder)
1295 {
1296 builder->GiveConstructionOrder(storage, pos, true);
1297 return true;
1298 }
1299 else
1300 {
1301 ai->Getbt()->BuildBuilderFor(storage);
1302 return false;
1303 }
1304 }
1305 else
1306 {
1307 ai->Getbrain()->ExpandBase(WATER_SECTOR);
1308 ai->Log("Base expanded by BuildStorage()\n");
1309 }
1310 }
1311 }
1312 }
1313
1314 return true;
1315 }
1316
BuildAirBase()1317 bool AAIExecute::BuildAirBase()
1318 {
1319 if(ai->Getut()->futureUnits[AIR_BASE] + ai->Getut()->requestedUnits[AIR_BASE] > 0 || ai->Getut()->activeUnits[AIR_BASE] >= cfg->MAX_AIR_BASE)
1320 return true;
1321
1322 int airbase = 0;
1323 bool checkWater, checkGround;
1324 AAIConstructor *builder;
1325 float3 pos;
1326
1327 for(list<AAISector*>::iterator sector = ai->Getbrain()->sectors[0].begin(); sector != ai->Getbrain()->sectors[0].end(); ++sector)
1328 {
1329 if((*sector)->water_ratio < 0.15)
1330 {
1331 checkWater = false;
1332 checkGround = true;
1333 }
1334 else if((*sector)->water_ratio < 0.85)
1335 {
1336 checkWater = true;
1337 checkGround = true;
1338 }
1339 else
1340 {
1341 checkWater = true;
1342 checkGround = false;
1343 }
1344
1345 if(checkGround)
1346 {
1347
1348 airbase = ai->Getbt()->GetAirBase(ai->Getside(), ai->Getbrain()->Affordable(), false, false);
1349
1350 if(airbase && ai->Getbt()->units_dynamic[airbase].constructorsAvailable <= 0)
1351 {
1352 if(ai->Getbt()->units_dynamic[airbase].constructorsRequested <= 0)
1353 ai->Getbt()->BuildBuilderFor(airbase);
1354
1355 airbase = ai->Getbt()->GetAirBase(ai->Getside(), ai->Getbrain()->Affordable(), false, true);
1356 }
1357
1358 if(airbase)
1359 {
1360 pos = (*sector)->GetBuildsite(airbase, false);
1361
1362 if(pos.x > 0)
1363 {
1364 float min_dist;
1365 builder = ai->Getut()->FindClosestBuilder(airbase, &pos, true, &min_dist);
1366
1367 if(builder)
1368 {
1369 builder->GiveConstructionOrder(airbase, pos, false);
1370 return true;
1371 }
1372 else
1373 {
1374 ai->Getbt()->BuildBuilderFor(airbase);
1375 return false;
1376 }
1377 }
1378 else
1379 {
1380 ai->Getbrain()->ExpandBase(LAND_SECTOR);
1381 ai->Log("Base expanded by BuildAirBase()\n");
1382 }
1383 }
1384 }
1385
1386 if(checkWater)
1387 {
1388 airbase = ai->Getbt()->GetAirBase(ai->Getside(), ai->Getbrain()->Affordable(), true, false);
1389
1390 if(airbase && ai->Getbt()->units_dynamic[airbase].constructorsAvailable <= 0 )
1391 {
1392 if(ai->Getbt()->units_dynamic[airbase].constructorsRequested <= 0)
1393 ai->Getbt()->BuildBuilderFor(airbase);
1394
1395 airbase = ai->Getbt()->GetAirBase(ai->Getside(), ai->Getbrain()->Affordable(), true, true);
1396 }
1397
1398 if(airbase)
1399 {
1400 pos = (*sector)->GetBuildsite(airbase, true);
1401
1402 if(pos.x > 0)
1403 {
1404 float min_dist;
1405 builder = ai->Getut()->FindClosestBuilder(airbase, &pos, true, &min_dist);
1406
1407 if(builder)
1408 {
1409 builder->GiveConstructionOrder(airbase, pos, true);
1410 return true;
1411 }
1412 else
1413 {
1414 ai->Getbt()->BuildBuilderFor(airbase);
1415 return false;
1416 }
1417 }
1418 else
1419 {
1420 ai->Getbrain()->ExpandBase(WATER_SECTOR);
1421 ai->Log("Base expanded by BuildAirBase() (water sector)\n");
1422 }
1423 }
1424 }
1425 }
1426
1427 return true;
1428 }
1429
BuildDefences()1430 bool AAIExecute::BuildDefences()
1431 {
1432 if(ai->Getut()->futureUnits[STATIONARY_DEF] + ai->Getut()->requestedUnits[STATIONARY_DEF] > 2 || next_defence == 0)
1433 return true;
1434
1435 BuildOrderStatus status = BuildStationaryDefenceVS(def_category, next_defence);
1436
1437 if(status == BUILDORDER_NOBUILDER)
1438 return false;
1439 else if(status == BUILDORDER_NOBUILDPOS)
1440 ++next_defence->failed_defences;
1441
1442
1443 next_defence = 0;
1444
1445 return true;
1446 }
1447
BuildStationaryDefenceVS(UnitCategory category,AAISector * dest)1448 BuildOrderStatus AAIExecute::BuildStationaryDefenceVS(UnitCategory category, AAISector *dest)
1449 {
1450 // dont build in sectors already occupied by allies
1451 if(dest->allied_structures > 5)
1452 return BUILDORDER_SUCCESFUL;
1453
1454 // dont start construction of further defences if expensive defences are already under construction in this sector
1455 for(list<AAIBuildTask*>::iterator task = ai->Getbuild_tasks().begin(); task != ai->Getbuild_tasks().end(); ++task)
1456 {
1457 if(ai->Getbt()->units_static[(*task)->def_id].category == STATIONARY_DEF)
1458 {
1459 if(dest->PosInSector(&(*task)->build_pos))
1460 {
1461 if(ai->Getbt()->units_static[(*task)->def_id].cost > 0.7f * ai->Getbt()->avg_cost[STATIONARY_DEF][ai->Getside()-1])
1462 return BUILDORDER_SUCCESFUL;
1463 }
1464 }
1465 }
1466
1467 double gr_eff = 0, air_eff = 0, hover_eff = 0, sea_eff = 0, submarine_eff = 0;
1468
1469 bool checkWater, checkGround;
1470 int building;
1471 float3 pos;
1472 AAIConstructor *builder;
1473
1474 float terrain = 2.0f;
1475
1476 if(dest->distance_to_base > 0)
1477 terrain = 5.0f;
1478
1479 if(dest->water_ratio < 0.15f)
1480 {
1481 checkWater = false;
1482 checkGround = true;
1483 }
1484 else if(dest->water_ratio < 0.85f)
1485 {
1486 checkWater = true;
1487 checkGround = true;
1488 }
1489 else
1490 {
1491 checkWater = true;
1492 checkGround = false;
1493 }
1494
1495 double urgency = 0.25f + 10.0f / ((double)dest->my_buildings[STATIONARY_DEF] + dest->GetMyDefencePowerAgainstAssaultCategory(ai->Getbt()->GetIDOfAssaultCategory(category)) + 1.0);
1496 double power = 0.5 + (double)dest->my_buildings[STATIONARY_DEF];
1497 double eff = 0.2 + 2.5 / (urgency + 1.0);
1498 double range = 0.2 + 0.6 / (urgency + 1.0);
1499 double cost = 0.5 + ai->Getbrain()->Affordable()/5.0;
1500
1501 if(dest->my_buildings[STATIONARY_DEF] > 3)
1502 {
1503 int t = rand()%500;
1504
1505 if(t < 70)
1506 {
1507 range = 2.5;
1508 terrain = 10.0f;
1509 }
1510 else if(t < 200)
1511 {
1512 range = 1;
1513 terrain = 5.0f;
1514 }
1515 }
1516
1517 if(category == GROUND_ASSAULT)
1518 gr_eff = 2;
1519 else if(category == AIR_ASSAULT)
1520 air_eff = 2;
1521 else if(category == HOVER_ASSAULT)
1522 {
1523 gr_eff = 1;
1524 sea_eff = 1;
1525 hover_eff = 4;
1526 }
1527 else if(category == SEA_ASSAULT)
1528 {
1529 sea_eff = 4;
1530 submarine_eff = 1;
1531 }
1532 else if(category == SUBMARINE_ASSAULT)
1533 {
1534 sea_eff = 1;
1535 submarine_eff = 4;
1536 }
1537
1538 if(checkGround)
1539 {
1540 if(dest->my_buildings[STATIONARY_DEF] > 2 && rand()%cfg->LEARN_RATE == 1)
1541 building = ai->Getbt()->GetRandomDefence(ai->Getside(), category);
1542 else
1543 building = ai->Getbt()->GetDefenceBuilding(ai->Getside(), eff, power, cost, gr_eff, air_eff, hover_eff, sea_eff, submarine_eff, urgency, range, 8, false, false);
1544
1545 if(building && ai->Getbt()->units_dynamic[building].constructorsAvailable <= 0)
1546 {
1547 if(ai->Getbt()->units_dynamic[building].constructorsRequested <= 0)
1548 ai->Getbt()->BuildBuilderFor(building);
1549
1550 building = ai->Getbt()->GetDefenceBuilding(ai->Getside(), eff, power, cost, gr_eff, air_eff, hover_eff, sea_eff, submarine_eff, urgency, range, 8, false, true);
1551 }
1552
1553 // stop building weak defences if urgency is too low (wait for better defences)
1554 if(dest->my_buildings[STATIONARY_DEF] > 3)
1555 {
1556 if(ai->Getbt()->units_static[building].efficiency[ ai->Getbt()->GetIDOfAssaultCategory(category) ] < 0.75f * ai->Getbt()->avg_eff[ai->Getside()-1][5][ ai->Getbt()->GetIDOfAssaultCategory(category) ] )
1557 building = 0;
1558 }
1559 else if(dest->my_buildings[STATIONARY_DEF] > 6)
1560 {
1561 if(ai->Getbt()->units_static[building].efficiency[ ai->Getbt()->GetIDOfAssaultCategory(category) ] < ai->Getbt()->avg_eff[ai->Getside()-1][5][ ai->Getbt()->GetIDOfAssaultCategory(category) ] )
1562 building = 0;
1563 }
1564
1565 if(building)
1566 {
1567 pos = dest->GetDefenceBuildsite(building, category, terrain, false);
1568
1569 if(pos.x > 0)
1570 {
1571 float min_dist;
1572 builder = ai->Getut()->FindClosestBuilder(building, &pos, true, &min_dist);
1573
1574 if(builder)
1575 {
1576 builder->GiveConstructionOrder(building, pos, false);
1577 return BUILDORDER_SUCCESFUL;
1578 }
1579 else
1580 {
1581 ai->Getbt()->BuildBuilderFor(building);
1582 return BUILDORDER_NOBUILDER;
1583 }
1584 }
1585 else
1586 return BUILDORDER_NOBUILDPOS;
1587 }
1588 }
1589
1590 if(checkWater)
1591 {
1592 if(rand()%cfg->LEARN_RATE == 1 && dest->my_buildings[STATIONARY_DEF] > 3)
1593 building = ai->Getbt()->GetRandomDefence(ai->Getside(), category);
1594 else
1595 building = ai->Getbt()->GetDefenceBuilding(ai->Getside(), eff, power, cost, gr_eff, air_eff, hover_eff, sea_eff, submarine_eff, urgency, 1, 8, true, false);
1596
1597 if(building && ai->Getbt()->units_dynamic[building].constructorsAvailable <= 0)
1598 {
1599 if(ai->Getbt()->units_dynamic[building].constructorsRequested <= 0)
1600 ai->Getbt()->BuildBuilderFor(building);
1601
1602 building = ai->Getbt()->GetDefenceBuilding(ai->Getside(), eff, power, cost, gr_eff, air_eff, hover_eff, sea_eff, submarine_eff, urgency, 1, 8, true, true);
1603 }
1604
1605 // stop building of weak defences if urgency is too low (wait for better defences)
1606 if(dest->my_buildings[STATIONARY_DEF] > 3)
1607 {
1608 if(ai->Getbt()->units_static[building].efficiency[ ai->Getbt()->GetIDOfAssaultCategory(category) ] < 0.75f * ai->Getbt()->avg_eff[ai->Getside()-1][5][ ai->Getbt()->GetIDOfAssaultCategory(category) ] )
1609 building = 0;
1610 }
1611 else if(dest->my_buildings[STATIONARY_DEF] > 6)
1612 {
1613 if(ai->Getbt()->units_static[building].efficiency[ ai->Getbt()->GetIDOfAssaultCategory(category) ] < ai->Getbt()->avg_eff[ai->Getside()-1][5][ ai->Getbt()->GetIDOfAssaultCategory(category) ] )
1614 building = 0;
1615 }
1616
1617 if(building)
1618 {
1619 pos = dest->GetDefenceBuildsite(building, category, terrain, true);
1620
1621 if(pos.x > 0)
1622 {
1623 float min_dist;
1624 builder = ai->Getut()->FindClosestBuilder(building, &pos, true, &min_dist);
1625
1626 if(builder)
1627 {
1628 builder->GiveConstructionOrder(building, pos, true);
1629
1630 // add defence to map
1631 ai->Getmap()->AddDefence(&pos, building);
1632
1633 return BUILDORDER_SUCCESFUL;
1634 }
1635 else
1636 {
1637 ai->Getbt()->BuildBuilderFor(building);
1638 return BUILDORDER_NOBUILDER;
1639 }
1640 }
1641 else
1642 return BUILDORDER_NOBUILDPOS;
1643 }
1644 }
1645
1646 return BUILDORDER_FAILED;
1647 }
1648
BuildArty()1649 bool AAIExecute::BuildArty()
1650 {
1651 if(ai->Getut()->futureUnits[STATIONARY_ARTY] + ai->Getut()->requestedUnits[STATIONARY_ARTY] > 0)
1652 return true;
1653
1654 int ground_arty = 0;
1655 int sea_arty = 0;
1656
1657 float3 my_pos, best_pos;
1658 float my_rating, best_rating = -100000.0f;
1659 int arty = 0;
1660
1661 // get ground radar
1662 if(ai->Getmap()->land_ratio > 0.02f)
1663 {
1664 ground_arty = ai->Getbt()->GetStationaryArty(ai->Getside(), 1, 2, 2, false, false);
1665
1666 if(ground_arty && ai->Getbt()->units_dynamic[ground_arty].constructorsAvailable <= 0)
1667 {
1668 if(ai->Getbt()->units_dynamic[ground_arty].constructorsRequested <= 0)
1669 ai->Getbt()->BuildBuilderFor(ground_arty);
1670
1671 ground_arty =ai->Getbt()->GetStationaryArty(ai->Getside(), 1, 2, 2, false, true);
1672 }
1673 }
1674
1675 // get sea radar
1676 if(ai->Getmap()->water_ratio > 0.02f)
1677 {
1678 sea_arty = ai->Getbt()->GetStationaryArty(ai->Getside(), 1, 2, 2, true, false);
1679
1680 if(sea_arty && ai->Getbt()->units_dynamic[sea_arty].constructorsAvailable <= 0)
1681 {
1682 if(ai->Getbt()->units_dynamic[sea_arty].constructorsRequested <= 0)
1683 ai->Getbt()->BuildBuilderFor(sea_arty);
1684
1685 sea_arty = ai->Getbt()->GetStationaryArty(ai->Getside(), 1, 2, 2, true, true);
1686 }
1687 }
1688
1689 for(list<AAISector*>::iterator sector = ai->Getbrain()->sectors[0].begin(); sector != ai->Getbrain()->sectors[0].end(); ++sector)
1690 {
1691 if((*sector)->my_buildings[STATIONARY_ARTY] < 2)
1692 {
1693 my_pos = ZeroVector;
1694
1695 if(ground_arty && (*sector)->water_ratio < 0.9f)
1696 my_pos = (*sector)->GetRadarArtyBuildsite(ground_arty, ai->Getbt()->units_static[ground_arty].range/4.0f, false);
1697
1698 if(my_pos.x <= 0 && sea_arty && (*sector)->water_ratio > 0.1f)
1699 {
1700 my_pos = (*sector)->GetRadarArtyBuildsite(sea_arty, ai->Getbt()->units_static[sea_arty].range/4.0f, true);
1701
1702 if(my_pos.x > 0)
1703 ground_arty = sea_arty;
1704 }
1705
1706 if(my_pos.x > 0)
1707 {
1708 my_rating = - ai->Getmap()->GetEdgeDistance(&my_pos);
1709
1710 if(my_rating > best_rating)
1711 {
1712 best_rating = my_rating;
1713 best_pos = my_pos;
1714 arty = ground_arty;
1715 }
1716 }
1717 }
1718 }
1719
1720 if(arty)
1721 {
1722 float min_dist;
1723 AAIConstructor *builder = ai->Getut()->FindClosestBuilder(arty, &best_pos, true, &min_dist);
1724
1725 if(builder)
1726 {
1727 builder->GiveConstructionOrder(arty, best_pos, false);
1728 return true;
1729 }
1730 else
1731 {
1732 ai->Getbt()->BuildBuilderFor(ground_arty);
1733 return false;
1734 }
1735 }
1736
1737 return true;
1738 }
1739
BuildFactory()1740 bool AAIExecute::BuildFactory()
1741 {
1742 if(ai->Getut()->futureUnits[STATIONARY_CONSTRUCTOR] + ai->Getut()->requestedUnits[STATIONARY_CONSTRUCTOR] > 0)
1743 return true;
1744
1745 AAIConstructor *builder = 0, *temp_builder;
1746 float3 pos = ZeroVector;
1747 float best_rating = 0, my_rating;
1748 int building = 0, factory_types_requested = 0;
1749
1750 // go through list of factories, build cheapest requested factory first
1751 for(list<int>::iterator fac = ai->Getbt()->units_of_category[STATIONARY_CONSTRUCTOR][ai->Getside()-1].begin(); fac != ai->Getbt()->units_of_category[STATIONARY_CONSTRUCTOR][ai->Getside()-1].end(); ++fac)
1752 {
1753 if(ai->Getbt()->units_dynamic[*fac].requested > 0)
1754 {
1755 ++factory_types_requested;
1756
1757 const float activeFacsOfType = ai->Getbt()->units_dynamic[*fac].active;
1758
1759 my_rating = ai->Getbt()->GetFactoryRating(*fac) / pow(activeFacsOfType + 1.0f, 2.0f);
1760 my_rating *= (1 + sqrt(2.0 + (float) GetBuildqueueOfFactory(*fac)->size()));
1761
1762 if(ai->Getut()->activeFactories < 1)
1763 my_rating /= ai->Getbt()->units_static[*fac].cost;
1764
1765 // skip factories that could not be built
1766 if(ai->Getbt()->units_static[*fac].efficiency[4] > 1)
1767 {
1768 my_rating = 0;
1769 ai->Getbt()->units_static[*fac].efficiency[4] = 0;
1770 }
1771
1772 // only check building if a suitable builder is available
1773 temp_builder = ai->Getut()->FindBuilder(*fac, true);
1774
1775 if(temp_builder && my_rating > best_rating)
1776 {
1777 best_rating = my_rating;
1778 building = *fac;
1779 }
1780 }
1781 }
1782
1783 if(building)
1784 {
1785 bool water;
1786
1787 // land
1788 if(ai->Getbt()->CanPlacedLand(building))
1789 {
1790 water = false;
1791
1792 ai->Getbrain()->sectors[0].sort(suitable_for_ground_factory);
1793
1794 // find buildpos
1795 for(list<AAISector*>::iterator sector = ai->Getbrain()->sectors[0].begin(); sector != ai->Getbrain()->sectors[0].end(); ++sector)
1796 {
1797 pos = (*sector)->GetRandomBuildsite(building, 20, false);
1798
1799 if(pos.x > 0)
1800 break;
1801 }
1802
1803 if(pos.x <= 0)
1804 {
1805 for(list<AAISector*>::iterator sector = ai->Getbrain()->sectors[0].begin(); sector != ai->Getbrain()->sectors[0].end(); ++sector)
1806 {
1807 pos = (*sector)->GetBuildsite(building, false);
1808
1809 if(pos.x > 0)
1810 break;
1811 }
1812 }
1813 }
1814
1815 // try to build on water if land has not been possible
1816 if(pos.x <= 0 && ai->Getbt()->CanPlacedWater(building))
1817 {
1818 water = true;
1819
1820 ai->Getbrain()->sectors[0].sort(suitable_for_sea_factory);
1821
1822 // find buildpos
1823 for(list<AAISector*>::iterator sector = ai->Getbrain()->sectors[0].begin(); sector != ai->Getbrain()->sectors[0].end(); ++sector)
1824 {
1825 if((*sector)->ConnectedToOcean())
1826 {
1827 pos = (*sector)->GetRandomBuildsite(building, 20, true);
1828
1829 if(pos.x > 0)
1830 break;
1831 }
1832 }
1833
1834 if(pos.x <= 0)
1835 {
1836 for(list<AAISector*>::iterator sector = ai->Getbrain()->sectors[0].begin(); sector != ai->Getbrain()->sectors[0].end(); ++sector)
1837 {
1838 if((*sector)->ConnectedToOcean())
1839 {
1840 pos = (*sector)->GetBuildsite(building, true);
1841
1842 if(pos.x > 0)
1843 break;
1844 }
1845 }
1846 }
1847 }
1848
1849 if(pos.x > 0)
1850 {
1851 float min_dist;
1852
1853 builder = ai->Getut()->FindClosestBuilder(building, &pos, true, &min_dist);
1854
1855 if(builder)
1856 {
1857 ai->Getbt()->units_dynamic[building].requested -= 1;
1858
1859 // give build order
1860 builder->GiveConstructionOrder(building, pos, water);
1861
1862 // add average ressource usage
1863 futureRequestedMetal += ai->Getbt()->units_static[building].efficiency[0];
1864 futureRequestedEnergy += ai->Getbt()->units_static[building].efficiency[1];
1865
1866 return true;
1867 }
1868 else
1869 {
1870 if(ai->Getbt()->units_dynamic[building].constructorsRequested + ai->Getbt()->units_dynamic[building].constructorsAvailable <= 0)
1871 ai->Getbt()->BuildBuilderFor(building);
1872
1873 return false;
1874 }
1875 }
1876 else
1877 {
1878 bool expanded = false;
1879
1880 // no suitable buildsite found
1881 if(ai->Getbt()->CanPlacedLand(building))
1882 {
1883 expanded = ai->Getbrain()->ExpandBase(LAND_SECTOR);
1884 ai->Log("Base expanded by BuildFactory()\n");
1885 }
1886
1887 if(!expanded && ai->Getbt()->CanPlacedWater(building))
1888 {
1889 ai->Getbrain()->ExpandBase(WATER_SECTOR);
1890 ai->Log("Base expanded by BuildFactory() (water sector)\n");
1891 }
1892
1893 // could not build due to lack of suitable buildpos
1894 ++ai->Getbt()->units_static[building].efficiency[4];
1895
1896 if(ai->Getbt()->units_static[building].efficiency[4] > 1)
1897 return true;
1898 else
1899 return false;
1900 }
1901 }
1902 else
1903 {
1904 // keep factory at highest urgency if the construction failed due to (temporarily) unavailable builder
1905 if(factory_types_requested > 0)
1906 return false;
1907 }
1908
1909 return true;
1910 }
1911
1912 /*
1913 void AAIExecute::BuildUnit(UnitCategory category, float speed, float cost, float range, float power, float ground_eff, float air_eff, float hover_eff, float sea_eff, float submarine_eff, float stat_eff, float eff, bool urgent)
1914 {
1915
1916 }
1917 */
1918
BuildRadar()1919 bool AAIExecute::BuildRadar()
1920 {
1921 if(ai->Getut()->activeUnits[STATIONARY_RECON] + ai->Getut()->futureUnits[STATIONARY_RECON] + ai->Getut()->requestedUnits[STATIONARY_RECON] > ai->Getbrain()->sectors[0].size())
1922 return true;
1923
1924 int ground_radar = 0;
1925 int sea_radar = 0;
1926 float3 my_pos, best_pos;
1927 float my_rating, best_rating = -1000000;
1928 int radar = 0;
1929
1930 float cost = ai->Getbrain()->Affordable();
1931 float range = 10.0 / (cost + 1);
1932
1933 // get ground radar
1934 if(ai->Getmap()->land_ratio > 0.02f)
1935 {
1936 ground_radar = ai->Getbt()->GetRadar(ai->Getside(), cost, range, false, false);
1937
1938 if(ground_radar && ai->Getbt()->units_dynamic[ground_radar].constructorsAvailable <= 0)
1939 {
1940 if(ai->Getbt()->units_dynamic[ground_radar].constructorsRequested <= 0)
1941 ai->Getbt()->BuildBuilderFor(ground_radar);
1942
1943 ground_radar = ai->Getbt()->GetRadar(ai->Getside(), cost, range, false, true);
1944 }
1945 }
1946
1947 // get sea radar
1948 if(ai->Getmap()->water_ratio > 0.02f)
1949 {
1950 sea_radar = ai->Getbt()->GetRadar(ai->Getside(), cost, range, false, false);
1951
1952 if(sea_radar && ai->Getbt()->units_dynamic[sea_radar].constructorsAvailable <= 0)
1953 {
1954 if(ai->Getbt()->units_dynamic[sea_radar].constructorsRequested <= 0)
1955 ai->Getbt()->BuildBuilderFor(sea_radar);
1956
1957 sea_radar = ai->Getbt()->GetRadar(ai->Getside(), cost, range, false, true);
1958 }
1959 }
1960
1961 for(int dist = 0; dist < 2; ++dist)
1962 {
1963 for(list<AAISector*>::iterator sector = ai->Getbrain()->sectors[dist].begin(); sector != ai->Getbrain()->sectors[dist].end(); ++sector)
1964 {
1965 if((*sector)->my_buildings[STATIONARY_RECON] <= 0)
1966 {
1967 my_pos = ZeroVector;
1968
1969 if(ground_radar && (*sector)->water_ratio < 0.9f)
1970 my_pos = (*sector)->GetRadarArtyBuildsite(ground_radar, ai->Getbt()->units_static[ground_radar].range, false);
1971
1972 if(my_pos.x <= 0 && sea_radar && (*sector)->water_ratio > 0.1f)
1973 {
1974 my_pos = (*sector)->GetRadarArtyBuildsite(sea_radar, ai->Getbt()->units_static[sea_radar].range, true);
1975
1976 if(my_pos.x > 0)
1977 ground_radar = sea_radar;
1978 }
1979
1980 if(my_pos.x > 0)
1981 {
1982 my_rating = - ai->Getmap()->GetEdgeDistance(&my_pos);
1983
1984 if(my_rating > best_rating)
1985 {
1986 radar = ground_radar;
1987 best_pos = my_pos;
1988 best_rating = my_rating;
1989 }
1990 }
1991 }
1992 }
1993 }
1994
1995 if(radar)
1996 {
1997 float min_dist;
1998 AAIConstructor *builder = ai->Getut()->FindClosestBuilder(radar, &best_pos, true, &min_dist);
1999
2000 if(builder)
2001 {
2002 builder->GiveConstructionOrder(radar, best_pos, false);
2003 return true;
2004 }
2005 else
2006 {
2007 ai->Getbt()->BuildBuilderFor(radar);
2008 return false;
2009 }
2010 }
2011
2012 return true;
2013 }
2014
BuildJammer()2015 bool AAIExecute::BuildJammer()
2016 {
2017 if(ai->Getut()->futureUnits[STATIONARY_JAMMER] + ai->Getut()->requestedUnits[STATIONARY_JAMMER] > 0)
2018 return true;
2019
2020 float3 pos = ZeroVector;
2021
2022 float cost = ai->Getbrain()->Affordable();
2023 float range = 10.0 / (cost + 1);
2024
2025 int ground_jammer = 0;
2026 int sea_jammer = 0;
2027
2028 // get ground jammer
2029 if(ai->Getmap()->land_ratio > 0.02f)
2030 {
2031 ground_jammer = ai->Getbt()->GetJammer(ai->Getside(), cost, range, false, false);
2032
2033 if(ground_jammer && ai->Getbt()->units_dynamic[ground_jammer].constructorsAvailable <= 0)
2034 {
2035 if(ai->Getbt()->units_dynamic[ground_jammer].constructorsRequested <= 0)
2036 ai->Getbt()->BuildBuilderFor(ground_jammer);
2037
2038 ground_jammer = ai->Getbt()->GetJammer(ai->Getside(), cost, range, false, true);
2039 }
2040 }
2041
2042 // get sea jammer
2043 if(ai->Getmap()->water_ratio > 0.02f)
2044 {
2045 sea_jammer = ai->Getbt()->GetJammer(ai->Getside(), cost, range, false, false);
2046
2047 if(sea_jammer && ai->Getbt()->units_dynamic[sea_jammer].constructorsAvailable <= 0)
2048 {
2049 if(ai->Getbt()->units_dynamic[sea_jammer].constructorsRequested <= 0)
2050 ai->Getbt()->BuildBuilderFor(sea_jammer);
2051
2052 sea_jammer = ai->Getbt()->GetJammer(ai->Getside(), cost, range, false, true);
2053 }
2054 }
2055
2056 for(list<AAISector*>::iterator sector = ai->Getbrain()->sectors[0].begin(); sector != ai->Getbrain()->sectors[0].end(); ++sector)
2057 {
2058 if((*sector)->my_buildings[STATIONARY_JAMMER] <= 0)
2059 {
2060 if(ground_jammer && (*sector)->water_ratio < 0.9f)
2061 pos = (*sector)->GetCenterBuildsite(ground_jammer, false);
2062
2063 if(pos.x == 0 && sea_jammer && (*sector)->water_ratio > 0.1f)
2064 {
2065 pos = (*sector)->GetCenterBuildsite(sea_jammer, true);
2066
2067 if(pos.x > 0)
2068 ground_jammer = sea_jammer;
2069 }
2070
2071 if(pos.x > 0)
2072 {
2073 float min_dist;
2074 AAIConstructor *builder = ai->Getut()->FindClosestBuilder(ground_jammer, &pos, true, &min_dist);
2075
2076 if(builder)
2077 {
2078 builder->GiveConstructionOrder(ground_jammer, pos, false);
2079 return true;
2080 }
2081 else
2082 {
2083 ai->Getbt()->BuildBuilderFor(ground_jammer);
2084 return false;
2085 }
2086 }
2087 }
2088 }
2089
2090 return true;
2091 }
2092
DefendMex(int mex,int def_id)2093 void AAIExecute::DefendMex(int mex, int def_id)
2094 {
2095 if(ai->Getut()->activeFactories < cfg->MIN_FACTORIES_FOR_DEFENCES)
2096 return;
2097
2098 float3 pos = ai->Getcb()->GetUnitPos(mex);
2099 float3 base_pos = ai->Getbrain()->base_center;
2100
2101 // check if mex is located in a small pond/on a little island
2102 if(ai->Getmap()->LocatedOnSmallContinent(&pos))
2103 return;
2104
2105 int x = pos.x/ai->Getmap()->xSectorSize;
2106 int y = pos.z/ai->Getmap()->ySectorSize;
2107
2108 if(x >= 0 && y >= 0 && x < ai->Getmap()->xSectors && y < ai->Getmap()->ySectors)
2109 {
2110 AAISector *sector = &ai->Getmap()->sector[x][y];
2111
2112 if(sector->distance_to_base > 0 && sector->distance_to_base <= cfg->MAX_MEX_DEFENCE_DISTANCE && sector->my_buildings[STATIONARY_DEF] < 1)
2113 {
2114 int defence = 0;
2115 bool water;
2116
2117 // get defence building dependend on water or land mex
2118 if(ai->Getbt()->GetUnitDef(def_id).minWaterDepth > 0)
2119 {
2120 water = true;
2121
2122 if(cfg->AIR_ONLY_MOD)
2123 defence = ai->Getbt()->GetCheapDefenceBuilding(ai->Getside(), 1, 2, 1, 1, 1, 0.5, 0, 0, 0, true);
2124 else
2125 defence = ai->Getbt()->GetCheapDefenceBuilding(ai->Getside(), 1, 2, 1, 1, 0, 0, 0.5, 1.5, 0.5, true);
2126 }
2127 else
2128 {
2129 if(cfg->AIR_ONLY_MOD)
2130 defence = ai->Getbt()->GetCheapDefenceBuilding(ai->Getside(), 1, 2, 1, 1, 1, 0.5, 0, 0, 0, false);
2131 else
2132 defence = ai->Getbt()->GetCheapDefenceBuilding(ai->Getside(), 1, 2, 1, 1, 1.5, 0, 0.5, 0, 0, false);
2133
2134 water = false;
2135 }
2136
2137 // find closest builder
2138 if(defence)
2139 {
2140 // place defences according to the direction of the main base
2141 if(pos.x > base_pos.x + 500)
2142 pos.x += 120;
2143 else if(pos.x > base_pos.x + 300)
2144 pos.x += 70;
2145 else if(pos.x < base_pos.x - 500)
2146 pos.x -= 120;
2147 else if(pos.x < base_pos.x - 300)
2148 pos.x -= 70;
2149
2150 if(pos.z > base_pos.z + 500)
2151 pos.z += 70;
2152 else if(pos.z > base_pos.z + 300)
2153 pos.z += 120;
2154 else if(pos.z < base_pos.z - 500)
2155 pos.z -= 120;
2156 else if(pos.z < base_pos.z - 300)
2157 pos.z -= 70;
2158
2159 // get suitable pos
2160 pos = ai->Getcb()->ClosestBuildSite(&ai->Getbt()->GetUnitDef(defence), pos, 1400.0, 2);
2161
2162 if(pos.x > 0)
2163 {
2164 AAIConstructor *builder;
2165 float min_dist;
2166
2167 if(ai->Getbrain()->sectors[0].size() > 2)
2168 builder = ai->Getut()->FindClosestBuilder(defence, &pos, false, &min_dist);
2169 else
2170 builder = ai->Getut()->FindClosestBuilder(defence, &pos, true, &min_dist);
2171
2172 if(builder)
2173 builder->GiveConstructionOrder(defence, pos, water);
2174 }
2175 }
2176 }
2177 }
2178 }
2179
UpdateRessources()2180 void AAIExecute::UpdateRessources()
2181 {
2182 // get current metal/energy surplus
2183 metalSurplus[counter] = ai->Getcb()->GetMetalIncome() - ai->Getcb()->GetMetalUsage();
2184 if(metalSurplus[counter] < 0) metalSurplus[counter] = 0;
2185
2186 energySurplus[counter] = ai->Getcb()->GetEnergyIncome() - ai->Getcb()->GetEnergyUsage();
2187 if(energySurplus[counter] < 0) energySurplus[counter] = 0;
2188
2189 // calculate average value
2190 averageMetalSurplus = 0;
2191 averageEnergySurplus = 0;
2192
2193 for(int i = 0; i < 8; i++)
2194 {
2195 averageMetalSurplus += metalSurplus[i];
2196 averageEnergySurplus += energySurplus[i];
2197 }
2198
2199 averageEnergySurplus /= 8.0f;
2200 averageMetalSurplus /= 8.0f;
2201
2202 // increase counter
2203 counter = (counter + 1) % 8;
2204 }
2205
CheckStationaryArty()2206 void AAIExecute::CheckStationaryArty()
2207 {
2208 if(cfg->MAX_STAT_ARTY == 0)
2209 return;
2210
2211 if(ai->Getut()->futureUnits[STATIONARY_ARTY] + ai->Getut()->requestedUnits[STATIONARY_ARTY]> 0)
2212 return;
2213
2214 if(ai->Getut()->activeUnits[STATIONARY_ARTY] >= cfg->MAX_STAT_ARTY)
2215 return;
2216
2217 float temp = 0.05f;
2218
2219 if(temp > urgency[STATIONARY_ARTY])
2220 urgency[STATIONARY_ARTY] = temp;
2221 }
2222
CheckBuildqueues()2223 void AAIExecute::CheckBuildqueues()
2224 {
2225 int req_units = 0;
2226 int active_factory_types = 0;
2227
2228 for(int i = 0; i < numOfFactories; ++i)
2229 {
2230 // sum up builque lengths of active factory types
2231 if(ai->Getbt()->units_dynamic[factory_table[i]].active > 0)
2232 {
2233 req_units += (int) buildques[i].size();
2234 ++active_factory_types;
2235 }
2236 }
2237
2238 if(active_factory_types > 0)
2239 {
2240 if( (float)req_units / (float)active_factory_types < (float)cfg->MAX_BUILDQUE_SIZE / 2.5f )
2241 {
2242 if(unitProductionRate < 70)
2243 ++unitProductionRate;
2244
2245 //ai->Log("Increasing unit production rate to %i\n", unitProductionRate);
2246 }
2247 else if( (float)req_units / (float)active_factory_types > (float)cfg->MAX_BUILDQUE_SIZE / 1.5f )
2248 {
2249 if(unitProductionRate > 1)
2250 {
2251 --unitProductionRate;
2252 //ai->Log("Decreasing unit production rate to %i\n", unitProductionRate);
2253 }
2254 }
2255 }
2256 }
2257
CheckDefences()2258 void AAIExecute::CheckDefences()
2259 {
2260 if(ai->Getut()->activeFactories < cfg->MIN_FACTORIES_FOR_DEFENCES || ai->Getut()->futureUnits[STATIONARY_DEF] + ai->Getut()->requestedUnits[STATIONARY_DEF] > 2)
2261 return;
2262
2263 int game_period = ai->Getbrain()->GetGamePeriod();
2264
2265 int max_dist = 2;
2266
2267 float rating, highest_rating = 0;
2268
2269 AAISector *first = 0, *second = 0;
2270 UnitCategory cat1 = UNKNOWN, cat2 = UNKNOWN;
2271
2272 for(int dist = 0; dist <= max_dist; ++dist)
2273 {
2274 for(list<AAISector*>::iterator sector = ai->Getbrain()->sectors[dist].begin(); sector != ai->Getbrain()->sectors[dist].end(); ++sector)
2275 {
2276 // stop building further defences if maximum has been reached / sector contains allied buildings / is occupied by another aai instance
2277 if((*sector)->my_buildings[STATIONARY_DEF] < cfg->MAX_DEFENCES && (*sector)->allied_structures < 4 && ai->Getmap()->team_sector_map[(*sector)->x][(*sector)->y] != ai->Getcb()->GetMyAllyTeam())
2278 {
2279 if((*sector)->failed_defences > 1)
2280 (*sector)->failed_defences = 0;
2281 else
2282 {
2283 for(list<int>::iterator cat = ai->Getmap()->map_categories_id.begin(); cat!= ai->Getmap()->map_categories_id.end(); ++cat)
2284 {
2285 // anti air defences may be built anywhere
2286 if(cfg->AIR_ONLY_MOD || *cat == AIR_ASSAULT)
2287 {
2288 //rating = (*sector)->own_structures * (0.25 + ai->Getbrain()->GetAttacksBy(*cat, game_period)) * (0.25 + (*sector)->GetThreatByID(*cat, learned, current)) / ( 0.1 + (*sector)->GetMyDefencePowerAgainstAssaultCategory(*cat));
2289 // how often did units of category attack that sector compared to current def power
2290 rating = (1.0f + (*sector)->GetThreatByID(*cat, learned, current)) / ( 1.0f + (*sector)->GetMyDefencePowerAgainstAssaultCategory(*cat));
2291
2292 // how often did unist of that category attack anywere in the current period of the game
2293 rating *= (0.1f + ai->Getbrain()->GetAttacksBy(*cat, game_period));
2294 }
2295 //else if(!(*sector)->interior)
2296 else if((*sector)->distance_to_base > 0) // dont build anti ground/hover/sea defences in interior sectors
2297 {
2298 // how often did units of category attack that sector compared to current def power
2299 rating = (1.0f + (*sector)->GetThreatByID(*cat, learned, current)) / ( 1.0f + (*sector)->GetMyDefencePowerAgainstAssaultCategory(*cat));
2300
2301 // how often did units of that category attack anywere in the current period of the game
2302 rating *= (0.1f + ai->Getbrain()->GetAttacksBy(*cat, game_period));
2303 }
2304 else
2305 rating = 0;
2306
2307 if(rating > highest_rating)
2308 {
2309 // dont block empty sectors with too much aa
2310 if(ai->Getbt()->GetAssaultCategoryOfID(*cat) != AIR_ASSAULT || ((*sector)->my_buildings[POWER_PLANT] > 0 || (*sector)->my_buildings[STATIONARY_CONSTRUCTOR] > 0 ) )
2311 {
2312 second = first;
2313 cat2 = cat1;
2314
2315 first = *sector;
2316 cat1 = ai->Getbt()->GetAssaultCategoryOfID(*cat);
2317
2318 highest_rating = rating;
2319 }
2320 }
2321 }
2322 }
2323 }
2324 }
2325 }
2326
2327 if(first)
2328 {
2329 // if no builder available retry later
2330 BuildOrderStatus status = BuildStationaryDefenceVS(cat1, first);
2331
2332 if(status == BUILDORDER_NOBUILDER)
2333 {
2334 float temp = 0.03f + 1.0f / ( (float) first->my_buildings[STATIONARY_DEF] + 0.5f);
2335
2336 if(urgency[STATIONARY_DEF] < temp)
2337 urgency[STATIONARY_DEF] = temp;
2338
2339 next_defence = first;
2340 def_category = cat1;
2341 }
2342 else if(status == BUILDORDER_NOBUILDPOS)
2343 ++first->failed_defences;
2344 }
2345
2346 if(second)
2347 BuildStationaryDefenceVS(cat2, second);
2348 }
2349
CheckRessources()2350 void AAIExecute::CheckRessources()
2351 {
2352 // prevent float rounding errors
2353 if(futureAvailableEnergy < 0)
2354 futureAvailableEnergy = 0;
2355
2356 // determine how much metal/energy is needed based on net surplus
2357 float temp = GetMetalUrgency();
2358
2359 if(urgency[EXTRACTOR] < temp) // && urgency[EXTRACTOR] > 0.05)
2360 urgency[EXTRACTOR] = temp;
2361
2362 temp = GetEnergyUrgency();
2363 if(urgency[POWER_PLANT] < temp) // && urgency[POWER_PLANT] > 0.05)
2364 urgency[POWER_PLANT] = temp;
2365
2366 // build storages if needed
2367 if(ai->Getut()->activeUnits[STORAGE] + ai->Getut()->requestedUnits[STORAGE] + ai->Getut()->futureUnits[STORAGE] < cfg->MAX_STORAGE
2368 && ai->Getut()->activeFactories >= cfg->MIN_FACTORIES_FOR_STORAGE)
2369 {
2370 float temp = max(GetMetalStorageUrgency(), GetEnergyStorageUrgency());
2371
2372 if(temp > urgency[STORAGE])
2373 urgency[STORAGE] = temp;
2374 }
2375
2376 // energy low
2377 if(averageEnergySurplus < 1.5 * cfg->METAL_ENERGY_RATIO)
2378 {
2379 // try to accelerate power plant construction
2380 if(ai->Getut()->futureUnits[POWER_PLANT] + ai->Getut()->requestedUnits[POWER_PLANT]> 0)
2381 AssistConstructionOfCategory(POWER_PLANT, 10);
2382
2383 // try to disbale some metal makers
2384 if((ai->Getut()->activeUnits[METAL_MAKER] - disabledMMakers) > 0)
2385 {
2386 for(set<int>::iterator maker = ai->Getut()->metal_makers.begin(); maker != ai->Getut()->metal_makers.end(); ++maker)
2387 {
2388 if(ai->Getcb()->IsUnitActivated(*maker))
2389 {
2390 Command c;
2391 c.id = CMD_ONOFF;
2392 c.params.push_back(0);
2393 //ai->Getcb()->GiveOrder(*maker, &c);
2394 GiveOrder(&c, *maker, "ToggleMMaker");
2395
2396 futureRequestedEnergy += ai->Getcb()->GetUnitDef(*maker)->energyUpkeep;
2397 ++disabledMMakers;
2398 break;
2399 }
2400 }
2401 }
2402 }
2403 // try to enable some metal makers
2404 else if(averageEnergySurplus > cfg->MIN_METAL_MAKER_ENERGY && disabledMMakers > 0)
2405 {
2406 for(set<int>::iterator maker = ai->Getut()->metal_makers.begin(); maker != ai->Getut()->metal_makers.end(); ++maker)
2407 {
2408 if(!ai->Getcb()->IsUnitActivated(*maker))
2409 {
2410 float usage = ai->Getcb()->GetUnitDef(*maker)->energyUpkeep;
2411
2412 if(averageEnergySurplus > usage * 0.7f)
2413 {
2414 Command c;
2415 c.id = CMD_ONOFF;
2416 c.params.push_back(1);
2417 //ai->Getcb()->GiveOrder(*maker, &c);
2418 GiveOrder(&c, *maker, "ToggleMMaker");
2419
2420 futureRequestedEnergy -= usage;
2421 --disabledMMakers;
2422 break;
2423 }
2424 }
2425 }
2426 }
2427
2428 // metal low
2429 if(averageMetalSurplus < 15.0/cfg->METAL_ENERGY_RATIO)
2430 {
2431 // try to accelerate mex construction
2432 if(ai->Getut()->futureUnits[EXTRACTOR] > 0)
2433 AssistConstructionOfCategory(EXTRACTOR, 10);
2434
2435 // try to accelerate mex construction
2436 if(ai->Getut()->futureUnits[METAL_MAKER] > 0 && averageEnergySurplus > cfg->MIN_METAL_MAKER_ENERGY)
2437 AssistConstructionOfCategory(METAL_MAKER, 10);
2438 }
2439 }
2440
CheckMexUpgrade()2441 void AAIExecute::CheckMexUpgrade()
2442 {
2443 if(ai->Getbrain()->freeBaseSpots)
2444 return;
2445
2446 float cost = 0.25f + ai->Getbrain()->Affordable() / 8.0f;
2447 float eff = 6.0f / (cost + 0.75f);
2448
2449 const UnitDef *my_def;
2450 const UnitDef *land_def = 0;
2451 const UnitDef *water_def = 0;
2452
2453 float gain, highest_gain = 0;
2454 AAIMetalSpot *best_spot = 0;
2455
2456 int my_team = ai->Getcb()->GetMyTeam();
2457
2458 int land_mex = ai->Getbt()->GetMex(ai->Getside(), cost, eff, false, false, false);
2459
2460 if(land_mex && ai->Getbt()->units_dynamic[land_mex].constructorsAvailable + ai->Getbt()->units_dynamic[land_mex].constructorsRequested <= 0)
2461 {
2462 ai->Getbt()->BuildBuilderFor(land_mex);
2463
2464 land_mex = ai->Getbt()->GetMex(ai->Getside(), cost, eff, false, false, true);
2465 }
2466
2467 int water_mex = ai->Getbt()->GetMex(ai->Getside(), cost, eff, false, true, false);
2468
2469 if(water_mex && ai->Getbt()->units_dynamic[water_mex].constructorsAvailable + ai->Getbt()->units_dynamic[water_mex].constructorsRequested <= 0)
2470 {
2471 ai->Getbt()->BuildBuilderFor(water_mex);
2472
2473 water_mex = ai->Getbt()->GetMex(ai->Getside(), cost, eff, false, true, true);
2474 }
2475
2476 if(land_mex)
2477 land_def = &ai->Getbt()->GetUnitDef(land_mex);
2478
2479 if(water_mex)
2480 water_def = &ai->Getbt()->GetUnitDef(water_mex);
2481
2482 // check extractor upgrades
2483 for(int dist = 0; dist < 2; ++dist)
2484 {
2485 for(list<AAISector*>::iterator sector = ai->Getbrain()->sectors[dist].begin(); sector != ai->Getbrain()->sectors[dist].end(); ++sector)
2486 {
2487 for(list<AAIMetalSpot*>::iterator spot = (*sector)->metalSpots.begin(); spot != (*sector)->metalSpots.end(); ++spot)
2488 {
2489 // quit when finding empty spots
2490 if(!(*spot)->occupied && (*sector)->enemy_structures <= 0 && (*sector)->lost_units[MOBILE_CONSTRUCTOR-COMMANDER] < 0.2)
2491 return;
2492
2493 if((*spot)->extractor_def > 0 && (*spot)->extractor > -1 && (*spot)->extractor < cfg->MAX_UNITS
2494 && ai->Getcb()->GetUnitTeam((*spot)->extractor) == my_team) // only upgrade own extractors
2495 {
2496 my_def = &ai->Getbt()->GetUnitDef((*spot)->extractor_def);
2497
2498 if(my_def->minWaterDepth <= 0 && land_def) // land mex
2499 {
2500 gain = land_def->extractsMetal - my_def->extractsMetal;
2501
2502 if(gain > 0.0001f && gain > highest_gain)
2503 {
2504 highest_gain = gain;
2505 best_spot = *spot;
2506 }
2507 }
2508 else // water mex
2509 {
2510 gain = water_def->extractsMetal - my_def->extractsMetal;
2511
2512 if(gain > 0.0001f && gain > highest_gain)
2513 {
2514 highest_gain = gain;
2515 best_spot = *spot;
2516 }
2517 }
2518 }
2519 }
2520 }
2521 }
2522
2523 if(best_spot)
2524 {
2525 AAIConstructor *builder = ai->Getut()->FindClosestAssistant(best_spot->pos, 10, true);
2526
2527 if(builder)
2528 builder->GiveReclaimOrder(best_spot->extractor);
2529 }
2530 }
2531
2532
CheckRadarUpgrade()2533 void AAIExecute::CheckRadarUpgrade()
2534 {
2535 if(ai->Getut()->futureUnits[STATIONARY_RECON] + ai->Getut()->requestedUnits[STATIONARY_RECON] > 0)
2536 return;
2537
2538 float cost = ai->Getbrain()->Affordable();
2539 float range = 10.0f / (cost + 1.0f);
2540
2541 const UnitDef *my_def;
2542 const UnitDef *land_def = 0;
2543 const UnitDef *water_def = 0;
2544
2545 int land_radar = ai->Getbt()->GetRadar(ai->Getside(), cost, range, false, true);
2546 int water_radar = ai->Getbt()->GetRadar(ai->Getside(), cost, range, true, true);
2547
2548 if(land_radar)
2549 land_def = &ai->Getbt()->GetUnitDef(land_radar);
2550
2551 if(water_radar)
2552 water_def = &ai->Getbt()->GetUnitDef(water_radar);
2553
2554 // check radar upgrades
2555 for(set<int>::iterator recon = ai->Getut()->recon.begin(); recon != ai->Getut()->recon.end(); ++recon)
2556 {
2557 my_def = ai->Getcb()->GetUnitDef(*recon);
2558
2559 if(my_def)
2560 {
2561 if(my_def->minWaterDepth <= 0) // land recon
2562 {
2563 if(land_def && my_def->radarRadius < land_def->radarRadius)
2564 {
2565 // better radar found, clear buildpos
2566 AAIConstructor *builder = ai->Getut()->FindClosestAssistant(ai->Getcb()->GetUnitPos(*recon), 10, true);
2567
2568 if(builder)
2569 {
2570 builder->GiveReclaimOrder(*recon);
2571 return;
2572 }
2573 }
2574 }
2575 else // water radar
2576 {
2577 if(water_def && my_def->radarRadius < water_def->radarRadius)
2578 {
2579 // better radar found, clear buildpos
2580 AAIConstructor *builder = ai->Getut()->FindClosestAssistant(ai->Getcb()->GetUnitPos(*recon), 10, true );
2581
2582 if(builder)
2583 {
2584 builder->GiveReclaimOrder(*recon);
2585 return;
2586 }
2587 }
2588 }
2589 }
2590 }
2591 }
2592
CheckJammerUpgrade()2593 void AAIExecute::CheckJammerUpgrade()
2594 {
2595 if(ai->Getut()->futureUnits[STATIONARY_JAMMER] + ai->Getut()->requestedUnits[STATIONARY_JAMMER] > 0)
2596 return;
2597
2598 float cost = ai->Getbrain()->Affordable();
2599 float range = 10.0 / (cost + 1);
2600
2601 const UnitDef *my_def;
2602 const UnitDef *land_def = 0;
2603 const UnitDef *water_def = 0;
2604
2605 int land_jammer = ai->Getbt()->GetJammer(ai->Getside(), cost, range, false, true);
2606 int water_jammer = ai->Getbt()->GetJammer(ai->Getside(), cost, range, true, true);
2607
2608 if(land_jammer)
2609 land_def = &ai->Getbt()->GetUnitDef(land_jammer);
2610
2611 if(water_jammer)
2612 water_def = &ai->Getbt()->GetUnitDef(water_jammer);
2613
2614 // check radar upgrades
2615 for(set<int>::iterator jammer = ai->Getut()->jammers.begin(); jammer != ai->Getut()->jammers.end(); ++jammer)
2616 {
2617 my_def = ai->Getcb()->GetUnitDef(*jammer);
2618
2619 if(my_def)
2620 {
2621 if(my_def->minWaterDepth <= 0) // land jammer
2622 {
2623 if(land_def && my_def->jammerRadius < land_def->jammerRadius)
2624 {
2625 // better jammer found, clear buildpos
2626 AAIConstructor *builder = ai->Getut()->FindClosestAssistant(ai->Getcb()->GetUnitPos(*jammer), 10, true);
2627
2628 if(builder)
2629 {
2630 builder->GiveReclaimOrder(*jammer);
2631 return;
2632 }
2633 }
2634 }
2635 else // water jammer
2636 {
2637 if(water_def && my_def->jammerRadius < water_def->jammerRadius)
2638 {
2639 // better radar found, clear buildpos
2640 AAIConstructor *builder = ai->Getut()->FindClosestAssistant(ai->Getcb()->GetUnitPos(*jammer), 10, true);
2641
2642 if(builder)
2643 {
2644 builder->GiveReclaimOrder(*jammer);
2645 return;
2646 }
2647 }
2648 }
2649 }
2650 }
2651 }
2652
GetEnergyUrgency()2653 float AAIExecute::GetEnergyUrgency()
2654 {
2655 float surplus = averageEnergySurplus + futureAvailableEnergy * 0.5f;
2656
2657 if(surplus < 0)
2658 surplus = 0;
2659
2660 if(ai->Getut()->activeUnits[POWER_PLANT] > 8)
2661 {
2662 if(averageEnergySurplus > 1000)
2663 return 0;
2664 else
2665 return 8.0f / pow( surplus / cfg->METAL_ENERGY_RATIO + 2.0f, 2.0f);
2666 }
2667 else if(ai->Getut()->activeUnits[POWER_PLANT] > 0)
2668 return 15.0f / pow( surplus / cfg->METAL_ENERGY_RATIO + 2.0f, 2.0f);
2669 else
2670 return 6.0f;
2671 }
2672
GetMetalUrgency()2673 float AAIExecute::GetMetalUrgency()
2674 {
2675 if(ai->Getut()->activeUnits[EXTRACTOR] > 0)
2676 return 20.0f / pow(averageMetalSurplus * cfg->METAL_ENERGY_RATIO + 2.0f, 2.0f);
2677 else
2678 return 7.0f;
2679 }
2680
GetEnergyStorageUrgency()2681 float AAIExecute::GetEnergyStorageUrgency()
2682 {
2683 if(averageEnergySurplus / cfg->METAL_ENERGY_RATIO > 4.0f)
2684 return 0.2f;
2685 else
2686 return 0;
2687 }
2688
GetMetalStorageUrgency()2689 float AAIExecute::GetMetalStorageUrgency()
2690 {
2691 if(averageMetalSurplus > 2.0f && (ai->Getcb()->GetMetalStorage() + futureStoredMetal - ai->Getcb()->GetMetal()) < 100.0f)
2692 return 0.3f;
2693 else
2694 return 0;
2695 }
2696
CheckFactories()2697 void AAIExecute::CheckFactories()
2698 {
2699 if(ai->Getut()->futureUnits[STATIONARY_CONSTRUCTOR] + ai->Getut()->requestedUnits[STATIONARY_CONSTRUCTOR] > 0)
2700 return;
2701
2702 for(list<int>::iterator fac = ai->Getbt()->units_of_category[STATIONARY_CONSTRUCTOR][ai->Getside()-1].begin(); fac != ai->Getbt()->units_of_category[STATIONARY_CONSTRUCTOR][ai->Getside()-1].end(); ++fac)
2703 {
2704 if(ai->Getbt()->units_dynamic[*fac].requested > 0)
2705 {
2706 // at least one requested factory has not been built yet
2707 float urgency;
2708
2709 if(ai->Getut()->activeFactories > 0)
2710 urgency = 0.4f;
2711 else
2712 urgency = 3.5f;
2713
2714 if(this->urgency[STATIONARY_CONSTRUCTOR] < urgency)
2715 this->urgency[STATIONARY_CONSTRUCTOR] = urgency;
2716
2717 return;
2718 }
2719 }
2720 }
2721
CheckRecon()2722 void AAIExecute::CheckRecon()
2723 {
2724 float urgency = 0.02f + 0.5f / ((float)(2 * ai->Getut()->activeUnits[STATIONARY_RECON] + 1));
2725
2726 if(this->urgency[STATIONARY_RECON] < urgency)
2727 this->urgency[STATIONARY_RECON] = urgency;
2728 }
2729
CheckAirBase()2730 void AAIExecute::CheckAirBase()
2731 {
2732 // only build repair pad if any air units have been built yet
2733 if(ai->Getut()->activeUnits[AIR_BASE] + ai->Getut()->requestedUnits[AIR_BASE] + ai->Getut()->futureUnits[AIR_BASE] < cfg->MAX_AIR_BASE && ai->Getgroup_list()[AIR_ASSAULT].size() > 0)
2734 urgency[AIR_BASE] = 0.5f;
2735 }
2736
CheckJammer()2737 void AAIExecute::CheckJammer()
2738 {
2739 if(ai->Getut()->activeFactories < 2 || ai->Getut()->activeUnits[STATIONARY_JAMMER] > ai->Getbrain()->sectors[0].size())
2740 {
2741 this->urgency[STATIONARY_JAMMER] = 0;
2742 }
2743 else
2744 {
2745 float temp = 0.2f / ((float) (ai->Getut()->activeUnits[STATIONARY_JAMMER]+1)) + 0.05f;
2746
2747 if(urgency[STATIONARY_JAMMER] < temp)
2748 urgency[STATIONARY_JAMMER] = temp;
2749 }
2750 }
2751
CheckConstruction()2752 void AAIExecute::CheckConstruction()
2753 {
2754 UnitCategory category = UNKNOWN;
2755 float highest_urgency = 0.5f; // min urgency (prevents aai from building things it doesnt really need that much)
2756 bool construction_started = false;
2757
2758 // get category with highest urgency
2759 if(ai->Getbrain()->enemy_pressure_estimation > 0.01f)
2760 {
2761 double current_urgency;
2762
2763 for(int i = 1; i <= METAL_MAKER; ++i)
2764 {
2765 current_urgency = urgency[i];
2766
2767 if(i != STATIONARY_DEF && i != POWER_PLANT && i != EXTRACTOR && i != STATIONARY_CONSTRUCTOR)
2768 current_urgency *= (1.1f - ai->Getbrain()->enemy_pressure_estimation);
2769
2770 if(urgency[i] > highest_urgency)
2771 {
2772 highest_urgency = urgency[i];
2773 category = (UnitCategory)i;
2774 }
2775 }
2776 }
2777 else
2778 {
2779 for(int i = 1; i <= METAL_MAKER; ++i)
2780 {
2781 if(urgency[i] > highest_urgency)
2782 {
2783 highest_urgency = urgency[i];
2784 category = (UnitCategory)i;
2785 }
2786 }
2787 }
2788
2789 if(category == POWER_PLANT)
2790 {
2791 if(BuildPowerPlant())
2792 construction_started = true;
2793 }
2794 else if(category == EXTRACTOR)
2795 {
2796 if(BuildExtractor())
2797 construction_started = true;
2798 }
2799 else if(category == STATIONARY_CONSTRUCTOR)
2800 {
2801 if(BuildFactory())
2802 construction_started = true;
2803 }
2804 else if(category == STATIONARY_DEF)
2805 {
2806 if(BuildDefences())
2807 construction_started = true;
2808 }
2809 else if(category == STATIONARY_RECON)
2810 {
2811 if(BuildRadar())
2812 construction_started = true;
2813 }
2814 else if(category == STATIONARY_JAMMER)
2815 {
2816 if(BuildJammer())
2817 construction_started = true;
2818 }
2819 else if(category == STATIONARY_ARTY)
2820 {
2821 if(BuildArty())
2822 construction_started = true;
2823 }
2824 else if(category == STORAGE)
2825 {
2826 if(BuildStorage())
2827 construction_started = true;
2828 }
2829 else if(category == METAL_MAKER)
2830 {
2831 if(BuildMetalMaker())
2832 construction_started = true;
2833 }
2834 else if(category == AIR_BASE)
2835 {
2836 if(BuildAirBase())
2837 construction_started = true;
2838 }
2839
2840 /*if(construction_started)
2841 {
2842 ai->Log("\n");
2843
2844 for(int i = 1; i < METAL_MAKER; ++i)
2845 ai->Log("%s: %f\n", ai->Getbt()->GetCategoryString2((UnitCategory)i), urgency[i]);
2846
2847 ai->Log("Selected category: %s\n", ai->Getbt()->GetCategoryString2(category));
2848 }*/
2849
2850 if(construction_started)
2851 urgency[category] = 0;
2852
2853 for(int i = 1; i <= METAL_MAKER; ++i)
2854 {
2855 urgency[i] *= 1.03f;
2856
2857 if(urgency[i] > 20.0f)
2858 urgency[i] -= 1.0f;
2859 }
2860 }
2861
AssistConstructionOfCategory(UnitCategory category,int)2862 bool AAIExecute::AssistConstructionOfCategory(UnitCategory category, int /*importance*/)
2863 {
2864 AAIConstructor *builder, *assistant;
2865
2866 for(list<AAIBuildTask*>::iterator task = ai->Getbuild_tasks().begin(); task != ai->Getbuild_tasks().end(); ++task)
2867 {
2868 if((*task)->builder_id >= 0)
2869 builder = ai->Getut()->units[(*task)->builder_id].cons;
2870 else
2871 builder = NULL;
2872
2873 if(builder && builder->construction_category == category && builder->assistants.size() < cfg->MAX_ASSISTANTS)
2874 {
2875 assistant = ai->Getut()->FindClosestAssistant(builder->build_pos, 5, true);
2876
2877 if(assistant)
2878 {
2879 builder->assistants.insert(assistant->unit_id);
2880 assistant->AssistConstruction(builder->unit_id, (*task)->unit_id);
2881 return true;
2882 }
2883 }
2884 }
2885
2886 return false;
2887 }
2888
sector_threat(AAISector * sec)2889 float AAIExecute::sector_threat(AAISector *sec)
2890 {
2891 float threat = sec->GetThreatBy(AIR_ASSAULT, learned, current);
2892
2893 if(cfg->AIR_ONLY_MOD)
2894 return threat;
2895
2896 threat += sec->GetThreatBy(HOVER_ASSAULT, learned, current);
2897
2898 if(sec->Getai()->Getmap()->map_type == LAND_MAP || sec->Getai()->Getmap()->map_type == LAND_WATER_MAP)
2899 threat += sec->GetThreatBy(GROUND_ASSAULT, learned, current);
2900 if(sec->Getai()->Getmap()->map_type == WATER_MAP || sec->Getai()->Getmap()->map_type == LAND_WATER_MAP)
2901 threat += sec->GetThreatBy(SEA_ASSAULT, learned, current);
2902 return threat;
2903 }
2904
least_dangerous(AAISector * left,AAISector * right)2905 bool AAIExecute::least_dangerous(AAISector *left, AAISector *right)
2906 {
2907 return sector_threat(left) < sector_threat(right);
2908 }
2909
suitable_for_power_plant(AAISector * left,AAISector * right)2910 bool AAIExecute::suitable_for_power_plant(AAISector *left, AAISector *right)
2911 {
2912 return sector_threat(left) * (float)left->map_border_dist < sector_threat(right) * (float)right->map_border_dist;
2913 }
2914
suitable_for_ground_factory(AAISector * left,AAISector * right)2915 bool AAIExecute::suitable_for_ground_factory(AAISector *left, AAISector *right)
2916 {
2917 return ( (2.0f * left->flat_ratio + left->map_border_dist)
2918 > (2.0f * right->flat_ratio + right->map_border_dist) );
2919 }
2920
suitable_for_sea_factory(AAISector * left,AAISector * right)2921 bool AAIExecute::suitable_for_sea_factory(AAISector *left, AAISector *right)
2922 {
2923 return ( (2.0f * left->water_ratio + left->map_border_dist)
2924 > (2.0f * right->water_ratio + right->map_border_dist) );
2925 }
2926
suitable_for_ground_rallypoint(AAISector * left,AAISector * right)2927 bool AAIExecute::suitable_for_ground_rallypoint(AAISector *left, AAISector *right)
2928 {
2929 return ( (left->flat_ratio + 0.5f * left->map_border_dist)/ ((float) (left->rally_points + 1) )
2930 > (right->flat_ratio + 0.5f * right->map_border_dist)/ ((float) (right->rally_points + 1) ) );
2931 }
2932
suitable_for_sea_rallypoint(AAISector * left,AAISector * right)2933 bool AAIExecute::suitable_for_sea_rallypoint(AAISector *left, AAISector *right)
2934 {
2935 return ( (left->water_ratio + 0.5f * left->map_border_dist)/ ((float) (left->rally_points + 1) )
2936 > (right->water_ratio + 0.5f * right->map_border_dist)/ ((float) (right->rally_points + 1) ) );
2937 }
2938
suitable_for_all_rallypoint(AAISector * left,AAISector * right)2939 bool AAIExecute::suitable_for_all_rallypoint(AAISector *left, AAISector *right)
2940 {
2941 return ( (left->flat_ratio + left->water_ratio + 0.5f * left->map_border_dist)/ ((float) (left->rally_points + 1) )
2942 > (right->flat_ratio + right->water_ratio + 0.5f * right->map_border_dist)/ ((float) (right->rally_points + 1) ) );
2943 }
2944
defend_vs_ground(AAISector * left,AAISector * right)2945 bool AAIExecute::defend_vs_ground(AAISector *left, AAISector *right)
2946 {
2947 return ((2.0f + left->GetThreatBy(GROUND_ASSAULT, learned, current)) / (left->GetMyDefencePowerAgainstAssaultCategory(0)+ 0.5f))
2948 > ((2.0f + right->GetThreatBy(GROUND_ASSAULT, learned, current)) / (left->GetMyDefencePowerAgainstAssaultCategory(0)+ 0.5f));
2949 }
2950
defend_vs_air(AAISector * left,AAISector * right)2951 bool AAIExecute::defend_vs_air(AAISector *left, AAISector *right)
2952 {
2953 return ((2.0f + left->GetThreatBy(AIR_ASSAULT, learned, current)) / (left->GetMyDefencePowerAgainstAssaultCategory(1)+ 0.5f))
2954 > ((2.0f + right->GetThreatBy(AIR_ASSAULT, learned, current)) / (left->GetMyDefencePowerAgainstAssaultCategory(1)+ 0.5f));
2955 }
2956
defend_vs_hover(AAISector * left,AAISector * right)2957 bool AAIExecute::defend_vs_hover(AAISector *left, AAISector *right)
2958 {
2959 return ((2.0f + left->GetThreatBy(HOVER_ASSAULT, learned, current)) / (left->GetMyDefencePowerAgainstAssaultCategory(2)+ 0.5f))
2960 > ((2.0f + right->GetThreatBy(HOVER_ASSAULT, learned, current)) / (left->GetMyDefencePowerAgainstAssaultCategory(2)+ 0.5f));
2961
2962 }
2963
defend_vs_sea(AAISector * left,AAISector * right)2964 bool AAIExecute::defend_vs_sea(AAISector *left, AAISector *right)
2965 {
2966 return ((2.0f + left->GetThreatBy(SEA_ASSAULT, learned, current)) / (left->GetMyDefencePowerAgainstAssaultCategory(3)+ 0.5f))
2967 > ((2.0f + right->GetThreatBy(SEA_ASSAULT, learned, current)) / (left->GetMyDefencePowerAgainstAssaultCategory(3)+ 0.5f));
2968 }
2969
defend_vs_submarine(AAISector * left,AAISector * right)2970 bool AAIExecute::defend_vs_submarine(AAISector *left, AAISector *right)
2971 {
2972 return ((2.0f + left->GetThreatBy(SUBMARINE_ASSAULT, learned, current)) / (left->GetMyDefencePowerAgainstAssaultCategory(4)+ 0.5f))
2973 > ((2.0f + right->GetThreatBy(SUBMARINE_ASSAULT, learned, current)) / (left->GetMyDefencePowerAgainstAssaultCategory(4)+ 0.5f));
2974 }
2975
ConstructionFinished()2976 void AAIExecute::ConstructionFinished()
2977 {
2978 }
2979
ConstructionFailed(float3 build_pos,int def_id)2980 void AAIExecute::ConstructionFailed(float3 build_pos, int def_id)
2981 {
2982 const UnitDef *def = &ai->Getbt()->GetUnitDef(def_id);
2983 UnitCategory category = ai->Getbt()->units_static[def_id].category;
2984
2985 int x = build_pos.x/ai->Getmap()->xSectorSize;
2986 int y = build_pos.z/ai->Getmap()->ySectorSize;
2987
2988 bool validSector = false;
2989
2990 if(x >= 0 && y >= 0 && x < ai->Getmap()->xSectors && y < ai->Getmap()->ySectors)
2991 validSector = true;
2992
2993 // decrease number of units of that category in the target sector
2994 if(validSector)
2995 ai->Getmap()->sector[x][y].RemoveBuildingType(def_id);
2996
2997 // free metalspot if mex was odered to be built
2998 if(category == EXTRACTOR && build_pos.x > 0)
2999 {
3000 ai->Getmap()->sector[x][y].FreeMetalSpot(build_pos, def);
3001 }
3002 else if(category == POWER_PLANT)
3003 {
3004 futureAvailableEnergy -= ai->Getbt()->units_static[def_id].efficiency[0];
3005
3006 if(futureAvailableEnergy < 0)
3007 futureAvailableEnergy = 0;
3008 }
3009 else if(category == STORAGE)
3010 {
3011 futureStoredEnergy -= ai->Getbt()->GetUnitDef(def->id).energyStorage;
3012 futureStoredMetal -= ai->Getbt()->GetUnitDef(def->id).metalStorage;
3013 }
3014 else if(category == METAL_MAKER)
3015 {
3016 futureRequestedEnergy -= ai->Getbt()->GetUnitDef(def->id).energyUpkeep;
3017
3018 if(futureRequestedEnergy < 0)
3019 futureRequestedEnergy = 0;
3020 }
3021 else if(category == STATIONARY_JAMMER)
3022 {
3023 futureRequestedEnergy -= ai->Getbt()->units_static[def->id].efficiency[0];
3024
3025 if(futureRequestedEnergy < 0)
3026 futureRequestedEnergy = 0;
3027 }
3028 else if(category == STATIONARY_RECON)
3029 {
3030 futureRequestedEnergy -= ai->Getbt()->units_static[def->id].efficiency[0];
3031
3032 if(futureRequestedEnergy < 0)
3033 futureRequestedEnergy = 0;
3034 }
3035 else if(category == STATIONARY_DEF)
3036 {
3037 ai->Getmap()->RemoveDefence(&build_pos, def_id);
3038 }
3039
3040 // clear buildmap
3041 if(category == STATIONARY_CONSTRUCTOR)
3042 {
3043 ai->Getut()->futureFactories -= 1;
3044
3045 for(list<int>::iterator unit = ai->Getbt()->units_static[def_id].canBuildList.begin(); unit != ai->Getbt()->units_static[def_id].canBuildList.end(); ++unit)
3046 ai->Getbt()->units_dynamic[*unit].constructorsRequested -= 1;
3047
3048 // remove future ressource demand since factory is no longer being built
3049 futureRequestedMetal -= ai->Getbt()->units_static[def->id].efficiency[0];
3050 futureRequestedEnergy -= ai->Getbt()->units_static[def->id].efficiency[1];
3051
3052 if(futureRequestedEnergy < 0)
3053 futureRequestedEnergy = 0;
3054
3055 if(futureRequestedMetal < 0)
3056 futureRequestedMetal = 0;
3057
3058 // update buildmap of sector
3059 ai->Getmap()->UpdateBuildMap(build_pos, def, false, ai->Getbt()->CanPlacedWater(def_id), true);
3060 }
3061 else // normal building
3062 {
3063 // update buildmap of sector
3064 ai->Getmap()->UpdateBuildMap(build_pos, def, false, ai->Getbt()->CanPlacedWater(def_id), false);
3065 }
3066 }
3067
AddStartFactory()3068 void AAIExecute::AddStartFactory()
3069 {
3070 float best_rating = 0, my_rating;
3071 int best_factory = 0;
3072
3073 for(list<int>::iterator fac = ai->Getbt()->units_of_category[STATIONARY_CONSTRUCTOR][ai->Getside()-1].begin(); fac != ai->Getbt()->units_of_category[STATIONARY_CONSTRUCTOR][ai->Getside()-1].end(); ++fac)
3074 {
3075 if(ai->Getbt()->units_dynamic[*fac].constructorsAvailable > 0)
3076 {
3077 my_rating = ai->Getbt()->GetFactoryRating(*fac);
3078 my_rating *= (2.0 - (ai->Getbt()->units_static[*fac].cost / ai->Getbt()->max_cost[STATIONARY_CONSTRUCTOR][ai->Getside()-1]));
3079
3080 if(my_rating > best_rating)
3081 {
3082 best_rating = my_rating;
3083 best_factory = *fac;
3084 }
3085 }
3086 }
3087
3088 if(best_factory)
3089 {
3090 ai->Getbt()->units_dynamic[best_factory].requested += 1;
3091 urgency[STATIONARY_CONSTRUCTOR] = 3.0f;
3092
3093 ai->Log("%s requested\n", ai->Getbt()->GetUnitDef(best_factory).humanName.c_str());
3094
3095 for(list<int>::iterator j = ai->Getbt()->units_static[best_factory].canBuildList.begin(); j != ai->Getbt()->units_static[best_factory].canBuildList.end(); ++j)
3096 ai->Getbt()->units_dynamic[*j].constructorsRequested += 1;
3097 }
3098 }
3099
GetClosestGroupForDefence(UnitType group_type,float3 * pos,int continent,int)3100 AAIGroup* AAIExecute::GetClosestGroupForDefence(UnitType group_type, float3 *pos, int continent, int /*importance*/)
3101 {
3102 AAIGroup *best_group = 0;
3103 float best_rating = 0, my_rating;
3104 float3 group_pos;
3105
3106 for(list<UnitCategory>::iterator category = ai->Getbt()->assault_categories.begin(); category != ai->Getbt()->assault_categories.end(); ++category)
3107 {
3108 for(list<AAIGroup*>::iterator group = ai->Getgroup_list()[*category].begin(); group != ai->Getgroup_list()[*category].end(); ++group)
3109 {
3110 if((*group)->group_unit_type == group_type && !(*group)->attack)
3111 {
3112 if((*group)->continent == -1 || (*group)->continent == continent)
3113 {
3114 if((*group)->task == GROUP_IDLE) // || (*group)->task_importance < importance)
3115 {
3116 group_pos = (*group)->GetGroupPos();
3117
3118 my_rating = (*group)->avg_speed / ( 1.0 + fastmath::apxsqrt((pos->x - group_pos.x) * (pos->x - group_pos.x) + (pos->z - group_pos.z) * (pos->z - group_pos.z) ));
3119
3120 if(my_rating > best_rating)
3121 {
3122 best_group = *group;
3123 best_rating = my_rating;
3124 }
3125 }
3126 }
3127 }
3128 }
3129 }
3130
3131 return best_group;
3132 }
3133
DefendUnitVS(int unit,unsigned int enemy_movement_type,float3 * enemy_pos,int importance)3134 void AAIExecute::DefendUnitVS(int unit, unsigned int enemy_movement_type, float3 *enemy_pos, int importance)
3135 {
3136 AAIGroup *support = 0;
3137
3138 int continent = ai->Getmap()->GetContinentID(enemy_pos);
3139
3140 UnitType group_type;
3141
3142 // anti air needed
3143 if(enemy_movement_type & MOVE_TYPE_AIR && !cfg->AIR_ONLY_MOD)
3144 group_type = ANTI_AIR_UNIT;
3145 else
3146 group_type = ASSAULT_UNIT;
3147
3148 support = GetClosestGroupForDefence(group_type, enemy_pos, continent, 100);
3149
3150 if(support)
3151 support->Defend(unit, enemy_pos, importance);
3152 }
3153
GetSafePos(int def_id,float3 unit_pos)3154 float3 AAIExecute::GetSafePos(int def_id, float3 unit_pos)
3155 {
3156 float3 best_pos = ZeroVector;
3157 float my_rating, best_rating = -10000.0f;
3158
3159 if(ai->Getbt()->units_static[def_id].movement_type & MOVE_TYPE_CONTINENT_BOUND)
3160 {
3161 // get continent id of the unit pos
3162 float3 pos;
3163 int cont_id = ai->Getmap()->GetContinentID(&unit_pos);
3164
3165 for(list<AAISector*>::iterator sector = ai->Getbrain()->sectors[0].begin(); sector != ai->Getbrain()->sectors[0].end(); ++sector)
3166 {
3167 pos = (*sector)->GetCenter();
3168
3169 if(ai->Getmap()->GetContinentID(&pos) == cont_id)
3170 {
3171 my_rating = (*sector)->map_border_dist - (*sector)->GetEnemyThreatToMovementType(ai->Getbt()->units_static[def_id].movement_type);
3172
3173 if(my_rating > best_rating)
3174 {
3175 best_rating = my_rating;
3176 best_pos = pos;
3177 }
3178 }
3179 }
3180
3181 }
3182 else // non continent bound movement types (air, hover, amphibious)
3183 {
3184 for(list<AAISector*>::iterator sector = ai->Getbrain()->sectors[0].begin(); sector != ai->Getbrain()->sectors[0].end(); ++sector)
3185 {
3186 my_rating = (*sector)->map_border_dist - (*sector)->GetEnemyThreatToMovementType(ai->Getbt()->units_static[def_id].movement_type);
3187
3188 if(my_rating > best_rating)
3189 {
3190 best_rating = my_rating;
3191 best_pos = (*sector)->GetCenter();
3192 }
3193 }
3194 }
3195
3196 return best_pos;
3197 }
3198
ChooseDifferentStartingSector(int x,int y)3199 void AAIExecute::ChooseDifferentStartingSector(int x, int y)
3200 {
3201 // get possible start sectors
3202 list<AAISector*> sectors;
3203
3204 if(x >= 1)
3205 {
3206 sectors.push_back( &ai->Getmap()->sector[x-1][y] );
3207
3208 if(y >= 1)
3209 sectors.push_back( &ai->Getmap()->sector[x-1][y-1] );
3210
3211 if(y < ai->Getmap()->ySectors-1)
3212 sectors.push_back( &ai->Getmap()->sector[x-1][y+1] );
3213 }
3214
3215 if(x < ai->Getmap()->xSectors-1)
3216 {
3217 sectors.push_back( &ai->Getmap()->sector[x+1][y] );
3218
3219 if(y >= 1)
3220 sectors.push_back( &ai->Getmap()->sector[x+1][y-1] );
3221
3222 if(y < ai->Getmap()->ySectors-1)
3223 sectors.push_back( &ai->Getmap()->sector[x+1][y+1] );
3224 }
3225
3226 if(y >= 1)
3227 sectors.push_back( &ai->Getmap()->sector[x][y-1] );
3228
3229 if(y < ai->Getmap()->ySectors-1)
3230 sectors.push_back( &ai->Getmap()->sector[x][y+1] );
3231
3232 // choose best
3233 AAISector *best_sector = 0;
3234 float my_rating, best_rating = 0;
3235
3236 for(list<AAISector*>::iterator sector = sectors.begin(); sector != sectors.end(); ++sector)
3237 {
3238 if(ai->Getmap()->team_sector_map[(*sector)->x][(*sector)->y] != -1)
3239 my_rating = 0;
3240 else
3241 my_rating = ( (float)(2 * (*sector)->GetNumberOfMetalSpots() + 1) ) * (*sector)->flat_ratio * (*sector)->flat_ratio;
3242
3243 if(my_rating > best_rating)
3244 {
3245 best_rating = my_rating;
3246 best_sector = *sector;
3247 }
3248 }
3249
3250 // add best sector to base
3251 if(best_sector)
3252 {
3253 ai->Getbrain()->AddSector(best_sector);
3254 ai->Getbrain()->start_pos = best_sector->GetCenter();
3255
3256 ai->Getbrain()->UpdateNeighbouringSectors();
3257 ai->Getbrain()->UpdateBaseCenter();
3258 }
3259 }
3260
CheckFallBack(int unit_id,int def_id)3261 void AAIExecute::CheckFallBack(int unit_id, int def_id)
3262 {
3263 float max_weapon_range = ai->Getbt()->units_static[def_id].range;
3264
3265 if(max_weapon_range > cfg->MIN_FALLBACK_RANGE && ai->Getbt()->GetUnitDef(def_id).turnRate >= cfg->MIN_FALLBACK_TURNRATE)
3266 {
3267 if(max_weapon_range > cfg->MAX_FALLBACK_RANGE)
3268 max_weapon_range = cfg->MAX_FALLBACK_RANGE;
3269
3270 float3 pos;
3271
3272 GetFallBackPos(&pos, unit_id, max_weapon_range);
3273
3274 if(pos.x > 0)
3275 {
3276 Command c;
3277 c.id = CMD_MOVE;
3278 c.params.resize(3);
3279
3280 c.params[0] = pos.x;
3281 c.params[1] = ai->Getcb()->GetElevation(pos.x, pos.z);
3282 c.params[2] = pos.z;
3283
3284 //ai->Getcb()->GiveOrder(unit_id, &c);
3285 GiveOrder(&c, unit_id, "Fallback");
3286 }
3287 }
3288 }
3289
3290
GetFallBackPos(float3 * pos,int unit_id,float max_weapon_range) const3291 void AAIExecute::GetFallBackPos(float3 *pos, int unit_id, float max_weapon_range) const
3292 {
3293 *pos = ZeroVector;
3294
3295 const float3 unit_pos = ai->Getcb()->GetUnitPos(unit_id);
3296
3297 // units without range should not end up here; this is for attacking units only
3298 // prevents a NaN
3299 assert(max_weapon_range != 0.0f);
3300
3301 // get list of enemies within weapons range
3302 const int number_of_enemies = ai->Getcb()->GetEnemyUnits(&(ai->Getmap()->units_in_los.front()), unit_pos, max_weapon_range * cfg->FALLBACK_DIST_RATIO);
3303
3304 if(number_of_enemies > 0)
3305 {
3306 float3 enemy_pos;
3307
3308 for(int k = 0; k < number_of_enemies; ++k)
3309 {
3310 enemy_pos = ai->Getcb()->GetUnitPos(ai->Getmap()->units_in_los[k]);
3311
3312 // get distance to enemy
3313 float dx = enemy_pos.x - unit_pos.x;
3314 float dz = enemy_pos.z - unit_pos.z;
3315 float dist = fastmath::apxsqrt(dx*dx + dz*dz);
3316
3317 // get dir from unit to enemy
3318 enemy_pos.x -= unit_pos.x;
3319 enemy_pos.z -= unit_pos.z;
3320
3321 // move closer to enemy if we are out of range,
3322 // and away if we are closer then our max range
3323 pos->x += ((dist / max_weapon_range) - 1) * enemy_pos.x;
3324 pos->z += ((dist / max_weapon_range) - 1) * enemy_pos.z;
3325 }
3326
3327 // move less if lots of enemies are close
3328 pos->x /= (float)number_of_enemies;
3329 pos->z /= (float)number_of_enemies;
3330
3331 // apply relative move distance to the current position
3332 // to get the target position
3333 pos->x += unit_pos.x;
3334 pos->z += unit_pos.z;
3335 }
3336 }
3337
GiveOrder(Command * c,int unit,const char * owner)3338 void AAIExecute::GiveOrder(Command *c, int unit, const char *owner)
3339 {
3340 ++issued_orders;
3341
3342 if(issued_orders%500 == 0)
3343 ai->Log("%i th order has been given by %s in frame %i\n", issued_orders, owner, ai->Getcb()->GetCurrentFrame());
3344
3345 ai->Getut()->units[unit].last_order = ai->Getcb()->GetCurrentFrame();
3346
3347 ai->Getcb()->GiveOrder(unit, c);
3348 }
3349