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