1 // -------------------------------------------------------------------------
2 // AAI
3 //
4 // A skirmish AI for the Spring engine.
5 // Copyright Alexander Seizinger
6 //
7 // Released under GPL license: see LICENSE.html for more information.
8 // -------------------------------------------------------------------------
9 
10 #include "System/Util.h"
11 #include "AAIBuildTable.h"
12 #include "AAI.h"
13 #include "AAIBrain.h"
14 #include "AAIExecute.h"
15 #include "AAIUnitTable.h"
16 #include "AAIConfig.h"
17 #include "AAIMap.h"
18 
19 #include "LegacyCpp/UnitDef.h"
20 #include "LegacyCpp/MoveData.h"
21 using namespace springLegacyAI;
22 
23 
24 // all the static vars
25 vector<vector<list<int>>> AAIBuildTable::units_of_category;
26 char AAIBuildTable::buildtable_filename[500];
27 vector<vector<float>> AAIBuildTable::avg_cost;
28 vector<vector<float>> AAIBuildTable::avg_buildtime;
29 vector<vector<float>> AAIBuildTable::avg_value;
30 vector<vector<float>> AAIBuildTable::max_cost;
31 vector<vector<float>> AAIBuildTable::max_buildtime;
32 vector<vector<float>> AAIBuildTable::max_value;
33 vector<vector<float>> AAIBuildTable::min_cost;
34 vector<vector<float>> AAIBuildTable::min_buildtime;
35 vector<vector<float>> AAIBuildTable::min_value;
36 vector<vector<float>> AAIBuildTable::avg_speed;
37 vector<vector<float>> AAIBuildTable::min_speed;
38 vector<vector<float>> AAIBuildTable::max_speed;
39 vector<vector<float>> AAIBuildTable::group_speed;
40 vector< vector< vector<float> > > AAIBuildTable::attacked_by_category_learned;
41 vector< vector<float> > AAIBuildTable::attacked_by_category_current;
42 vector<UnitTypeStatic> AAIBuildTable::units_static;
43 vector<vector<double> >AAIBuildTable::def_power;
44 vector<double>AAIBuildTable::max_pplant_eff;
45 /*float* AAIBuildTable::max_builder_buildtime;
46 float* AAIBuildTable::max_builder_cost;
47 float* AAIBuildTable::max_builder_buildspeed;*/
48 vector< vector< vector<float> > > AAIBuildTable::avg_eff;
49 vector< vector< vector<float> > > AAIBuildTable::max_eff;
50 vector< vector< vector<float> > > AAIBuildTable::min_eff;
51 vector< vector< vector<float> > > AAIBuildTable::total_eff;
52 vector< vector<float> > AAIBuildTable::fixed_eff;
53 
54 
AAIBuildTable(AAI * ai)55 AAIBuildTable::AAIBuildTable(AAI* ai)
56 {
57 	this->ai = ai;
58 
59 	initialized = false;
60 
61 	numOfSides = cfg->SIDES;
62 	startUnits.resize(numOfSides);
63 	sideNames.resize(numOfSides+1);
64 	sideNames[0] = "Neutral";
65 
66 	const UnitDef *temp;
67 
68 	for(int i = 0; i < numOfSides; ++i)
69 	{
70 		temp = ai->Getcb()->GetUnitDef(cfg->START_UNITS[i].c_str());
71 
72 		if(temp)
73 			startUnits[i] = temp->id;
74 		else
75 		{
76 			startUnits[i] = -1;
77 			ai->LogConsole("Error: starting unit %s not found\n",
78 					cfg->START_UNITS[i].c_str());
79 		}
80 
81 		sideNames[i+1].assign(cfg->SIDE_NAMES[i]);
82 	}
83 
84 	// add assault categories
85 	assault_categories.push_back(GROUND_ASSAULT);
86 	assault_categories.push_back(AIR_ASSAULT);
87 	assault_categories.push_back(HOVER_ASSAULT);
88 	assault_categories.push_back(SEA_ASSAULT);
89 	assault_categories.push_back(SUBMARINE_ASSAULT);
90 
91 	// only set up static things if first aai intsance is iniatialized
92 	if(ai->GetInstances() == 1)
93 	{
94 		avg_cost.resize(MOBILE_CONSTRUCTOR+1);
95 		avg_buildtime.resize(MOBILE_CONSTRUCTOR+1);
96 		avg_value.resize(MOBILE_CONSTRUCTOR+1);
97 		max_cost.resize(MOBILE_CONSTRUCTOR+1);
98 		max_buildtime.resize(MOBILE_CONSTRUCTOR+1);
99 		max_value.resize(MOBILE_CONSTRUCTOR+1);
100 		min_cost.resize(MOBILE_CONSTRUCTOR+1);
101 		min_buildtime.resize(MOBILE_CONSTRUCTOR+1);
102 		min_value.resize(MOBILE_CONSTRUCTOR+1);
103 		units_of_category.resize(MOBILE_CONSTRUCTOR+1);
104 
105 		for(int i = 0; i <= MOBILE_CONSTRUCTOR; ++i)
106 		{
107 			// set up the unit lists
108 			units_of_category[i].resize(numOfSides);
109 
110 			// statistical values (mod sepcific)
111 			avg_cost[i].resize(numOfSides);
112 			avg_buildtime[i].resize(numOfSides);
113 			avg_value[i].resize(numOfSides);
114 			max_cost[i].resize(numOfSides);
115 			max_buildtime[i].resize(numOfSides);
116 			max_value[i].resize(numOfSides);
117 			min_cost[i].resize(numOfSides);
118 			min_buildtime[i].resize(numOfSides);
119 			min_value[i].resize(numOfSides);
120 
121 			for(int s = 0; s < numOfSides; ++s)
122 			{
123 				avg_cost[i][s] = -1;
124 				avg_buildtime[i][s] = -1;
125 				avg_value[i][s] = -1;
126 				max_cost[i][s] = -1;
127 				max_buildtime[i][s] = -1;
128 				max_value[i][s] = -1;
129 				min_cost[i][s] = -1;
130 				min_buildtime[i][s] = -1;
131 				min_value[i][s] = -1;
132 			}
133 		}
134 
135 		// statistical values for builders (map specific)
136 		/*max_builder_buildtime = new float[numOfSides];
137 		max_builder_cost = new float[numOfSides];
138 		max_builder_buildspeed = new float[numOfSides];
139 
140 		for(int s = 0; s < numOfSides; ++s)
141 		{
142 			max_builder_buildtime[s] = -1;
143 			max_builder_cost[s] = -1;
144 			max_builder_buildspeed[s] = -1;
145 		}*/
146 
147 		// set up speed and attacked_by table
148 		avg_speed.resize(combat_categories);
149 		max_speed.resize(combat_categories);
150 		min_speed.resize(combat_categories);
151 		group_speed.resize(combat_categories);
152 
153 		attacked_by_category_current.resize(cfg->GAME_PERIODS, vector<float>(combat_categories, 0));
154 		attacked_by_category_learned.resize(3,  vector< vector<float> >(cfg->GAME_PERIODS, vector<float>(combat_categories, 0)));
155 
156 		for(int i = 0; i < combat_categories; ++i)
157 		{
158 			avg_speed[i].resize(numOfSides);
159 			max_speed[i].resize(numOfSides);
160 			min_speed[i].resize(numOfSides);
161 			group_speed[i].resize(numOfSides);
162 		}
163 
164 		// init eff stats
165 		avg_eff.resize(numOfSides, vector< vector<float> >(combat_categories, vector<float>(combat_categories, 1.0f)));
166 		max_eff.resize(numOfSides, vector< vector<float> >(combat_categories, vector<float>(combat_categories, 1.0f)));
167 		min_eff.resize(numOfSides, vector< vector<float> >(combat_categories, vector<float>(combat_categories, 1.0f)));
168 		total_eff.resize(numOfSides, vector< vector<float> >(combat_categories, vector<float>(combat_categories, 1.0f)));
169 	}
170 }
171 
~AAIBuildTable(void)172 AAIBuildTable::~AAIBuildTable(void)
173 {
174 	// delete common data only if last aai instance has gone
175 	if(ai->GetInstances() == 0)
176 	{
177 		units_of_category.clear();
178 
179 		avg_cost.clear();
180 		avg_buildtime.clear();
181 		avg_value.clear();
182 		max_cost.clear();
183 		max_buildtime.clear();
184 		max_value.clear();
185 		min_cost.clear();
186 		min_buildtime.clear();
187 		min_value.clear();
188 
189 		/*SafeDeleteArray(max_builder_buildtime);
190 		SafeDeleteArray(max_builder_cost);
191 		SafeDeleteArray(max_builder_buildspeed);*/
192 
193 		avg_speed.clear();
194 		max_speed.clear();
195 		min_speed.clear();
196 		group_speed.clear();
197 
198 		attacked_by_category_learned.clear();
199 		attacked_by_category_current.clear();
200 
201 		avg_eff.clear();
202 		max_eff.clear();
203 		min_eff.clear();
204 		total_eff.clear();
205 	}
206 	unitList.clear();
207 }
208 
Init()209 void AAIBuildTable::Init()
210 {
211 	float max_cost = 0, min_cost = 1000000, eff;
212 
213 	// get number of units and alloc memory for unit list
214 	const int numOfUnits = ai->Getcb()->GetNumUnitDefs();
215 
216 	// one more than needed because 0 is dummy object (so UnitDef->id can be used to adress that unit in the array)
217 	units_dynamic.resize(numOfUnits+1);
218 
219 	for(int i = 0; i <= numOfUnits; ++i)
220 	{
221 		units_dynamic[i].active = 0;
222 		units_dynamic[i].requested = 0;
223 		units_dynamic[i].constructorsAvailable = 0;
224 		units_dynamic[i].constructorsRequested = 0;
225 	}
226 
227 	// get unit defs from game
228 	if(unitList.empty())
229 	{
230 		//spring first unitdef id is 1, we remap it so id = is position in array
231 		unitList.resize(numOfUnits+1);
232 		ai->Getcb()->GetUnitDefList(&unitList[1]);
233 		UnitDef* tmp = new UnitDef();
234 		tmp->id=0;
235 		unitList[0] = tmp;
236 		#ifndef NDEBUG
237 		for(int i=0; i<numOfUnits; i++) {
238 			assert(i == GetUnitDef(i).id);
239 		}
240 		#endif
241 	}
242 
243 	// Try to load buildtable; if not possible, create a new one
244 	if(!LoadBuildTable())
245 	{
246 		// one more than needed because 0 is dummy object
247 		// (so UnitDef->id can be used to address that unit in the array)
248 		units_static.resize(numOfUnits+1);
249 		fixed_eff.resize(numOfUnits+1, vector<float>(combat_categories));
250 
251 		// temporary list to sort air unit in air only mods
252 		list<int> *temp_list;
253 		temp_list = new list<int>[numOfSides];
254 
255 		units_static[0].def_id = 0;
256 		units_static[0].side = 0;
257 
258 		// add units to buildtable
259 		for(int i = 0; i <= numOfUnits; ++i)
260 		{
261 			// get id
262 			units_static[i].def_id = GetUnitDef(i).id;
263 			units_static[i].cost = (GetUnitDef(i).metalCost + (GetUnitDef(i).energyCost / 75.0f)) / 10.0f;
264 
265 			if(units_static[i].cost > max_cost)
266 				max_cost = units_static[i].cost;
267 
268 			if(units_static[i].cost < min_cost)
269 				min_cost = units_static[i].cost;
270 
271 			units_static[i].builder_cost = 0; // will be added later when calculating the buildtree
272 
273 			// side has not been assigned - will be done later
274 			units_static[i].side = 0;
275 			units_static[i].range = 0;
276 
277 			units_static[i].category = UNKNOWN;
278 
279 			units_static[i].unit_type = 0;
280 
281 			// get build options
282 			for(map<int, string>::const_iterator j = GetUnitDef(i).buildOptions.begin(); j != GetUnitDef(i).buildOptions.end(); ++j)
283 				units_static[i].canBuildList.push_back(ai->Getcb()->GetUnitDef(j->second.c_str())->id);
284 		}
285 
286 		// now set the sides and create buildtree
287 		for(int s = 0; s < numOfSides; s++)
288 		{
289 			// set side of the start unit (eg commander) and continue recursively
290 			units_static[startUnits[s]].side = s+1;
291 			CalcBuildTree(startUnits[s]);
292 		}
293 
294 		// now calculate efficiency of combat units and get max range
295 		for(int i = 1; i <= numOfUnits; i++)
296 		{
297 			// effiency has starting value of 1
298 			if(!GetUnitDef(i).weapons.empty())
299 			{
300 				// get range
301 				units_static[i].range = GetMaxRange(i);
302 
303 				// get memory for eff
304 				units_static[i].efficiency.resize(combat_categories);
305 
306 				eff = 5 + 25 * (units_static[i].cost - min_cost)/(max_cost - min_cost);
307 
308 				for(int k = 0; k < combat_categories; ++k)
309 				{
310 					units_static[i].efficiency[k] = eff;
311 					fixed_eff[i][k] = eff;
312 				}
313 			}
314 			else
315 			{
316 				units_static[i].range = 0;
317 
318 				// get memory for eff
319 				units_static[i].efficiency.resize(combat_categories, -1.0f);
320 			}
321 		}
322 
323 		//
324 		// determine movement type
325 		//
326 		for(int i = 1; i <= numOfUnits; i++)
327 		{
328 			units_static[i].movement_type = 0;
329 
330 			if(GetUnitDef(i).movedata)
331 			{
332 				if(GetUnitDef(i).movedata->moveFamily == MoveData::Tank || GetUnitDef(i).movedata->moveFamily == MoveData::KBot)
333 				{
334 					// check for amphibious units
335 					if(GetUnitDef(i).movedata->depth > 250)
336 						units_static[i].movement_type |= MOVE_TYPE_AMPHIB;
337 					else
338 						units_static[i].movement_type |= MOVE_TYPE_GROUND;
339 				}
340 				else if(GetUnitDef(i).movedata->moveFamily == MoveData::Hover) {
341 					units_static[i].movement_type |= MOVE_TYPE_HOVER;
342 				}
343 				// ship
344 				else if(GetUnitDef(i).movedata->moveFamily == MoveData::Ship)
345 				{
346 					units_static[i].movement_type |= MOVE_TYPE_SEA;
347 
348 					if(GetUnitDef(i).categoryString.find("UNDERWATER") != string::npos) {
349 						units_static[i].movement_type |= MOVE_TYPE_UNDERWATER;
350 					} else {
351 						units_static[i].movement_type |= MOVE_TYPE_FLOATER;
352 					}
353 				}
354 			}
355 			// aircraft
356 			else if(GetUnitDef(i).canfly)
357 				units_static[i].movement_type |= MOVE_TYPE_AIR;
358 			// stationary
359 			else
360 			{
361 				units_static[i].movement_type |= MOVE_TYPE_STATIC;
362 
363 				if(GetUnitDef(i).minWaterDepth <= 0)
364 				{
365 					units_static[i].movement_type |= MOVE_TYPE_STATIC_LAND;
366 				}
367 				else
368 				{
369 					units_static[i].movement_type |= MOVE_TYPE_STATIC_WATER;
370 
371 					if(GetUnitDef(i).floater)
372 						units_static[i].movement_type |= MOVE_TYPE_FLOATER;
373 					else
374 						units_static[i].movement_type |= MOVE_TYPE_UNDERWATER;
375 				}
376 			}
377 		}
378 
379 		//
380 		// put units into the different categories
381 		//
382 		for(int i = 0; i <= numOfUnits; ++i)
383 		{
384 			if(!units_static[i].side || !AllowedToBuild(i))
385 			{
386 			}
387 			// get scouts
388 			else if(IsScout(i))
389 			{
390 				units_of_category[SCOUT][units_static[i].side-1].push_back(GetUnitDef(i).id);
391 				units_static[i].category = SCOUT;
392 			}
393 			// get mobile transport
394 			else if(IsTransporter(i))
395 			{
396 				units_of_category[MOBILE_TRANSPORT][units_static[i].side-1].push_back(GetUnitDef(i).id);
397 				units_static[i].category = MOBILE_TRANSPORT;
398 			}
399 			// check if builder or factory
400 			else if(GetUnitDef(i).buildOptions.size() > 0 && !IsAttacker(i))
401 			{
402 				// stationary constructors
403 				if(units_static[i].movement_type & MOVE_TYPE_STATIC)
404 				{
405 					// ground factory or sea factory
406 					units_of_category[STATIONARY_CONSTRUCTOR][units_static[i].side-1].push_back(GetUnitDef(i).id);
407 					units_static[i].category = STATIONARY_CONSTRUCTOR;
408 				}
409 				// mobile constructors
410 				else
411 				{
412 					units_of_category[MOBILE_CONSTRUCTOR][units_static[i].side-1].push_back(GetUnitDef(i).id);
413 					units_static[i].category = MOBILE_CONSTRUCTOR;
414 				}
415 			}
416 			// no builder or factory
417 			// check if other building
418 			else if(units_static[i].movement_type & MOVE_TYPE_STATIC)
419 			{
420 				// check if extractor
421 				if(GetUnitDef(i).extractsMetal)
422 				{
423 					units_of_category[EXTRACTOR][units_static[i].side-1].push_back(GetUnitDef(i).id);
424 					units_static[i].category = EXTRACTOR;
425 				}
426 				// check if repair pad
427 				else if(GetUnitDef(i).isAirBase)
428 				{
429 					units_of_category[AIR_BASE][units_static[i].side-1].push_back(GetUnitDef(i).id);
430 					units_static[i].category = AIR_BASE;
431 				}
432 				// check if powerplant
433 				else if(GetUnitDef(i).energyMake > cfg->MIN_ENERGY || GetUnitDef(i).tidalGenerator || GetUnitDef(i).windGenerator || GetUnitDef(i).energyUpkeep < -cfg->MIN_ENERGY)
434 				{
435 					if(!GetUnitDef(i).isAirBase && GetUnitDef(i).radarRadius == 0 && GetUnitDef(i).sonarRadius == 0)
436 					{
437 						units_of_category[POWER_PLANT][units_static[i].side-1].push_back(GetUnitDef(i).id);
438 						units_static[i].category = POWER_PLANT;
439 					}
440 				}
441 				// check if defence building
442 				else if(!GetUnitDef(i).weapons.empty() && GetMaxDamage(i) > 1)
443 				{
444 					// filter out nuke silos, antinukes and stuff like that
445 					if(IsMissileLauncher(i))
446 					{
447 						units_of_category[STATIONARY_LAUNCHER][units_static[i].side-1].push_back(GetUnitDef(i).id);
448 						units_static[i].category = STATIONARY_LAUNCHER;
449 					}
450 					else if(IsDeflectionShieldEmitter(i))
451 					{
452 						units_of_category[DEFLECTION_SHIELD][units_static[i].side-1].push_back(GetUnitDef(i).id);
453 						units_static[i].category = DEFLECTION_SHIELD;
454 					}
455 					else
456 					{
457 						if(GetMaxRange(GetUnitDef(i).id) < cfg->STATIONARY_ARTY_RANGE)
458 						{
459 							units_of_category[STATIONARY_DEF][units_static[i].side-1].push_back(GetUnitDef(i).id);
460 							units_static[i].category = STATIONARY_DEF;
461 						}
462 						else
463 						{
464 							units_of_category[STATIONARY_ARTY][units_static[i].side-1].push_back(GetUnitDef(i).id);
465 							units_static[i].category = STATIONARY_ARTY;
466 						}
467 					}
468 
469 				}
470 				// check if radar or sonar
471 				else if(GetUnitDef(i).radarRadius > 0 || GetUnitDef(i).sonarRadius > 0)
472 				{
473 					units_of_category[STATIONARY_RECON][units_static[i].side-1].push_back(GetUnitDef(i).id);
474 					units_static[i].category = STATIONARY_RECON;
475 				}
476 				// check if jammer
477 				else if(GetUnitDef(i).jammerRadius > 0 || GetUnitDef(i).sonarJamRadius > 0)
478 				{
479 					units_of_category[STATIONARY_JAMMER][units_static[i].side-1].push_back(GetUnitDef(i).id);
480 					units_static[i].category = STATIONARY_JAMMER;
481 				}
482 				// check storage or converter
483 				else if( GetUnitDef(i).energyStorage > cfg->MIN_ENERGY_STORAGE && !GetUnitDef(i).energyMake)
484 				{
485 					units_of_category[STORAGE][units_static[i].side-1].push_back(GetUnitDef(i).id);
486 					units_static[i].category = STORAGE;
487 				}
488 				else if(GetUnitDef(i).metalStorage > cfg->MIN_METAL_STORAGE && !GetUnitDef(i).extractsMetal)
489 				{
490 					units_of_category[STORAGE][units_static[i].side-1].push_back(GetUnitDef(i).id);
491 					units_static[i].category = STORAGE;
492 				}
493 				else if(GetUnitDef(i).makesMetal > 0 || GetUnitDef(i).metalMake > 0 || IsMetalMaker(i))
494 				{
495 					units_of_category[METAL_MAKER][units_static[i].side-1].push_back(GetUnitDef(i).id);
496 					units_static[i].category = METAL_MAKER;
497 				}
498 			}
499 			// units that are not builders
500 			else if(GetUnitDef(i).movedata)
501 			{
502 				// ground units
503 				if(GetUnitDef(i).movedata->moveFamily == MoveData::Tank ||
504 					GetUnitDef(i).movedata->moveFamily == MoveData::KBot ||
505 					GetUnitDef(i).movedata->moveFamily == MoveData::Hover)
506 				{
507 					// units with weapons
508 					if((!GetUnitDef(i).weapons.empty() && GetMaxDamage(i) > 1) || IsAttacker(i))
509 					{
510 						if(IsMissileLauncher(i))
511 						{
512 							units_of_category[MOBILE_LAUNCHER][units_static[i].side-1].push_back(GetUnitDef(i).id);
513 							units_static[i].category = MOBILE_LAUNCHER;
514 						}
515 						else if(GetMaxDamage(GetUnitDef(i).id) > 1)
516 						{
517 							// switch between arty and assault
518 							if(IsArty(i))
519 							{
520 								if(GetUnitDef(i).movedata->moveFamily == MoveData::Tank || GetUnitDef(i).movedata->moveFamily == MoveData::KBot)
521 								{
522 									units_of_category[GROUND_ARTY][units_static[i].side-1].push_back(GetUnitDef(i).id);
523 									units_static[i].category = GROUND_ARTY;
524 								}
525 								else
526 								{
527 									units_of_category[HOVER_ARTY][units_static[i].side-1].push_back(GetUnitDef(i).id);
528 									units_static[i].category = HOVER_ARTY;
529 								}
530 							}
531 							else if(GetUnitDef(i).speed > 0)
532 							{
533 								if(GetUnitDef(i).movedata->moveFamily == MoveData::Tank || GetUnitDef(i).movedata->moveFamily == MoveData::KBot)
534 								{
535 									units_of_category[GROUND_ASSAULT][units_static[i].side-1].push_back(GetUnitDef(i).id);
536 									units_static[i].category = GROUND_ASSAULT;
537 								}
538 								else
539 								{
540 									units_of_category[HOVER_ASSAULT][units_static[i].side-1].push_back(GetUnitDef(i).id);
541 									units_static[i].category = HOVER_ASSAULT;
542 								}
543 							}
544 						}
545 
546 						else if(GetUnitDef(i).sonarJamRadius > 0 || GetUnitDef(i).jammerRadius > 0)
547 						{
548 							units_of_category[MOBILE_JAMMER][units_static[i].side-1].push_back(GetUnitDef(i).id);
549 							units_static[i].category = MOBILE_JAMMER;
550 						}
551 					}
552 					// units without weapons
553 					else
554 					{
555 						if(GetUnitDef(i).sonarJamRadius > 0 || GetUnitDef(i).jammerRadius > 0)
556 						{
557 							units_of_category[MOBILE_JAMMER][units_static[i].side-1].push_back(GetUnitDef(i).id);
558 							units_static[i].category = MOBILE_JAMMER;
559 						}
560 					}
561 				}
562 				else if(GetUnitDef(i).movedata->moveFamily == MoveData::Ship)
563 				{
564 					// ship
565 					if(!GetUnitDef(i).weapons.empty())
566 					{
567 						if(IsMissileLauncher(i))
568 						{
569 							units_of_category[MOBILE_LAUNCHER][units_static[i].side-1].push_back(GetUnitDef(i).id);
570 							units_static[i].category = MOBILE_LAUNCHER;
571 						}
572 						else if(GetMaxDamage(GetUnitDef(i).id) > 1 || IsAttacker(i))
573 						{
574 							if(GetUnitDef(i).categoryString.find("UNDERWATER") != string::npos)
575 							{
576 								units_of_category[SUBMARINE_ASSAULT][units_static[i].side-1].push_back(GetUnitDef(i).id);
577 								units_static[i].category = SUBMARINE_ASSAULT;
578 							}
579 							else
580 							{
581 								// switch between arty and assault
582 								if(IsArty(i))
583 								{	units_of_category[SEA_ARTY][units_static[i].side-1].push_back(GetUnitDef(i).id);
584 									units_static[i].category = SEA_ARTY;
585 								}
586 								else
587 								{
588 									units_of_category[SEA_ASSAULT][units_static[i].side-1].push_back(GetUnitDef(i).id);
589 									units_static[i].category = SEA_ASSAULT;
590 								}
591 							}
592 						}
593 						else if(GetUnitDef(i).sonarJamRadius > 0 || GetUnitDef(i).jammerRadius > 0)
594 						{
595 							units_of_category[MOBILE_JAMMER][units_static[i].side-1].push_back(GetUnitDef(i).id);
596 							units_static[i].category = MOBILE_JAMMER;
597 						}
598 					}
599 					else
600 					{
601 						if(GetUnitDef(i).sonarJamRadius > 0 || GetUnitDef(i).jammerRadius > 0)
602 						{
603 							units_of_category[MOBILE_JAMMER][units_static[i].side-1].push_back(GetUnitDef(i).id);
604 							units_static[i].category = MOBILE_JAMMER;
605 						}
606 					}
607 				}
608 			}
609 			// aircraft
610 			else if(GetUnitDef(i).canfly)
611 			{
612 				// units with weapons
613 				if((!GetUnitDef(i).weapons.empty() && GetMaxDamage(GetUnitDef(i).id) > 1) || IsAttacker(i))
614 				{
615 					if(GetUnitDef(i).weapons.begin()->def->stockpile)
616 					{
617 						units_of_category[MOBILE_LAUNCHER][units_static[i].side-1].push_back(GetUnitDef(i).id);
618 						units_static[i].category = MOBILE_LAUNCHER;
619 					}
620 					else
621 					{
622 						// to apply different sorting rules later
623 						if(cfg->AIR_ONLY_MOD)
624 							temp_list[units_static[i].side-1].push_back(GetUnitDef(i).id);
625 
626 						units_of_category[AIR_ASSAULT][units_static[i].side-1].push_back(GetUnitDef(i).id);
627 						units_static[i].category = AIR_ASSAULT;
628 					}
629 				}
630 			}
631 
632 			// get commander
633 			if(IsStartingUnit(GetUnitDef(i).id))
634 			{
635 				units_static[i].category = COMMANDER;
636 				units_of_category[COMMANDER][units_static[i].side-1].push_back(GetUnitDef(i).id);
637 			}
638 		}
639 
640 		//
641 		// determine unit type
642 		//
643 		for(int i = 1; i <= numOfUnits; i++)
644 		{
645 			// check for factories and builders
646 			if(units_static[i].canBuildList.size() > 0)
647 			{
648 				for(list<int>::iterator unit = units_static[i].canBuildList.begin(); unit != units_static[i].canBuildList.end(); ++unit)
649 				{
650 					// filter out neutral and unknown units
651 					if(units_static[*unit].side > 0 && units_static[*unit].category != UNKNOWN)
652 					{
653 						if(units_static[*unit].movement_type & MOVE_TYPE_STATIC)
654 							units_static[i].unit_type |= UNIT_TYPE_BUILDER;
655 						else
656 							units_static[i].unit_type |= UNIT_TYPE_FACTORY;
657 					}
658 				}
659 
660 				if(!(units_static[i].movement_type & MOVE_TYPE_STATIC) && GetUnitDef(i).canAssist)
661 					units_static[i].unit_type |= UNIT_TYPE_ASSISTER;
662 			}
663 
664 			if(GetUnitDef(i).canResurrect)
665 				units_static[i].unit_type |= UNIT_TYPE_RESURRECTOR;
666 
667 			if(IsStartingUnit(GetUnitDef(i).id))
668 				units_static[i].unit_type |= UNIT_TYPE_COMMANDER;
669 		}
670 
671 
672 		if(!cfg->AIR_ONLY_MOD)
673 		{
674 			UnitCategory cat;
675 			float eff;
676 
677 			for(int i = 1; i <= numOfUnits; ++i)
678 			{
679 				cat = units_static[i].category;
680 				eff = 1.5 + 7 * (units_static[i].cost - min_cost)/(max_cost - min_cost);
681 
682 				if(cat == AIR_ASSAULT)
683 				{
684 					for(int k = 0; k < combat_categories; ++k)
685 						units_static[i].efficiency[k] = eff;
686 				}
687 				else if(cat == GROUND_ASSAULT ||  cat == HOVER_ASSAULT || cat == SEA_ASSAULT || cat == SUBMARINE_ASSAULT || cat == GROUND_ARTY || cat == SEA_ARTY || cat == HOVER_ARTY || cat == STATIONARY_DEF)
688 				{
689 					units_static[i].efficiency[1] = eff;
690 				}
691 			}
692 		}
693 
694 		// precache stats
695 		PrecacheStats();
696 
697 		// apply specific sort rules
698 		if(cfg->AIR_ONLY_MOD)
699 		{
700 			float total_cost, my_cost;
701 
702 			for(int s = 0; s < numOfSides; ++s)
703 			{
704 				total_cost = this->max_cost[AIR_ASSAULT][s] - this->min_cost[AIR_ASSAULT][s];
705 
706 				if(total_cost <= 0)
707 					break;
708 
709 				// clear list
710 				units_of_category[AIR_ASSAULT][s].clear();
711 
712 				for(list<int>::iterator unit = temp_list[s].begin(); unit != temp_list[s].end(); ++unit)
713 				{
714 					my_cost = (units_static[*unit].cost - this->min_cost[AIR_ASSAULT][s]) / total_cost;
715 
716 					if(my_cost < cfg->MAX_COST_LIGHT_ASSAULT)
717 					{
718 						units_of_category[GROUND_ASSAULT][s].push_back(*unit);
719 						units_static[*unit].category = GROUND_ASSAULT;
720 					}
721 					else if(my_cost < cfg->MAX_COST_MEDIUM_ASSAULT)
722 					{
723 						units_of_category[AIR_ASSAULT][s].push_back(*unit);
724 						units_static[*unit].category = AIR_ASSAULT;
725 					}
726 					else if(my_cost < cfg->MAX_COST_HEAVY_ASSAULT)
727 					{
728 						units_of_category[HOVER_ASSAULT][s].push_back(*unit);
729 						units_static[*unit].category = HOVER_ASSAULT;
730 					}
731 					else
732 					{
733 						units_of_category[SEA_ASSAULT][s].push_back(*unit);
734 						units_static[*unit].category = SEA_ASSAULT;
735 					}
736 				}
737 			}
738 
739 			// recalculate stats
740 			PrecacheStats();
741 		}
742 
743 		// save to cache file
744 		SaveBuildTable(0, LAND_MAP);
745 
746 		ai->LogConsole("New BuildTable has been created");
747 	}
748 
749 
750 
751 	// only once
752 	if(ai->GetInstances() == 1)
753 	{
754 		// apply possible cost multipliers
755 		if(cfg->cost_multipliers.size() > 0)
756 		{
757 			for(size_t i = 0; i < cfg->cost_multipliers.size(); ++i)
758 				units_static[cfg->cost_multipliers[i].id].cost *= cfg->cost_multipliers[i].multiplier;
759 
760 			// recalculate costs
761 			PrecacheCosts();
762 		}
763 
764 		UpdateMinMaxAvgEfficiency();
765 
766 		float temp;
767 
768 		def_power.resize(numOfSides);
769 		max_pplant_eff.resize(numOfSides);
770 
771 		for(int s = 0; s < numOfSides; ++s)
772 		{
773 			def_power[s].resize(units_of_category[STATIONARY_DEF][s].size());
774 
775 			// power plant max eff
776 			max_pplant_eff[s] = 0;
777 
778 			for(list<int>::iterator pplant = units_of_category[POWER_PLANT][s].begin(); pplant != units_of_category[POWER_PLANT][s].end(); ++pplant)
779 			{
780 				temp = units_static[*pplant].efficiency[1];
781 
782 				// eff. of tidal generators have not been calculated yet (depend on map)
783 				if(temp == 0)
784 				{
785 					temp = ai->Getcb()->GetTidalStrength() / units_static[*pplant].cost;
786 
787 					units_static[*pplant].efficiency[0] = ai->Getcb()->GetTidalStrength();
788 					units_static[*pplant].efficiency[1] = temp;
789 				} else if (temp < 0) {
790 					temp = (ai->Getcb()->GetMaxWind() + ai->Getcb()->GetMinWind()) * 0.5f / units_static[*pplant].cost;
791 
792 					units_static[*pplant].efficiency[0] = (ai->Getcb()->GetMaxWind() + ai->Getcb()->GetMinWind()) * 0.5f;
793 					units_static[*pplant].efficiency[1] = temp;
794 				}
795 
796 				if(temp > max_pplant_eff[s])
797 					max_pplant_eff[s] = temp;
798 			}
799 		}
800 
801 		DebugPrint();
802 	}
803 
804 	// buildtable is initialized
805 	initialized = true;
806 }
807 
InitCombatEffCache(int side)808 void AAIBuildTable::InitCombatEffCache(int side)
809 {
810 	side--;
811 
812 	size_t max_size = 0;
813 
814 	UnitCategory category;
815 
816 	for(int cat = 0; cat < combat_categories; ++cat)
817 	{
818 		category = GetAssaultCategoryOfID(cat);
819 
820 		if(units_of_category[(int)category][side].size() > max_size)
821 			max_size = units_of_category[(int)category][side].size();
822 	}
823 
824 	combat_eff.resize(max_size, 0);
825 }
826 
PrecacheStats()827 void AAIBuildTable::PrecacheStats()
828 {
829 	for(int s = 0; s < numOfSides; s++)
830 	{
831 		// precache efficiency of power plants
832 		for(list<int>::iterator i = units_of_category[POWER_PLANT][s].begin(); i != units_of_category[POWER_PLANT][s].end(); ++i)
833 		{
834 			if(GetUnitDef(*i).tidalGenerator)
835 				units_static[*i].efficiency[0] = 0;
836 			else if (GetUnitDef(*i).windGenerator)
837 				units_static[*i].efficiency[0] = -1;
838 			else if(GetUnitDef(*i).energyMake >= cfg->MIN_ENERGY)
839 				units_static[*i].efficiency[0] = GetUnitDef(*i).energyMake;
840 			else if(GetUnitDef(*i).energyUpkeep <= -cfg->MIN_ENERGY)
841 				units_static[*i].efficiency[0] = - GetUnitDef(*i).energyUpkeep;
842 
843 			units_static[*i].efficiency[1] = units_static[*i].efficiency[0] / units_static[*i].cost;
844 		}
845 
846 		// precache efficiency of extractors
847 		for(list<int>::iterator i = units_of_category[EXTRACTOR][s].begin(); i != units_of_category[EXTRACTOR][s].end(); ++i)
848 			units_static[*i].efficiency[0] = GetUnitDef(*i).extractsMetal;
849 
850 		// precache efficiency of metalmakers
851 		for(list<int>::iterator i = units_of_category[METAL_MAKER][s].begin(); i != units_of_category[METAL_MAKER][s].end(); ++i) {
852 			if (GetUnitDef(*i).makesMetal <= 0.1f) {
853 				units_static[*i].efficiency[0] = 12.0f/600.0f; //FIXME: this somehow is broken...
854 			} else {
855 				units_static[*i].efficiency[0] = GetUnitDef(*i).makesMetal/(GetUnitDef(*i).energyUpkeep+1);
856 			}
857 		}
858 
859 
860 		// precache average metal and energy consumption of factories
861 		float average_metal, average_energy;
862 		for(list<int>::iterator i = units_of_category[STATIONARY_CONSTRUCTOR][s].begin(); i != units_of_category[STATIONARY_CONSTRUCTOR][s].end(); ++i)
863 		{
864 			average_metal = average_energy = 0;
865 
866 			for(list<int>::iterator unit = units_static[*i].canBuildList.begin(); unit != units_static[*i].canBuildList.end(); ++unit)
867 			{
868 				average_metal += ( GetUnitDef(*unit).metalCost * GetUnitDef(*i).buildSpeed ) / GetUnitDef(*unit).buildTime;
869 				average_energy += ( GetUnitDef(*unit).energyCost * GetUnitDef(*i).buildSpeed ) / GetUnitDef(*unit).buildTime;
870 			}
871 
872 			units_static[*i].efficiency[0] = average_metal / units_static[*i].canBuildList.size();
873 			units_static[*i].efficiency[1] = average_energy / units_static[*i].canBuildList.size();
874 		}
875 
876 		// precache range of arty
877 		for(list<int>::iterator i = units_of_category[STATIONARY_ARTY][s].begin(); i != units_of_category[STATIONARY_ARTY][s].end(); ++i)
878 		{
879 			units_static[*i].efficiency[1] = GetMaxRange(*i);
880 			units_static[*i].efficiency[0] = 1 + units_static[*i].cost/100.0;
881 		}
882 
883 		// precache costs and buildtime
884 		float buildtime;
885 
886 		for(int i = 1; i <= MOBILE_CONSTRUCTOR; ++i)
887 		{
888 			// precache costs
889 			avg_cost[i][s] = 0;
890 			this->min_cost[i][s] = 10000;
891 			this->max_cost[i][s] = 0;
892 
893 			for(list<int>::iterator unit = units_of_category[i][s].begin(); unit != units_of_category[i][s].end(); ++unit)
894 			{
895 				avg_cost[i][s] += units_static[*unit].cost;
896 
897 				if(units_static[*unit].cost > this->max_cost[i][s])
898 					this->max_cost[i][s] = units_static[*unit].cost;
899 
900 				if(units_static[*unit].cost < this->min_cost[i][s] )
901 					this->min_cost[i][s] = units_static[*unit].cost;
902 			}
903 
904 			if(units_of_category[i][s].size() > 0)
905 				avg_cost[i][s] /= units_of_category[i][s].size();
906 			else
907 			{
908 				avg_cost[i][s] = -1;
909 				this->min_cost[i][s] = -1;
910 				this->max_cost[i][s] = -1;
911 			}
912 
913 			// precache buildtime
914 			min_buildtime[i][s] = 10000;
915 			avg_buildtime[i][s] = 0;
916 			max_buildtime[i][s] = 0;
917 
918 			for(list<int>::iterator unit = units_of_category[i][s].begin(); unit != units_of_category[i][s].end(); ++unit)
919 			{
920 				buildtime = GetUnitDef(*unit).buildTime;
921 
922 				avg_buildtime[i][s] += buildtime;
923 
924 				if(buildtime > max_buildtime[i][s])
925 					max_buildtime[i][s] = buildtime;
926 
927 				if(buildtime < min_buildtime[i][s])
928 					min_buildtime[i][s] = buildtime;
929 			}
930 
931 			if(units_of_category[i][s].size() > 0)
932 				avg_buildtime[i][s] /= units_of_category[i][s].size();
933 			else
934 			{
935 				avg_buildtime[i][s] = -1;
936 				min_buildtime[i][s] = -1;
937 				max_buildtime[i][s] = -1;
938 			}
939 		}
940 
941 		// precache radar ranges
942 		min_value[STATIONARY_RECON][s] = 10000;
943 		avg_value[STATIONARY_RECON][s] = 0;
944 		max_value[STATIONARY_RECON][s] = 0;
945 
946 		for(list<int>::iterator unit = units_of_category[STATIONARY_RECON][s].begin(); unit != units_of_category[STATIONARY_RECON][s].end(); ++unit)
947 		{
948 			avg_value[STATIONARY_RECON][s] += GetUnitDef(*unit).radarRadius;
949 
950 			if(GetUnitDef(*unit).radarRadius > max_value[STATIONARY_RECON][s])
951 				max_value[STATIONARY_RECON][s] = GetUnitDef(*unit).radarRadius;
952 
953 			if(GetUnitDef(*unit).radarRadius < min_value[STATIONARY_RECON][s])
954 				min_value[STATIONARY_RECON][s] = GetUnitDef(*unit).radarRadius;
955 		}
956 
957 		if(units_of_category[STATIONARY_RECON][s].size() > 0)
958 			avg_value[STATIONARY_RECON][s] /= units_of_category[STATIONARY_RECON][s].size();
959 		else
960 		{
961 			min_value[STATIONARY_RECON][s] = -1;
962 			avg_value[STATIONARY_RECON][s] = -1;
963 			max_value[STATIONARY_RECON][s] = -1;
964 		}
965 
966 		// precache jammer ranges
967 		min_value[STATIONARY_JAMMER][s] = 10000;
968 		avg_value[STATIONARY_JAMMER][s] = 0;
969 		max_value[STATIONARY_JAMMER][s] = 0;
970 
971 		for(list<int>::iterator unit = units_of_category[STATIONARY_JAMMER][s].begin(); unit != units_of_category[STATIONARY_JAMMER][s].end(); ++unit)
972 		{
973 			avg_value[STATIONARY_JAMMER][s] += GetUnitDef(*unit).jammerRadius;
974 
975 			if(GetUnitDef(*unit).jammerRadius > max_value[STATIONARY_JAMMER][s])
976 				max_value[STATIONARY_JAMMER][s] = GetUnitDef(*unit).jammerRadius;
977 
978 			if(GetUnitDef(*unit).jammerRadius < min_value[STATIONARY_JAMMER][s])
979 				min_value[STATIONARY_JAMMER][s] = GetUnitDef(*unit).jammerRadius;
980 		}
981 
982 		if(units_of_category[STATIONARY_JAMMER][s].size() > 0)
983 			avg_value[STATIONARY_JAMMER][s] /= units_of_category[STATIONARY_JAMMER][s].size();
984 		else
985 		{
986 			min_value[STATIONARY_JAMMER][s] = -1;
987 			avg_value[STATIONARY_JAMMER][s] = -1;
988 			max_value[STATIONARY_JAMMER][s] = -1;
989 		}
990 
991 		// precache usage of jammers
992 		for(list<int>::iterator i = units_of_category[STATIONARY_JAMMER][s].begin(); i != units_of_category[STATIONARY_JAMMER][s].end(); ++i)
993 		{
994 			if(GetUnitDef(*i).energyUpkeep - GetUnitDef(*i).energyMake > 0)
995 				units_static[*i].efficiency[0] = GetUnitDef(*i).energyUpkeep - GetUnitDef(*i).energyMake;
996 		}
997 
998 		// precache usage of radar
999 		for(list<int>::iterator i = units_of_category[STATIONARY_RECON][s].begin(); i != units_of_category[STATIONARY_RECON][s].end(); ++i)
1000 		{
1001 			if(GetUnitDef(*i).energyUpkeep - GetUnitDef(*i).energyMake > 0)
1002 				units_static[*i].efficiency[0] = GetUnitDef(*i).energyUpkeep - GetUnitDef(*i).energyMake;
1003 		}
1004 
1005 		// precache extractor efficiency
1006 		min_value[EXTRACTOR][s] = 10000;
1007 		avg_value[EXTRACTOR][s] = 0;
1008 		max_value[EXTRACTOR][s] = 0;
1009 
1010 		for(list<int>::iterator unit = units_of_category[EXTRACTOR][s].begin(); unit != units_of_category[EXTRACTOR][s].end(); ++unit)
1011 		{
1012 			avg_value[EXTRACTOR][s] += GetUnitDef(*unit).extractsMetal;
1013 
1014 			if(GetUnitDef(*unit).extractsMetal > max_value[EXTRACTOR][s])
1015 				max_value[EXTRACTOR][s] = GetUnitDef(*unit).extractsMetal;
1016 
1017 			if(GetUnitDef(*unit).extractsMetal < min_value[EXTRACTOR][s])
1018 				min_value[EXTRACTOR][s] = GetUnitDef(*unit).extractsMetal;
1019 		}
1020 
1021 		if(units_of_category[EXTRACTOR][s].size() > 0)
1022 			avg_value[EXTRACTOR][s] /= units_of_category[EXTRACTOR][s].size();
1023 		else
1024 		{
1025 			min_value[EXTRACTOR][s] = -1;
1026 			avg_value[EXTRACTOR][s] = -1;
1027 			max_value[EXTRACTOR][s] = -1;
1028 		}
1029 
1030 		// precache power plant energy production
1031 		min_value[POWER_PLANT][s] = 10000;
1032 		avg_value[POWER_PLANT][s] = 0;
1033 		max_value[POWER_PLANT][s] = 0;
1034 
1035 		for(list<int>::iterator unit = units_of_category[POWER_PLANT][s].begin(); unit != units_of_category[POWER_PLANT][s].end(); ++unit)
1036 		{
1037 			avg_value[POWER_PLANT][s] += units_static[*unit].efficiency[0];
1038 
1039 			if(units_static[*unit].efficiency[0] > max_value[POWER_PLANT][s])
1040 				max_value[POWER_PLANT][s] = units_static[*unit].efficiency[0];
1041 
1042 			if(units_static[*unit].efficiency[0] < min_value[POWER_PLANT][s])
1043 				min_value[POWER_PLANT][s] = units_static[*unit].efficiency[0];
1044 		}
1045 
1046 		if(units_of_category[POWER_PLANT][s].size() > 0)
1047 			avg_value[POWER_PLANT][s] /= units_of_category[POWER_PLANT][s].size();
1048 		else
1049 		{
1050 			min_value[POWER_PLANT][s] = -1;
1051 			avg_value[POWER_PLANT][s] = -1;
1052 			max_value[POWER_PLANT][s] = -1;
1053 		}
1054 
1055 		// precache stationary arty range
1056 		min_value[STATIONARY_ARTY][s] = 100000;
1057 		avg_value[STATIONARY_ARTY][s] = 0;
1058 		max_value[STATIONARY_ARTY][s] = 0;
1059 
1060 		for(list<int>::iterator unit = units_of_category[STATIONARY_ARTY][s].begin(); unit != units_of_category[STATIONARY_ARTY][s].end(); ++unit)
1061 		{
1062 			avg_value[STATIONARY_ARTY][s] += units_static[*unit].efficiency[1];
1063 
1064 			if(units_static[*unit].efficiency[1] > max_value[STATIONARY_ARTY][s])
1065 				max_value[STATIONARY_ARTY][s] = units_static[*unit].efficiency[1];
1066 
1067 			if(units_static[*unit].efficiency[1] < min_value[STATIONARY_ARTY][s])
1068 				min_value[STATIONARY_ARTY][s] = units_static[*unit].efficiency[1];
1069 		}
1070 
1071 		if(units_of_category[STATIONARY_ARTY][s].size() > 0)
1072 			avg_value[STATIONARY_ARTY][s] /= units_of_category[STATIONARY_ARTY][s].size();
1073 		else
1074 		{
1075 			min_value[STATIONARY_ARTY][s] = -1;
1076 			avg_value[STATIONARY_ARTY][s] = -1;
1077 			max_value[STATIONARY_ARTY][s] = -1;
1078 		}
1079 
1080 		// precache scout los
1081 		min_value[SCOUT][s] = 100000;
1082 		avg_value[SCOUT][s] = 0;
1083 		max_value[SCOUT][s] = 0;
1084 
1085 		for(list<int>::iterator unit = units_of_category[SCOUT][s].begin(); unit != units_of_category[SCOUT][s].end(); ++unit)
1086 		{
1087 			avg_value[SCOUT][s] += GetUnitDef(*unit).losRadius;
1088 
1089 			if(GetUnitDef(*unit).losRadius > max_value[SCOUT][s])
1090 				max_value[SCOUT][s] = GetUnitDef(*unit).losRadius;
1091 
1092 			if(GetUnitDef(*unit).losRadius < min_value[SCOUT][s])
1093 				min_value[SCOUT][s] = GetUnitDef(*unit).losRadius;
1094 		}
1095 
1096 		if(units_of_category[SCOUT][s].size() > 0)
1097 			avg_value[SCOUT][s] /= units_of_category[SCOUT][s].size();
1098 		else
1099 		{
1100 			min_value[SCOUT][s] = -1;
1101 			avg_value[SCOUT][s] = -1;
1102 			max_value[SCOUT][s] = -1;
1103 		}
1104 
1105 		// precache stationary defences weapon range
1106 		min_value[STATIONARY_DEF][s] = 100000;
1107 		avg_value[STATIONARY_DEF][s] = 0;
1108 		max_value[STATIONARY_DEF][s] = 0;
1109 
1110 		float range;
1111 
1112 		if(units_of_category[STATIONARY_DEF][s].size() > 0)
1113 		{
1114 			for(list<int>::iterator unit = units_of_category[STATIONARY_DEF][s].begin(); unit != units_of_category[STATIONARY_DEF][s].end(); ++unit)
1115 			{
1116 				range = units_static[*unit].range;
1117 
1118 				avg_value[STATIONARY_DEF][s] += range;
1119 
1120 				if(range > max_value[STATIONARY_DEF][s])
1121 					max_value[STATIONARY_DEF][s] = range;
1122 
1123 				if(range < min_value[STATIONARY_DEF][s])
1124 					min_value[STATIONARY_DEF][s] = range;
1125 			}
1126 
1127 			avg_value[STATIONARY_DEF][s] /= (float)units_of_category[STATIONARY_DEF][s].size();
1128 		}
1129 		else
1130 		{
1131 			min_value[STATIONARY_DEF][s] = -1;
1132 			avg_value[STATIONARY_DEF][s] = -1;
1133 			max_value[STATIONARY_DEF][s] = -1;
1134 		}
1135 
1136 		// precache builders' buildspeed
1137 		float buildspeed;
1138 
1139 		if(units_of_category[MOBILE_CONSTRUCTOR][s].size() > 0)
1140 		{
1141 			min_value[MOBILE_CONSTRUCTOR][s] = 100000;
1142 			avg_value[MOBILE_CONSTRUCTOR][s] = 0;
1143 			max_value[MOBILE_CONSTRUCTOR][s] = 0;
1144 
1145 			for(list<int>::iterator unit = units_of_category[MOBILE_CONSTRUCTOR][s].begin(); unit != units_of_category[MOBILE_CONSTRUCTOR][s].end(); ++unit)
1146 			{
1147 				buildspeed = GetUnitDef(*unit).buildSpeed;
1148 
1149 				avg_value[MOBILE_CONSTRUCTOR][s] += buildspeed;
1150 
1151 				if(buildspeed > max_value[MOBILE_CONSTRUCTOR][s])
1152 					max_value[MOBILE_CONSTRUCTOR][s] = buildspeed;
1153 
1154 				if(buildspeed < min_value[MOBILE_CONSTRUCTOR][s])
1155 					min_value[MOBILE_CONSTRUCTOR][s] = buildspeed;
1156 			}
1157 
1158 			avg_value[MOBILE_CONSTRUCTOR][s] /= (float)units_of_category[MOBILE_CONSTRUCTOR][s].size();
1159 		}
1160 		else
1161 		{
1162 			min_value[MOBILE_CONSTRUCTOR][s] = -1;
1163 			avg_value[MOBILE_CONSTRUCTOR][s] = -1;
1164 			max_value[MOBILE_CONSTRUCTOR][s] = -1;
1165 		}
1166 
1167 
1168 		// precache unit speed and weapons range
1169 		int cat;
1170 
1171 		for(list<UnitCategory>::iterator category = assault_categories.begin(); category != assault_categories.end(); ++category)
1172 		{
1173 			// precache range
1174 			min_value[*category][s] = 10000;
1175 			avg_value[*category][s] = 0;
1176 			max_value[*category][s] = 0;
1177 
1178 			if(units_of_category[*category][s].size() > 0)
1179 			{
1180 				for(list<int>::iterator unit = units_of_category[*category][s].begin(); unit != units_of_category[*category][s].end(); ++unit)
1181 				{
1182 					range = GetMaxRange(*unit);
1183 
1184 					avg_value[*category][s] += range;
1185 
1186 					if(range > max_value[*category][s])
1187 						max_value[*category][s] = range;
1188 
1189 					if(range < min_value[*category][s])
1190 						min_value[*category][s] = range;
1191 				}
1192 
1193 				avg_value[*category][s] /= (float)units_of_category[*category][s].size();
1194 			}
1195 			else
1196 			{
1197 				min_value[*category][s] = -1;
1198 				avg_value[*category][s] = -1;
1199 				max_value[*category][s] = -1;
1200 			}
1201 
1202 			// precache speed
1203 			cat = GetIDOfAssaultCategory(*category);
1204 
1205 			if(cat != -1)
1206 			{
1207 				if(units_of_category[*category][s].size() > 0)
1208 				{
1209 					min_speed[cat][s] = 10000;
1210 					max_speed[cat][s] = 0;
1211 					group_speed[cat][s] = 0;
1212 					avg_speed[cat][s] = 0;
1213 
1214 					for(list<int>::iterator unit = units_of_category[*category][s].begin(); unit != units_of_category[*category][s].end(); ++unit)
1215 					{
1216 						avg_speed[cat][s] += GetUnitDef(*unit).speed;
1217 
1218 						if(GetUnitDef(*unit).speed < min_speed[cat][s])
1219 							min_speed[cat][s] = GetUnitDef(*unit).speed;
1220 
1221 						if(GetUnitDef(*unit).speed > max_speed[cat][s])
1222 							max_speed[cat][s] = GetUnitDef(*unit).speed;
1223 					}
1224 
1225 					avg_speed[cat][s] /= (float)units_of_category[*category][s].size();
1226 
1227 					group_speed[cat][s] = (1 + max_speed[cat][s] - min_speed[cat][s]) / ((float)cfg->UNIT_SPEED_SUBGROUPS);
1228 				}
1229 				else
1230 				{
1231 					min_speed[cat][s] = -1;
1232 					max_speed[cat][s] = -1;
1233 					group_speed[cat][s] = -1;
1234 					avg_speed[cat][s] = -1;
1235 				}
1236 			}
1237 		}
1238 	}
1239 }
1240 
PrecacheCosts()1241 void AAIBuildTable::PrecacheCosts()
1242 {
1243 	for(int s = 0; s < numOfSides; ++s)
1244 	{
1245 		for(int i = 1; i <= MOBILE_CONSTRUCTOR; ++i)
1246 		{
1247 			// precache costs
1248 			avg_cost[i][s] = 0;
1249 			this->min_cost[i][s] = 10000;
1250 			this->max_cost[i][s] = 0;
1251 
1252 			for(list<int>::iterator unit = units_of_category[i][s].begin(); unit != units_of_category[i][s].end(); ++unit)
1253 			{
1254 				avg_cost[i][s] += units_static[*unit].cost;
1255 
1256 				if(units_static[*unit].cost > this->max_cost[i][s])
1257 					this->max_cost[i][s] = units_static[*unit].cost;
1258 
1259 				if(units_static[*unit].cost < this->min_cost[i][s] )
1260 					this->min_cost[i][s] = units_static[*unit].cost;
1261 			}
1262 
1263 			if(units_of_category[i][s].size() > 0)
1264 				avg_cost[i][s] /= units_of_category[i][s].size();
1265 			else
1266 			{
1267 				avg_cost[i][s] = -1;
1268 				this->min_cost[i][s] = -1;
1269 				this->max_cost[i][s] = -1;
1270 			}
1271 		}
1272 	}
1273 }
1274 
1275 
GetSide(int unit)1276 int AAIBuildTable::GetSide(int unit)
1277 {
1278 	return units_static[GetUnitDef(unit).id].side;
1279 }
1280 
GetSideByID(int unit_id)1281 int AAIBuildTable::GetSideByID(int unit_id)
1282 {
1283 	return units_static[unit_id].side;
1284 }
1285 
GetUnitType(int def_id)1286 UnitType AAIBuildTable::GetUnitType(int def_id)
1287 {
1288 	if(cfg->AIR_ONLY_MOD)
1289 	{
1290 		return ASSAULT_UNIT;
1291 	}
1292 	else
1293 	{
1294 		if (units_static.empty()) return UNKNOWN_UNIT;
1295 		UnitCategory cat = units_static[def_id].category;
1296 		int side = units_static[def_id].side-1;
1297 
1298 		if(cat == GROUND_ASSAULT)
1299 		{
1300 			if( units_static[def_id].efficiency[1] / max_eff[side][0][1]  > 6 * units_static[def_id].efficiency[0] / max_eff[side][0][0] )
1301 				return ANTI_AIR_UNIT;
1302 			else
1303 				return ASSAULT_UNIT;
1304 		}
1305 		else if(cat == AIR_ASSAULT)
1306 		{
1307 			float vs_building = units_static[def_id].efficiency[5] / max_eff[side][1][5];
1308 
1309 			float vs_units = (units_static[def_id].efficiency[0] / max_eff[side][1][0]
1310 							+ units_static[def_id].efficiency[3] / max_eff[side][1][3]) / 2.0f;
1311 
1312 			if( units_static[def_id].efficiency[1]  / max_eff[side][1][1] > 2 * (vs_building + vs_units) )
1313 				return ANTI_AIR_UNIT;
1314 			else
1315 			{
1316 				if(vs_building > 4 * vs_units || GetUnitDef(def_id).type == string("Bomber"))
1317 					return BOMBER_UNIT;
1318 				else
1319 					return ASSAULT_UNIT;
1320 			}
1321 		}
1322 		else if(cat == HOVER_ASSAULT)
1323 		{
1324 			if( units_static[def_id].efficiency[1] / max_eff[side][2][1] > 6 * units_static[def_id].efficiency[0] / max_eff[side][2][0] )
1325 				return ANTI_AIR_UNIT;
1326 			else
1327 				return ASSAULT_UNIT;
1328 		}
1329 		else if(cat == SEA_ASSAULT)
1330 		{
1331 			if( units_static[def_id].efficiency[1] / max_eff[side][3][1] > 6 * units_static[def_id].efficiency[3] / max_eff[side][3][3] )
1332 				return ANTI_AIR_UNIT;
1333 			else
1334 				return ASSAULT_UNIT;
1335 		}
1336 		else if(cat == SUBMARINE_ASSAULT)
1337 		{
1338 			if( units_static[def_id].efficiency[1] / max_eff[side][4][1] > 6 * units_static[def_id].efficiency[3] / max_eff[side][4][3] )
1339 				return ANTI_AIR_UNIT;
1340 			else
1341 				return ASSAULT_UNIT;
1342 		}
1343 		else if(cat >= GROUND_ARTY && cat <= HOVER_ARTY)
1344 		{
1345 			return ARTY_UNIT;
1346 		} else //throw "AAIBuildTable::GetUnitType: invalid unit category";
1347 			return UNKNOWN_UNIT;
1348 	}
1349 }
1350 
MemberOf(int unit_id,list<int> unit_list)1351 bool AAIBuildTable::MemberOf(int unit_id, list<int> unit_list)
1352 {
1353 	// test all units in list
1354 	for(list<int>::iterator i = unit_list.begin(); i != unit_list.end(); ++i)
1355 	{
1356 		if(*i == unit_id)
1357 			return true;
1358 	}
1359 
1360 	// unitid not found
1361 	return false;
1362 }
1363 
GetPowerPlant(int side,float cost,float urgency,float power,float,bool water,bool geo,bool canBuild)1364 int AAIBuildTable::GetPowerPlant(int side, float cost, float urgency, float power, float /*current_energy*/, bool water, bool geo, bool canBuild)
1365 {
1366 	UnitTypeStatic *unit;
1367 
1368 	int best_unit = 0;
1369 
1370 	float best_ranking = -10000, my_ranking;
1371 
1372 	//debug
1373 	//ai->Log("Selecting power plant:     power %f    cost %f    urgency %f   energy %f \n", power, cost, urgency, current_energy);
1374 
1375 	for(list<int>::iterator pplant = units_of_category[POWER_PLANT][side-1].begin(); pplant != units_of_category[POWER_PLANT][side-1].end(); ++pplant)
1376 	{
1377 		unit = &units_static[*pplant];
1378 
1379 		if(canBuild && units_dynamic[*pplant].constructorsAvailable <= 0)
1380 			my_ranking = -10000;
1381 		else if(!geo && GetUnitDef(*pplant).needGeo)
1382 			my_ranking = -10000;
1383 		else if( (!water && GetUnitDef(*pplant).minWaterDepth <= 0) || (water && GetUnitDef(*pplant).minWaterDepth > 0) )
1384 		{
1385 			my_ranking = cost * unit->efficiency[1] / max_pplant_eff[side-1] + power * unit->efficiency[0] / max_value[POWER_PLANT][side-1]
1386 						- urgency * (GetUnitDef(*pplant).buildTime / max_buildtime[POWER_PLANT][side-1]);
1387 
1388 			//
1389 			if(unit->cost >= max_cost[POWER_PLANT][side-1])
1390 				my_ranking -= (cost + urgency + power)/2.0f;
1391 
1392 			//ai->Log("%-20s: %f\n", GetUnitDef(*pplant)->humanName.c_str(), my_ranking);
1393 		}
1394 		else
1395 			my_ranking = -10000;
1396 
1397 		if(my_ranking > best_ranking)
1398 		{
1399 				best_ranking = my_ranking;
1400 				best_unit = *pplant;
1401 		}
1402 	}
1403 
1404 	// 0 if no unit found (list was probably empty)
1405 	return best_unit;
1406 }
1407 
GetMex(int side,float cost,float effiency,bool armed,bool water,bool canBuild)1408 int AAIBuildTable::GetMex(int side, float cost, float effiency, bool armed, bool water, bool canBuild)
1409 {
1410 	int best_unit = 0;
1411 	float best_ranking = -10000, my_ranking;
1412 
1413 	side -= 1;
1414 
1415 	for(list<int>::iterator i = units_of_category[EXTRACTOR][side].begin(); i != units_of_category[EXTRACTOR][side].end(); ++i)
1416 	{
1417 		if(canBuild && units_dynamic[*i].constructorsAvailable <= 0)
1418 			my_ranking = -10000;
1419 		// check if under water or ground || water = true and building under water
1420 		else if( ( (!water) && GetUnitDef(*i).minWaterDepth <= 0 ) || ( water && GetUnitDef(*i).minWaterDepth > 0 ) )
1421 		{
1422 			my_ranking = effiency * (GetUnitDef(*i).extractsMetal - avg_value[EXTRACTOR][side]) / max_value[EXTRACTOR][side]
1423 						- cost * (units_static[*i].cost - avg_cost[EXTRACTOR][side]) / max_cost[EXTRACTOR][side];
1424 
1425 			if(armed && !GetUnitDef(*i).weapons.empty())
1426 				my_ranking += 1;
1427 		}
1428 		else
1429 			my_ranking = -10000;
1430 
1431 		if(my_ranking > best_ranking)
1432 		{
1433 			best_ranking = my_ranking;
1434 			best_unit = *i;
1435 		}
1436 	}
1437 
1438 	// 0 if no unit found (list was probably empty)
1439 	return best_unit;
1440 }
1441 
GetBiggestMex()1442 int AAIBuildTable::GetBiggestMex()
1443 {
1444 	int biggest_mex = 0, biggest_yard_map = 0;
1445 
1446 	for(int s = 0; s < cfg->SIDES; ++s)
1447 	{
1448 		for(list<int>::iterator mex = units_of_category[EXTRACTOR][s].begin();  mex != units_of_category[EXTRACTOR][s].end(); ++mex)
1449 		{
1450 			if(GetUnitDef(*mex).xsize * GetUnitDef(*mex).zsize > biggest_yard_map)
1451 			{
1452 				biggest_yard_map = GetUnitDef(*mex).xsize * GetUnitDef(*mex).zsize;
1453 				biggest_mex = *mex;
1454 			}
1455 		}
1456 	}
1457 
1458 	return biggest_mex;
1459 }
1460 
GetStorage(int side,float cost,float metal,float energy,float urgency,bool water,bool canBuild)1461 int AAIBuildTable::GetStorage(int side, float cost, float metal, float energy, float urgency, bool water, bool canBuild)
1462 {
1463 	int best_storage = 0;
1464 	float best_rating = 0, my_rating;
1465 
1466 	for(list<int>::iterator storage = units_of_category[STORAGE][side-1].begin(); storage != units_of_category[STORAGE][side-1].end(); ++storage)
1467 	{
1468 		if(canBuild && units_dynamic[*storage].constructorsAvailable <= 0)
1469 			my_rating = 0;
1470 		else if(!water && GetUnitDef(*storage).minWaterDepth <= 0)
1471 		{
1472 			my_rating = (metal * GetUnitDef(*storage).metalStorage + energy * GetUnitDef(*storage).energyStorage)
1473 				/(cost * units_static[*storage].cost + urgency * GetUnitDef(*storage).buildTime);
1474 		}
1475 		else if(water && GetUnitDef(*storage).minWaterDepth > 0)
1476 		{
1477 			my_rating = (metal * GetUnitDef(*storage).metalStorage + energy * GetUnitDef(*storage).energyStorage)
1478 				/(cost * units_static[*storage].cost + urgency * GetUnitDef(*storage).buildTime);
1479 		}
1480 		else
1481 			my_rating = 0;
1482 
1483 
1484 		if(my_rating > best_rating)
1485 		{
1486 			best_rating = my_rating;
1487 			best_storage = *storage;
1488 		}
1489 	}
1490 
1491 	return best_storage;
1492 }
1493 
GetMetalMaker(int side,float cost,float efficiency,float metal,float urgency,bool water,bool canBuild)1494 int AAIBuildTable::GetMetalMaker(int side, float cost, float efficiency, float metal, float urgency, bool water, bool canBuild)
1495 {
1496 	int best_maker = 0;
1497 	float best_rating = 0, my_rating;
1498 
1499 	for(list<int>::iterator maker = units_of_category[METAL_MAKER][side-1].begin(); maker != units_of_category[METAL_MAKER][side-1].end(); ++maker)
1500 	{
1501 
1502 		//ai->LogConsole("MakesMetal: %f", GetUnitDef(*maker).makesMetal);
1503 		//this somehow got broken in spring... :(
1504 		float makesMetal = GetUnitDef(*maker).makesMetal;
1505 		if (makesMetal <= 0.1f) {
1506 			makesMetal = 12.0f/600.0f;
1507 		}
1508 
1509 		if(canBuild && units_dynamic[*maker].constructorsAvailable <= 0)
1510 			my_rating = 0;
1511 		else if(!water && GetUnitDef(*maker).minWaterDepth <= 0)
1512 		{
1513 			my_rating = (pow((long double) efficiency * units_static[*maker].efficiency[0], (long double) 1.4) + pow((long double) metal * makesMetal, (long double) 1.6))
1514 				/(pow((long double) cost * units_static[*maker].cost,(long double) 1.4) + pow((long double) urgency * GetUnitDef(*maker).buildTime,(long double) 1.4));
1515 		}
1516 		else if(water && GetUnitDef(*maker).minWaterDepth > 0)
1517 		{
1518 			my_rating = (pow((long double) efficiency * units_static[*maker].efficiency[0], (long double) 1.4) + pow((long double) metal * makesMetal, (long double) 1.6))
1519 				/(pow((long double) cost * units_static[*maker].cost,(long double) 1.4) + pow((long double) urgency * GetUnitDef(*maker).buildTime,(long double) 1.4));
1520 		}
1521 		else
1522 			my_rating = 0;
1523 
1524 
1525 		if(my_rating > best_rating)
1526 		{
1527 			best_rating = my_rating;
1528 			best_maker = *maker;
1529 		}
1530 	}
1531 
1532 	return best_maker;
1533 }
1534 
GetDefenceBuilding(int side,double efficiency,double combat_power,double cost,double ground_eff,double air_eff,double hover_eff,double sea_eff,double submarine_eff,double urgency,double range,int randomness,bool water,bool canBuild)1535 int AAIBuildTable::GetDefenceBuilding(int side, double efficiency, double combat_power, double cost, double ground_eff, double air_eff, double hover_eff, double sea_eff, double submarine_eff, double urgency, double range, int randomness, bool water, bool canBuild)
1536 {
1537 	--side;
1538 
1539 	double best_ranking = -100000, my_ranking;
1540 	int best_defence = 0;
1541 
1542 	UnitTypeStatic *unit;
1543 
1544 	double my_power;
1545 
1546 	double total_eff = ground_eff + air_eff + hover_eff + sea_eff + submarine_eff;
1547 	double max_eff_selection = 0;
1548 	double max_power = 0;
1549 
1550 	int k = 0;
1551 
1552 	// use my_power as temp var
1553 	for(list<int>::iterator defence = units_of_category[STATIONARY_DEF][side].begin(); defence != units_of_category[STATIONARY_DEF][side].end(); ++defence)
1554 	{
1555 		if(!canBuild || units_dynamic[*defence].constructorsAvailable > 0)
1556 		{
1557 			unit = &units_static[*defence];
1558 
1559 			// calculate eff.
1560 			my_power = ground_eff * unit->efficiency[0] / max_eff[side][5][0] + air_eff * unit->efficiency[1] / max_eff[side][5][1]
1561 					+ hover_eff * unit->efficiency[2] / max_eff[side][5][2] + sea_eff * unit->efficiency[3] / max_eff[side][5][3]
1562 					+ submarine_eff * unit->efficiency[4] / max_eff[side][5][4];
1563 			my_power /= total_eff;
1564 
1565 			// store result
1566 			def_power[side][k] = my_power;
1567 
1568 			if(my_power > max_power)
1569 				max_power = my_power;
1570 
1571 			// calculate eff
1572 			my_power /= unit->cost;
1573 
1574 			if(my_power > max_eff_selection)
1575 				max_eff_selection = my_power;
1576 
1577 			++k;
1578 		}
1579 	}
1580 
1581 	// something went wrong
1582 	if(max_eff_selection <= 0)
1583 		return 0;
1584 
1585 	//ai->Log("\nSelecting defence: eff %f   power %f   urgency %f  range %f\n", efficiency, combat_power, urgency, range);
1586 
1587 	// reset counter
1588 	k = 0;
1589 
1590 	// calculate rating
1591 	for(list<int>::iterator defence = units_of_category[STATIONARY_DEF][side].begin(); defence != units_of_category[STATIONARY_DEF][side].end(); ++defence)
1592 	{
1593 		if(canBuild && units_dynamic[*defence].constructorsAvailable <= 0)
1594 			my_ranking = -100000;
1595 		else if( (!water && GetUnitDef(*defence).minWaterDepth <= 0) || (water && GetUnitDef(*defence).minWaterDepth > 0) )
1596 		{
1597 			unit = &units_static[*defence];
1598 
1599 			my_ranking = efficiency * (def_power[side][k] / unit->cost) / max_eff_selection
1600 						+ combat_power * def_power[side][k] / max_power
1601 						+ range * unit->range / max_value[STATIONARY_DEF][side]
1602 						- cost * unit->cost / max_cost[STATIONARY_DEF][side]
1603 						- urgency * GetUnitDef(*defence).buildTime / max_buildtime[STATIONARY_DEF][side];
1604 
1605 			my_ranking += (0.1 * ((double)(rand()%randomness)));
1606 
1607 			//ai->Log("%-20s: %f %f %f %f %f\n", GetUnitDef(unit->id).humanName.c_str(), t1, t2, t3, t4, my_ranking);
1608 		}
1609 		else
1610 			my_ranking = -100000;
1611 
1612 		if(my_ranking > best_ranking)
1613 		{
1614 			best_ranking = my_ranking;
1615 			best_defence = *defence;
1616 		}
1617 
1618 		++k;
1619 	}
1620 
1621 	return best_defence;
1622 }
1623 
GetCheapDefenceBuilding(int side,double efficiency,double combat_power,double cost,double urgency,double ground_eff,double air_eff,double hover_eff,double sea_eff,double submarine_eff,bool water)1624 int AAIBuildTable::GetCheapDefenceBuilding(int side, double efficiency, double combat_power, double cost, double urgency, double ground_eff, double air_eff, double hover_eff, double sea_eff, double submarine_eff, bool water)
1625 {
1626 	--side;
1627 
1628 	double best_ranking = -100000, my_ranking;
1629 	int best_defence = 0;
1630 
1631 	UnitTypeStatic *unit;
1632 
1633 	double my_power;
1634 
1635 	double total_eff = ground_eff + air_eff + hover_eff + sea_eff + submarine_eff;
1636 	double max_eff_selection = 0;
1637 	double max_power = 0;
1638 
1639 	unsigned int building_type;
1640 
1641 	if(water)
1642 		building_type = MOVE_TYPE_STATIC_WATER;
1643 	else
1644 		building_type = MOVE_TYPE_STATIC_LAND;
1645 
1646 	int k = 0;
1647 
1648 	// use my_power as temp var
1649 	for(list<int>::iterator defence = units_of_category[STATIONARY_DEF][side].begin(); defence != units_of_category[STATIONARY_DEF][side].end(); ++defence)
1650 	{
1651 		if( units_dynamic[*defence].constructorsAvailable > 0 && building_type & units_static[*defence].movement_type)
1652 		{
1653 			unit = &units_static[*defence];
1654 
1655 			// calculate eff.
1656 			my_power = ground_eff * unit->efficiency[0] / avg_eff[side][5][0] + air_eff * unit->efficiency[1] / avg_eff[side][5][1]
1657 					+ hover_eff * unit->efficiency[2] / avg_eff[side][5][2] + sea_eff * unit->efficiency[3] / avg_eff[side][5][3]
1658 					+ submarine_eff * unit->efficiency[4] / avg_eff[side][5][4];
1659 			my_power /= total_eff;
1660 
1661 			// store result
1662 			def_power[side][k] = my_power;
1663 
1664 			if(my_power > max_power)
1665 				max_power = my_power;
1666 
1667 			// calculate eff
1668 			my_power /= unit->cost;
1669 
1670 			if(my_power > max_eff_selection)
1671 				max_eff_selection = my_power;
1672 
1673 			++k;
1674 		}
1675 	}
1676 
1677 	// something went wrong
1678 	if(max_eff_selection <= 0)
1679 		return 0;
1680 
1681 	// reset counter
1682 	k = 0;
1683 
1684 	// calculate rating
1685 	for(list<int>::iterator defence = units_of_category[STATIONARY_DEF][side].begin(); defence != units_of_category[STATIONARY_DEF][side].end(); ++defence)
1686 	{
1687 		if( units_dynamic[*defence].constructorsAvailable > 0 && building_type & units_static[*defence].movement_type)
1688 		{
1689 			unit = &units_static[*defence];
1690 
1691 			my_ranking = efficiency * (def_power[side][k] / unit->cost) / max_eff_selection
1692 						+ combat_power * def_power[side][k] / max_power
1693 						- cost * unit->cost / avg_cost[STATIONARY_DEF][side]
1694 						- urgency * GetUnitDef(*defence).buildTime / max_buildtime[STATIONARY_DEF][side];
1695 
1696 			if(my_ranking > best_ranking)
1697 			{
1698 				best_ranking = my_ranking;
1699 				best_defence = *defence;
1700 			}
1701 
1702 			++k;
1703 			//ai->Log("%-20s: %f %f %f %f %f\n", GetUnitDef(unit->id).humanName.c_str(), t1, t2, t3, t4, my_ranking);
1704 		}
1705 	}
1706 
1707 	return best_defence;
1708 }
1709 
GetRandomDefence(int side,UnitCategory)1710 int AAIBuildTable::GetRandomDefence(int side, UnitCategory /*category*/)
1711 {
1712 	float best_rating = 0, my_rating;
1713 
1714 	int best_defence = 0;
1715 
1716 	for(list<int>::iterator i = units_of_category[STATIONARY_DEF][side-1].begin(); i != units_of_category[STATIONARY_DEF][side-1].end(); ++i)
1717 	{
1718 		my_rating = rand()%512;
1719 
1720 		if(my_rating >best_rating)
1721 		{
1722 			if(GetUnitDef(*i).metalCost < cfg->MAX_METAL_COST)
1723 			{
1724 				best_defence = *i;
1725 				best_rating = my_rating;
1726 			}
1727 		}
1728 	}
1729 	return best_defence;
1730 }
1731 
GetAirBase(int side,float,bool water,bool canBuild)1732 int AAIBuildTable::GetAirBase(int side, float /*cost*/, bool water, bool canBuild)
1733 {
1734 	float best_ranking = 0, my_ranking;
1735 	int best_airbase = 0;
1736 
1737 	for(list<int>::iterator airbase = units_of_category[AIR_BASE][side-1].begin(); airbase != units_of_category[AIR_BASE][side-1].end(); ++airbase)
1738 	{
1739 		// check if water
1740 		if(canBuild && units_dynamic[*airbase].constructorsAvailable <= 0)
1741 			my_ranking = 0;
1742 		else if(!water && GetUnitDef(*airbase).minWaterDepth <= 0)
1743 		{
1744 			my_ranking = 100.f / (units_dynamic[*airbase].active + 1);
1745 		}
1746 		else if(water && GetUnitDef(*airbase).minWaterDepth > 0)
1747 		{
1748 			//my_ranking =  100 / (cost * units_static[*airbase].cost);
1749 			my_ranking = 100.f / (units_dynamic[*airbase].active + 1);
1750 		}
1751 		else
1752 			my_ranking = 0;
1753 
1754 		if(my_ranking > best_ranking)
1755 		{
1756 			best_ranking = my_ranking;
1757 			best_airbase = *airbase;
1758 		}
1759 	}
1760 	return best_airbase;
1761 }
1762 
GetStationaryArty(int side,float cost,float range,float efficiency,bool water,bool canBuild)1763 int AAIBuildTable::GetStationaryArty(int side, float cost, float range, float efficiency, bool water, bool canBuild)
1764 {
1765 	float best_ranking = 0, my_ranking;
1766 	int best_arty = 0;
1767 
1768 	for(list<int>::iterator arty = units_of_category[STATIONARY_ARTY][side-1].begin(); arty != units_of_category[STATIONARY_ARTY][side-1].end(); ++arty)
1769 	{
1770 		// check if water
1771 		if(canBuild && units_dynamic[*arty].constructorsAvailable <= 0)
1772 			my_ranking = 0;
1773 		else if(!water && GetUnitDef(*arty).minWaterDepth <= 0)
1774 		{
1775 			my_ranking =  (range * units_static[*arty].efficiency[1] + efficiency * units_static[*arty].efficiency[0]) / (cost * units_static[*arty].cost);
1776 		}
1777 		else if(water && GetUnitDef(*arty).minWaterDepth > 0)
1778 		{
1779 			my_ranking =  (range * units_static[*arty].efficiency[1] + efficiency * units_static[*arty].efficiency[0]) / (cost * units_static[*arty].cost);
1780 		}
1781 		else
1782 			my_ranking = 0;
1783 
1784 		if(my_ranking > best_ranking)
1785 		{
1786 			best_ranking = my_ranking;
1787 			best_arty = *arty;
1788 		}
1789 	}
1790 	return best_arty;
1791 }
1792 
GetRadar(int side,float cost,float range,bool water,bool canBuild)1793 int AAIBuildTable::GetRadar(int side, float cost, float range, bool water, bool canBuild)
1794 {
1795 	int best_radar = 0;
1796 	float my_rating, best_rating = -10000;
1797 	side -= 1;
1798 
1799 	for(list<int>::iterator i = units_of_category[STATIONARY_RECON][side].begin(); i != units_of_category[STATIONARY_RECON][side].end(); ++i)
1800 	{
1801 		if(GetUnitDef(*i).radarRadius > 0)
1802 		{
1803 			if(canBuild && units_dynamic[*i].constructorsAvailable <= 0)
1804 				my_rating = -10000;
1805 			else if(water && GetUnitDef(*i).minWaterDepth > 0)
1806 				my_rating = cost * (avg_cost[STATIONARY_RECON][side] - units_static[*i].cost)/max_cost[STATIONARY_RECON][side]
1807 						+ range * (GetUnitDef(*i).radarRadius - avg_value[STATIONARY_RECON][side])/max_value[STATIONARY_RECON][side];
1808 			else if (!water && GetUnitDef(*i).minWaterDepth <= 0)
1809 				my_rating = cost * (avg_cost[STATIONARY_RECON][side] - units_static[*i].cost)/max_cost[STATIONARY_RECON][side]
1810 						+ range * (GetUnitDef(*i).radarRadius - avg_value[STATIONARY_RECON][side])/max_value[STATIONARY_RECON][side];
1811 			else
1812 				my_rating = -10000;
1813 		}
1814 		else
1815 			my_rating = 0;
1816 
1817 		if(my_rating > best_rating)
1818 		{
1819 			if(GetUnitDef(*i).metalCost < cfg->MAX_METAL_COST)
1820 			{
1821 				best_radar = *i;
1822 				best_rating = my_rating;
1823 			}
1824 		}
1825 	}
1826 
1827 	return best_radar;
1828 }
1829 
GetJammer(int side,float cost,float range,bool water,bool canBuild)1830 int AAIBuildTable::GetJammer(int side, float cost, float range, bool water, bool canBuild)
1831 {
1832 	int best_jammer = 0;
1833 	float my_rating, best_rating = -10000;
1834 	side -= 1;
1835 
1836 	for(list<int>::iterator i = units_of_category[STATIONARY_JAMMER][side].begin(); i != units_of_category[STATIONARY_JAMMER][side].end(); ++i)
1837 	{
1838 		if(canBuild && units_dynamic[*i].constructorsAvailable <= 0)
1839 			my_rating = -10000;
1840 		else if(water && GetUnitDef(*i).minWaterDepth > 0)
1841 			my_rating = cost * (avg_cost[STATIONARY_JAMMER][side] - units_static[*i].cost)/max_cost[STATIONARY_JAMMER][side]
1842 						+ range * (GetUnitDef(*i).jammerRadius - avg_value[STATIONARY_JAMMER][side])/max_value[STATIONARY_JAMMER][side];
1843 		else if (!water &&  GetUnitDef(*i).minWaterDepth <= 0)
1844 			my_rating = cost * (avg_cost[STATIONARY_JAMMER][side] - units_static[*i].cost)/max_cost[STATIONARY_JAMMER][side]
1845 						+ range * (GetUnitDef(*i).jammerRadius - avg_value[STATIONARY_JAMMER][side])/max_value[STATIONARY_JAMMER][side];
1846 		else
1847 			my_rating = -10000;
1848 
1849 
1850 		if(my_rating > best_rating)
1851 		{
1852 			if(GetUnitDef(*i).metalCost < cfg->MAX_METAL_COST)
1853 			{
1854 				best_jammer = *i;
1855 				best_rating = my_rating;
1856 			}
1857 		}
1858 	}
1859 
1860 	return best_jammer;
1861 }
1862 
GetScout(int side,float los,float cost,unsigned int allowed_movement_types,int randomness,bool cloakable,bool canBuild)1863 int AAIBuildTable::GetScout(int side, float los, float cost, unsigned int allowed_movement_types, int randomness, bool cloakable, bool canBuild)
1864 {
1865 	side -= 1;
1866 
1867 	float best_ranking = -10000, my_ranking;
1868 	int best_scout = 0;
1869 
1870 	for(list<int>::iterator i = units_of_category[SCOUT][side].begin(); i != units_of_category[SCOUT][side].end(); ++i)
1871 	{
1872 		if(units_static[*i].movement_type & allowed_movement_types)
1873 		{
1874 			if(!canBuild || (canBuild && units_dynamic[*i].constructorsAvailable > 0))
1875 			{
1876 				my_ranking = los * ( GetUnitDef(*i).losRadius - avg_value[SCOUT][side]) / max_value[SCOUT][side];
1877 				my_ranking += cost * (avg_cost[SCOUT][side] - units_static[*i].cost) / max_cost[SCOUT][side];
1878 
1879 				if(cloakable && GetUnitDef(*i).canCloak)
1880 					my_ranking += 8.0f;
1881 
1882 				my_ranking *= (1 + 0.05 * ((float)(rand()%randomness)));
1883 
1884 				if(my_ranking > best_ranking)
1885 				{
1886 					best_ranking = my_ranking;
1887 					best_scout = *i;
1888 				}
1889 			}
1890 		}
1891 	}
1892 
1893 
1894 	return best_scout;
1895 }
1896 
GetRandomUnit(list<int> unit_list)1897 int AAIBuildTable::GetRandomUnit(list<int> unit_list)
1898 {
1899 	float best_rating = 0, my_rating;
1900 
1901 	int best_unit = 0;
1902 
1903 	for(list<int>::iterator i = unit_list.begin(); i != unit_list.end(); ++i)
1904 	{
1905 		my_rating = rand()%512;
1906 
1907 		if(my_rating >best_rating)
1908 		{
1909 			if(GetUnitDef(*i).metalCost < cfg->MAX_METAL_COST)
1910 			{
1911 				best_unit = *i;
1912 				best_rating = my_rating;
1913 			}
1914 		}
1915 	}
1916 	return best_unit;
1917 }
1918 
GetGroundAssault(int side,float power,float gr_eff,float air_eff,float hover_eff,float sea_eff,float stat_eff,float efficiency,float speed,float range,float cost,int randomness,bool canBuild)1919 int AAIBuildTable::GetGroundAssault(int side, float power, float gr_eff, float air_eff, float hover_eff, float sea_eff, float stat_eff, float efficiency, float speed, float range, float cost, int randomness, bool canBuild)
1920 {
1921 	--side;
1922 
1923 	float best_ranking = -10000, my_ranking;
1924 	int best_unit = 0;
1925 
1926 	float max_cost = this->max_cost[GROUND_ASSAULT][side];
1927 	float max_range = max_value[GROUND_ASSAULT][side];
1928 	float max_speed = this->max_speed[0][side];
1929 
1930 	float max_power = 0;
1931 	float max_efficiency  = 0;
1932 
1933 	UnitTypeStatic *unit;
1934 
1935 	// precache eff
1936 	int c = 0;
1937 
1938 	for(list<int>::iterator i = units_of_category[GROUND_ASSAULT][side].begin(); i != units_of_category[GROUND_ASSAULT][side].end(); ++i)
1939 	{
1940 		unit = &units_static[*i];
1941 
1942 		combat_eff[c] = gr_eff * unit->efficiency[0] + air_eff * unit->efficiency[1] + hover_eff * unit->efficiency[2]
1943 						+ sea_eff * unit->efficiency[3] + stat_eff * unit->efficiency[5];
1944 
1945 		if(combat_eff[c] > max_power)
1946 			max_power = combat_eff[c];
1947 
1948 		if(combat_eff[c] / unit->cost > max_efficiency)
1949 			max_efficiency = combat_eff[c] / unit->cost;
1950 
1951 		++c;
1952 	}
1953 
1954 	c = 0;
1955 
1956 	if(max_power <= 0)
1957 		max_power = 1;
1958 
1959 	if(max_efficiency <= 0)
1960 		max_efficiency = 1;
1961 
1962 	// TODO: improve algorithm
1963 	for(list<int>::iterator i = units_of_category[GROUND_ASSAULT][side].begin(); i != units_of_category[GROUND_ASSAULT][side].end(); ++i)
1964 	{
1965 		unit = &units_static[*i];
1966 
1967 		if(canBuild && units_dynamic[*i].constructorsAvailable > 0)
1968 		{
1969 			my_ranking = power * combat_eff[c] / max_power;
1970 			my_ranking -= cost * unit->cost / max_cost;
1971 			my_ranking += efficiency * (combat_eff[c] / unit->cost) / max_efficiency;
1972 			my_ranking += range * unit->range / max_range;
1973 			my_ranking += speed * GetUnitDef(*i).speed / max_speed;
1974 			my_ranking += 0.1f * ((float)(rand()%randomness));
1975 		}
1976 		else if(!canBuild)
1977 		{
1978 			my_ranking = power * combat_eff[c] / max_power;
1979 			my_ranking -= cost * unit->cost / max_cost;
1980 			my_ranking += efficiency * (combat_eff[c] / unit->cost) / max_efficiency;
1981 			my_ranking += range * unit->range / max_range;
1982 			my_ranking += speed * GetUnitDef(*i).speed / max_speed;
1983 			my_ranking += 0.1f * ((float)(rand()%randomness));
1984 		}
1985 		else
1986 			my_ranking = -10000;
1987 
1988 
1989 		if(my_ranking > best_ranking)
1990 		{
1991 			// check max metal cost
1992 			if(GetUnitDef(*i).metalCost < cfg->MAX_METAL_COST)
1993 			{
1994 				best_ranking = my_ranking;
1995 				best_unit = *i;
1996 			}
1997 		}
1998 
1999 		++c;
2000 	}
2001 
2002 	return best_unit;
2003 }
2004 
GetHoverAssault(int side,float power,float gr_eff,float air_eff,float hover_eff,float sea_eff,float stat_eff,float efficiency,float speed,float range,float cost,int randomness,bool canBuild)2005 int AAIBuildTable::GetHoverAssault(int side,  float power, float gr_eff, float air_eff, float hover_eff, float sea_eff, float stat_eff, float efficiency, float speed, float range, float cost, int randomness, bool canBuild)
2006 {
2007 	UnitTypeStatic *unit;
2008 
2009 	--side;
2010 
2011 	float best_ranking = -10000, my_ranking;
2012 	int best_unit = 0;
2013 
2014 	int c = 0;
2015 
2016 	float max_cost = this->max_cost[HOVER_ASSAULT][side];
2017 	float max_range = max_value[HOVER_ASSAULT][side];
2018 	float max_speed = this->max_speed[2][side];
2019 
2020 	float max_power = 0;
2021 	float max_efficiency  = 0;
2022 
2023 	// precache eff
2024 	for(list<int>::iterator i = units_of_category[HOVER_ASSAULT][side].begin(); i != units_of_category[HOVER_ASSAULT][side].end(); ++i)
2025 	{
2026 		unit = &units_static[*i];
2027 
2028 		combat_eff[c] = gr_eff * unit->efficiency[0] + air_eff * unit->efficiency[1] + hover_eff * unit->efficiency[2]
2029 						+ sea_eff * unit->efficiency[3] + stat_eff * unit->efficiency[5];
2030 
2031 		if(combat_eff[c] > max_power)
2032 			max_power = combat_eff[c];
2033 
2034 		if(combat_eff[c] / unit->cost > max_efficiency)
2035 			max_efficiency = combat_eff[c] / unit->cost;
2036 
2037 		++c;
2038 	}
2039 
2040 	c = 0;
2041 
2042 	if(max_power <= 0)
2043 		max_power = 1;
2044 
2045 	if(max_efficiency <= 0)
2046 		max_efficiency = 0;
2047 
2048 	for(list<int>::iterator i = units_of_category[HOVER_ASSAULT][side].begin(); i != units_of_category[HOVER_ASSAULT][side].end(); ++i)
2049 	{
2050 		unit = &units_static[*i];
2051 
2052 		if(canBuild && units_dynamic[*i].constructorsAvailable > 0)
2053 		{
2054 			my_ranking = power * combat_eff[c] / max_power;
2055 			my_ranking -= cost * unit->cost / max_cost;
2056 			my_ranking += efficiency * (combat_eff[c] / unit->cost) / max_efficiency;
2057 			my_ranking += range * unit->range / max_range;
2058 			my_ranking += speed * GetUnitDef(*i).speed / max_speed;
2059 			my_ranking += 0.1f * ((float)(rand()%randomness));
2060 		}
2061 		else if(!canBuild)
2062 		{
2063 			my_ranking = power * combat_eff[c] / max_power;
2064 			my_ranking -= cost * unit->cost / max_cost;
2065 			my_ranking += efficiency * (combat_eff[c] / unit->cost) / max_efficiency;
2066 			my_ranking += range * unit->range / max_range;
2067 			my_ranking += speed * GetUnitDef(*i).speed / max_speed;
2068 			my_ranking += 0.1f * ((float)(rand()%randomness));
2069 		}
2070 		else
2071 			my_ranking = -10000;
2072 
2073 		if(my_ranking > best_ranking)
2074 		{
2075 			// check max metal cost
2076 			if(GetUnitDef(*i).metalCost < cfg->MAX_METAL_COST)
2077 			{
2078 				best_ranking = my_ranking;
2079 				best_unit = *i;
2080 			}
2081 		}
2082 	}
2083 
2084 	return best_unit;
2085 }
2086 
2087 
GetAirAssault(int side,float power,float gr_eff,float air_eff,float hover_eff,float sea_eff,float stat_eff,float efficiency,float speed,float range,float cost,int randomness,bool canBuild)2088 int AAIBuildTable::GetAirAssault(int side,  float power, float gr_eff, float air_eff, float hover_eff, float sea_eff, float stat_eff, float efficiency, float speed, float range, float cost, int randomness, bool canBuild)
2089 {
2090 	UnitTypeStatic *unit;
2091 
2092 	--side;
2093 
2094 	float best_ranking = -10000, my_ranking;
2095 	int best_unit = 0;
2096 
2097 	int c = 0;
2098 
2099 	float max_cost = this->max_cost[AIR_ASSAULT][side];
2100 	float max_range = max_value[AIR_ASSAULT][side];
2101 	float max_speed = this->max_speed[1][side];
2102 
2103 	float max_power = 0;
2104 	float max_efficiency  = 0;
2105 
2106 	// precache eff
2107 	for(list<int>::iterator i = units_of_category[AIR_ASSAULT][side].begin(); i != units_of_category[AIR_ASSAULT][side].end(); ++i)
2108 	{
2109 		unit = &units_static[*i];
2110 
2111 		combat_eff[c] = gr_eff * unit->efficiency[0] + air_eff * unit->efficiency[1] + hover_eff * unit->efficiency[2]
2112 						+ sea_eff * unit->efficiency[3] + stat_eff * unit->efficiency[5];
2113 
2114 		if(combat_eff[c] > max_power)
2115 			max_power = combat_eff[c];
2116 
2117 		if(combat_eff[c] / unit->cost > max_efficiency)
2118 			max_efficiency = combat_eff[c] / unit->cost;
2119 
2120 		++c;
2121 	}
2122 
2123 	c = 0;
2124 
2125 	if(max_power <= 0)
2126 		max_power = 1;
2127 
2128 	if(max_efficiency <= 0)
2129 		max_efficiency = 0;
2130 
2131 	for(list<int>::iterator i = units_of_category[AIR_ASSAULT][side].begin(); i != units_of_category[AIR_ASSAULT][side].end(); ++i)
2132 	{
2133 		unit = &units_static[*i];
2134 
2135 		if(canBuild && units_dynamic[*i].constructorsAvailable > 0)
2136 		{
2137 			my_ranking = power * combat_eff[c] / max_power;
2138 			my_ranking -= cost * unit->cost / max_cost;
2139 			my_ranking += efficiency * (combat_eff[c] / unit->cost) / max_efficiency;
2140 			my_ranking += range * unit->range / max_range;
2141 			my_ranking += speed * GetUnitDef(*i).speed / max_speed;
2142 			my_ranking += 0.1f * ((float)(rand()%randomness));
2143 		}
2144 		else if(!canBuild)
2145 		{
2146 			my_ranking = power * combat_eff[c] / max_power;
2147 			my_ranking -= cost * unit->cost / max_cost;
2148 			my_ranking += efficiency * (combat_eff[c] / unit->cost) / max_efficiency;
2149 			my_ranking += range * unit->range / max_range;
2150 			my_ranking += speed * GetUnitDef(*i).speed / max_speed;
2151 			my_ranking += 0.1f * ((float)(rand()%randomness));
2152 		}
2153 		else
2154 			my_ranking = 0;
2155 
2156 		if(my_ranking > best_ranking)
2157 		{
2158 			// check max metal cost
2159 			if(GetUnitDef(*i).metalCost < cfg->MAX_METAL_COST)
2160 			{
2161 				best_ranking = my_ranking;
2162 				best_unit = *i;
2163 			}
2164 		}
2165 	}
2166 
2167 	return best_unit;
2168 }
2169 
GetSeaAssault(int side,float power,float gr_eff,float air_eff,float hover_eff,float sea_eff,float submarine_eff,float stat_eff,float efficiency,float speed,float range,float cost,int randomness,bool canBuild)2170 int AAIBuildTable::GetSeaAssault(int side,  float power, float gr_eff, float air_eff, float hover_eff, float sea_eff, float submarine_eff, float stat_eff, float efficiency, float speed, float range, float cost, int randomness, bool canBuild)
2171 {
2172 	UnitTypeStatic *unit;
2173 
2174 	--side;
2175 
2176 	float best_ranking = -10000, my_ranking;
2177 	int best_unit = 0;
2178 
2179 	int c = 0;
2180 
2181 	float max_cost = this->max_cost[SEA_ASSAULT][side];
2182 	float max_range = max_value[SEA_ASSAULT][side];
2183 	float max_speed = this->max_speed[3][side];
2184 
2185 	float max_power = 0;
2186 	float max_efficiency  = 0;
2187 
2188 	// precache eff
2189 	for(list<int>::iterator i = units_of_category[SEA_ASSAULT][side].begin(); i != units_of_category[SEA_ASSAULT][side].end(); ++i)
2190 	{
2191 		unit = &units_static[*i];
2192 
2193 		combat_eff[c] = gr_eff * unit->efficiency[0] + air_eff * unit->efficiency[1] + hover_eff * unit->efficiency[2]
2194 		+ sea_eff * unit->efficiency[3] + submarine_eff * unit->efficiency[4] + stat_eff * unit->efficiency[5];
2195 
2196 		if(combat_eff[c] > max_power)
2197 			max_power = combat_eff[c];
2198 
2199 		if(combat_eff[c] / unit->cost > max_efficiency)
2200 			max_efficiency = combat_eff[c] / unit->cost;
2201 
2202 		++c;
2203 	}
2204 
2205 	c = 0;
2206 
2207 	if(max_power <= 0)
2208 		max_power = 1;
2209 
2210 	if(max_efficiency <= 0)
2211 		max_efficiency = 0;
2212 
2213 	for(list<int>::iterator i = units_of_category[SEA_ASSAULT][side].begin(); i != units_of_category[SEA_ASSAULT][side].end(); ++i)
2214 	{
2215 		unit = &units_static[*i];
2216 
2217 		if(canBuild && units_dynamic[*i].constructorsAvailable > 0)
2218 		{
2219 			my_ranking = power * combat_eff[c] / max_power;
2220 			my_ranking -= cost * unit->cost / max_cost;
2221 			my_ranking += efficiency * (combat_eff[c] / unit->cost) / max_efficiency;
2222 			my_ranking += range * unit->range / max_range;
2223 			my_ranking += speed * GetUnitDef(*i).speed / max_speed;
2224 			my_ranking += 0.1f * ((float)(rand()%randomness));
2225 		}
2226 		else if(!canBuild)
2227 		{
2228 			my_ranking = power * combat_eff[c] / max_power;
2229 			my_ranking -= cost * unit->cost / max_cost;
2230 			my_ranking += efficiency * (combat_eff[c] / unit->cost) / max_efficiency;
2231 			my_ranking += range * unit->range / max_range;
2232 			my_ranking += speed * GetUnitDef(*i).speed / max_speed;
2233 			my_ranking += 0.1f * ((float)(rand()%randomness));
2234 		}
2235 		else
2236 			my_ranking = -10000;
2237 
2238 		if(my_ranking > best_ranking)
2239 		{
2240 			// check max metal cost
2241 			if(GetUnitDef(*i).metalCost < cfg->MAX_METAL_COST)
2242 			{
2243 				best_ranking = my_ranking;
2244 				best_unit = *i;
2245 			}
2246 		}
2247 	}
2248 
2249 	return best_unit;
2250 }
2251 
GetSubmarineAssault(int side,float power,float sea_eff,float submarine_eff,float stat_eff,float efficiency,float speed,float range,float cost,int randomness,bool canBuild)2252 int AAIBuildTable::GetSubmarineAssault(int side, float power, float sea_eff, float submarine_eff, float stat_eff, float efficiency, float speed, float range, float cost, int randomness, bool canBuild)
2253 {
2254 	UnitTypeStatic *unit;
2255 
2256 	--side;
2257 
2258 	float best_ranking = -10000, my_ranking;
2259 	int best_unit = 0;
2260 
2261 	int c = 0;
2262 
2263 	float max_cost = this->max_cost[SUBMARINE_ASSAULT][side];
2264 	float max_range = max_value[SUBMARINE_ASSAULT][side];
2265 	float max_speed = this->max_speed[4][side];
2266 
2267 	float max_power = 0;
2268 	float max_efficiency  = 0;
2269 
2270 	// precache eff
2271 	for(list<int>::iterator i = units_of_category[SUBMARINE_ASSAULT][side].begin(); i != units_of_category[SUBMARINE_ASSAULT][side].end(); ++i)
2272 	{
2273 		unit = &units_static[*i];
2274 
2275 		combat_eff[c] = sea_eff * unit->efficiency[3] + submarine_eff * unit->efficiency[4] + stat_eff * unit->efficiency[5];
2276 
2277 		if(combat_eff[c] > max_power)
2278 			max_power = combat_eff[c];
2279 
2280 		if(combat_eff[c] / unit->cost > max_efficiency)
2281 			max_efficiency = combat_eff[c] / unit->cost;
2282 
2283 		++c;
2284 	}
2285 
2286 	c = 0;
2287 
2288 	if(max_power <= 0)
2289 		max_power = 1;
2290 
2291 	if(max_efficiency <= 0)
2292 		max_efficiency = 0;
2293 
2294 	for(list<int>::iterator i = units_of_category[SUBMARINE_ASSAULT][side].begin(); i != units_of_category[SUBMARINE_ASSAULT][side].end(); ++i)
2295 	{
2296 		unit = &units_static[*i];
2297 
2298 		if(canBuild && units_dynamic[*i].constructorsAvailable > 0)
2299 		{
2300 			my_ranking = power * combat_eff[c] / max_power;
2301 			my_ranking -= cost * unit->cost / max_cost;
2302 			my_ranking += SafeDivide(efficiency * (SafeDivide(combat_eff[c], unit->cost)), max_efficiency);
2303 			my_ranking += range * unit->range / max_range;
2304 			my_ranking += speed * GetUnitDef(*i).speed / max_speed;
2305 			my_ranking += 0.1f * ((float)(rand()%randomness));
2306 		}
2307 		else if(!canBuild)
2308 		{
2309 			my_ranking = power * combat_eff[c] / max_power;
2310 			my_ranking -= cost * unit->cost / max_cost;
2311 			my_ranking += SafeDivide(efficiency * (SafeDivide(combat_eff[c], unit->cost)), max_efficiency);
2312 			my_ranking += range * unit->range / max_range;
2313 			my_ranking += speed * GetUnitDef(*i).speed / max_speed;
2314 			my_ranking += 0.1f * ((float)(rand()%randomness));
2315 		}
2316 		else
2317 			my_ranking = -10000;
2318 
2319 		if(my_ranking > best_ranking)
2320 		{
2321 			// check max metal cost
2322 			if(GetUnitDef(*i).metalCost < cfg->MAX_METAL_COST)
2323 			{
2324 				best_ranking = my_ranking;
2325 				best_unit = *i;
2326 			}
2327 		}
2328 	}
2329 
2330 	return best_unit;
2331 }
2332 
UpdateTable(const UnitDef * def_killer,int killer,const UnitDef * def_killed,int killed)2333 void AAIBuildTable::UpdateTable(const UnitDef* def_killer, int killer, const UnitDef *def_killed, int killed)
2334 {
2335 	// buidling killed
2336 	if(killed == 5)
2337 	{
2338 		// stationary defence killed
2339 		if(units_static[def_killed->id].category == STATIONARY_DEF)
2340 		{
2341 			float change = cfg->LEARN_SPEED * units_static[def_killed->id].efficiency[killer] / units_static[def_killer->id].efficiency[killed];
2342 
2343 			if(change > 0.5)
2344 				change = 0.5;
2345 			else if(change < cfg->MIN_EFFICIENCY/2.0f)
2346 				change = cfg->MIN_EFFICIENCY/2.0f;
2347 
2348 			units_static[def_killer->id].efficiency[killed] += change;
2349 			units_static[def_killed->id].efficiency[killer] -= change;
2350 
2351 			if(units_static[def_killed->id].efficiency[killer] < cfg->MIN_EFFICIENCY)
2352 				units_static[def_killed->id].efficiency[killer] = cfg->MIN_EFFICIENCY;
2353 		}
2354 		// other building killed
2355 		else
2356 		{
2357 			if(units_static[def_killer->id].efficiency[5] < 8)
2358 			{
2359 				if(killer == 1)	// aircraft
2360 					units_static[def_killer->id].efficiency[5] += cfg->LEARN_SPEED / 3.0f;
2361 				else			// other assault units
2362 					units_static[def_killer->id].efficiency[5] += cfg->LEARN_SPEED / 9.0f;
2363 			}
2364 		}
2365 	}
2366 	// unit killed
2367 	else
2368 	{
2369 		float change = cfg->LEARN_SPEED * units_static[def_killed->id].efficiency[killer] / units_static[def_killer->id].efficiency[killed];
2370 
2371 		if(change > 0.5)
2372 			change = 0.5;
2373 		else if(change < cfg->MIN_EFFICIENCY/2.0f)
2374 			change = cfg->MIN_EFFICIENCY/2.0f;
2375 
2376 		units_static[def_killer->id].efficiency[killed] += change;
2377 		units_static[def_killed->id].efficiency[killer] -= change;
2378 
2379 		if(units_static[def_killed->id].efficiency[killer] < cfg->MIN_EFFICIENCY)
2380 			units_static[def_killed->id].efficiency[killer] = cfg->MIN_EFFICIENCY;
2381 	}
2382 }
2383 
UpdateMinMaxAvgEfficiency()2384 void AAIBuildTable::UpdateMinMaxAvgEfficiency()
2385 {
2386 	int counter;
2387 	float min, max, sum;
2388 	UnitCategory killer, killed;
2389 
2390 	for(int side = 0; side < numOfSides; ++side)
2391 	{
2392 		for(int i = 0; i < combat_categories; ++i)
2393 		{
2394 			for(int j = 0; j < combat_categories; ++j)
2395 			{
2396 				killer = GetAssaultCategoryOfID(i);
2397 				killed = GetAssaultCategoryOfID(j);
2398 				counter = 0;
2399 
2400 				// update max and avg efficiency of i versus j
2401 				max = 0;
2402 				min = 100000;
2403 				sum = 0;
2404 
2405 				for(list<int>::iterator unit = units_of_category[killer][side].begin(); unit != units_of_category[killer][side].end(); ++unit)
2406 				{
2407 					// only count anti air units vs air and assault units vs non air
2408 					if( (killed == AIR_ASSAULT && units_static[*unit].unit_type == ANTI_AIR_UNIT) || (killed != AIR_ASSAULT && units_static[*unit].unit_type != ANTI_AIR_UNIT))
2409 					{
2410 						sum += units_static[*unit].efficiency[j];
2411 
2412 						if(units_static[*unit].efficiency[j] > max)
2413 							max = units_static[*unit].efficiency[j];
2414 
2415 						if(units_static[*unit].efficiency[j] < min)
2416 							min = units_static[*unit].efficiency[j];
2417 
2418 						++counter;
2419 					}
2420 				}
2421 
2422 				if(counter > 0)
2423 				{
2424 					avg_eff[side][i][j] = sum / counter;
2425 					max_eff[side][i][j] = max;
2426 					min_eff[side][i][j] = min;
2427 
2428 					total_eff[side][i][j] = max - min;
2429 
2430 					if(total_eff[side][i][j] <= 0)
2431 						total_eff[side][i][j] = 1;
2432 
2433 					if(max_eff[side][i][j] <= 0)
2434 						max_eff[side][i][j] = 1;
2435 
2436 					if(avg_eff[side][i][j] <= 0)
2437 						avg_eff[side][i][j] = 1;
2438 
2439 					if(min_eff[side][i][j] <= 0)
2440 						min_eff[side][i][j] = 1;
2441 				}
2442 				else
2443 				{
2444 					// set to 1 to prevent division by zero crashes
2445 					max_eff[side][i][j] = 1;
2446 					min_eff[side][i][j] = 1;
2447 					avg_eff[side][i][j] = 1;
2448 					total_eff[side][i][j] = 1;
2449 				}
2450 
2451 				//ai->Log("min_eff[%i][%i] %f;  max_eff[%i][%i] %f\n", i, j, this->min_eff[i][j], i, j, this->max_eff[i][j]);
2452 			}
2453 		}
2454 	}
2455 }
2456 
2457 // returns true if unitdef->id can build unit with unitdef->id
CanBuildUnit(int id_builder,int id_unit)2458 bool AAIBuildTable::CanBuildUnit(int id_builder, int id_unit)
2459 {
2460 	// look in build options of builder for unit
2461 	for(list<int>::iterator i = units_static[id_builder].canBuildList.begin(); i != units_static[id_builder].canBuildList.end(); ++i)
2462 	{
2463 		if(*i == id_unit)
2464 			return true;
2465 	}
2466 
2467 	// unit not found in builder�s buildoptions
2468 	return false;
2469 }
2470 
2471 // dtermines sides of units by recursion
CalcBuildTree(int unit)2472 void AAIBuildTable::CalcBuildTree(int unit)
2473 {
2474 	// go through all possible build options and set side if necessary
2475 	for(list<int>::iterator i = units_static[unit].canBuildList.begin(); i != units_static[unit].canBuildList.end(); ++i)
2476 	{
2477 		// add this unit to targets builtby-list
2478 		units_static[*i].builtByList.push_back(unit);
2479 
2480 		// calculate builder_costs
2481 		if( units_static[unit].cost < units_static[*i].builder_cost || units_static[*i].builder_cost <= 0)
2482 			units_static[*i].builder_cost = units_static[unit].cost;
2483 
2484 		// continue with all builldoptions (if they have not been visited yet)
2485 		if(!units_static[*i].side && AllowedToBuild(*i))
2486 		{
2487 			// unit has not been checked yet, set side as side of its builder and continue
2488 			units_static[*i].side = units_static[unit].side;
2489 
2490 			CalcBuildTree(*i);
2491 		}
2492 	}
2493 }
2494 
GetBuildCacheFileName()2495 std::string AAIBuildTable::GetBuildCacheFileName()
2496 {
2497 	return cfg->GetFileName(cfg->getUniqueName(true, true, true, true), MOD_LEARN_PATH, "_buildcache.txt", true);
2498 }
2499 
2500 
2501 // returns true if cache found
LoadBuildTable()2502 bool AAIBuildTable::LoadBuildTable()
2503 {
2504 	// stop further loading if already done
2505 	if(!units_static.empty())
2506 	{
2507 		units_dynamic.resize(unitList.size());
2508 
2509 		for(int i = 0; i < unitList.size(); ++i)
2510 		{
2511 			units_dynamic[i].active = 0;
2512 			units_dynamic[i].requested = 0;
2513 			units_dynamic[i].constructorsAvailable = 0;
2514 			units_dynamic[i].constructorsRequested = 0;
2515 		}
2516 
2517 		return true;
2518 	} else {
2519 		// load data
2520 
2521 		FILE *load_file;
2522 
2523 		int tmp = 0, cat = 0;
2524 		size_t bo = 0, bb = 0;
2525 		const std::string filename = GetBuildCacheFileName();
2526 		// load units if file exists
2527 		if((load_file = fopen(filename.c_str(), "r")))
2528 		{
2529 			char buffer[1024];
2530 			// check if correct version
2531 			fscanf(load_file, "%s", buffer);
2532 
2533 			if(strcmp(buffer, MOD_LEARN_VERSION))
2534 			{
2535 				ai->LogConsole("Buildtable version out of date - creating new one");
2536 				return false;
2537 			}
2538 
2539 			int counter = 0;
2540 
2541 			// load attacked_by table
2542 			for(int map = 0; map <= (int)WATER_MAP; ++map)
2543 			{
2544 				for(int t = 0; t < 4; ++t)
2545 				{
2546 					for(int category = 0; category < combat_categories; ++category)
2547 					{
2548 						++counter;
2549 						fscanf(load_file, "%f ", &attacked_by_category_learned[map][t][category]);
2550 					}
2551 				}
2552 			}
2553 
2554 			units_static.resize(unitList.size());
2555 			units_dynamic.resize(unitList.size());
2556 			fixed_eff.resize(unitList.size(), vector<float>(combat_categories));
2557 
2558 			units_static[0].def_id = 0;
2559 			units_static[0].side = 0;
2560 
2561 			for(int i = 1; i < unitList.size(); ++i)
2562 			{
2563 				fscanf(load_file, "%i %i %u %u %f %f %f %i " _STPF_ " " _STPF_ " ",&units_static[i].def_id, &units_static[i].side,
2564 									&units_static[i].unit_type, &units_static[i].movement_type,
2565 									&units_static[i].range, &units_static[i].cost, &units_static[i].builder_cost,
2566 									&cat, &bo, &bb);
2567 
2568 				// get memory for eff
2569 				units_static[i].efficiency.resize(combat_categories);
2570 
2571 				// load eff
2572 				for(int k = 0; k < combat_categories; ++k)
2573 				{
2574 					fscanf(load_file, "%f ", &units_static[i].efficiency[k]);
2575 					fixed_eff[i][k] = units_static[i].efficiency[k];
2576 				}
2577 
2578 				units_static[i].category = (UnitCategory) cat;
2579 
2580 				units_dynamic[i].active = 0;
2581 				units_dynamic[i].requested = 0;
2582 				units_dynamic[i].constructorsAvailable = 0;
2583 				units_dynamic[i].constructorsRequested = 0;
2584 
2585 				// load buildoptions
2586 				for(size_t j = 0; j < bo; j++)
2587 				{
2588 					fscanf(load_file, "%i ", &tmp);
2589 					units_static[i].canBuildList.push_back(tmp);
2590 				}
2591 
2592 				// load builtby-list
2593 				for(size_t k = 0; k < bb; ++k)
2594 				{
2595 					fscanf(load_file, "%i ", &tmp);
2596 					units_static[i].builtByList.push_back(tmp);
2597 				}
2598 			}
2599 
2600 			// now load unit lists
2601 			for(int s = 0; s < numOfSides; ++s)
2602 			{
2603 				for(int cat = 0; cat <= MOBILE_CONSTRUCTOR; ++cat)
2604 				{
2605 					// load number of buildoptions
2606 					fscanf(load_file,  _STPF_ " ", &bo);
2607 
2608 					for(size_t i = 0; i < bo; ++i)
2609 					{
2610 						fscanf(load_file, "%i ", &tmp);
2611 						units_of_category[cat][s].push_back(tmp);
2612 					}
2613 
2614 					// load pre cached values
2615 					fscanf(load_file, "%f %f %f %f %f %f %f %f %f \n",
2616 						&max_cost[cat][s], &min_cost[cat][s], &avg_cost[cat][s],
2617 						&max_buildtime[cat][s], &min_buildtime[cat][s], &avg_buildtime[cat][s],
2618 						&max_value[cat][s], &min_value[cat][s], &avg_value[cat][s]);
2619 				}
2620 
2621 				// load cached speed stats
2622 				for(int cat = 0; cat < combat_categories; ++cat)
2623 				{
2624 					fscanf(load_file, "%f %f %f %f \n", &min_speed[cat][s], &max_speed[cat][s], &group_speed[cat][s], &avg_speed[cat][s]);
2625 				}
2626 
2627 				// update group speed (in case UNIT_SPEED_SUBGROUPS changed)
2628 				for(int cat = 0; cat < combat_categories; ++cat)
2629 					group_speed[cat][s] = (1 + max_speed[cat][s] - min_speed[cat][s]) / ((float)cfg->UNIT_SPEED_SUBGROUPS);
2630 			}
2631 
2632 			fclose(load_file);
2633 			return true;
2634 		}
2635 	}
2636 
2637 	return false;
2638 }
2639 
SaveBuildTable(int game_period,MapType map_type)2640 void AAIBuildTable::SaveBuildTable(int game_period, MapType map_type)
2641 {
2642 	// reset factory ratings
2643 	for(int s = 0; s < cfg->SIDES; ++s)
2644 	{
2645 		for(list<int>::iterator fac = units_of_category[STATIONARY_CONSTRUCTOR][s].begin(); fac != units_of_category[STATIONARY_CONSTRUCTOR][s].end(); ++fac)
2646 		{
2647 			units_static[*fac].efficiency[5] = -1;
2648 			units_static[*fac].efficiency[4] = 0;
2649 		}
2650 	}
2651 	// reset builder ratings
2652 	for(int s = 0; s < cfg->SIDES; ++s)
2653 	{
2654 		for(list<int>::iterator builder = units_of_category[MOBILE_CONSTRUCTOR][s].begin(); builder != units_of_category[MOBILE_CONSTRUCTOR][s].end(); ++builder)
2655 			units_static[*builder].efficiency[5] = -1;
2656 	}
2657 
2658 	const std::string filename = GetBuildCacheFileName();
2659 	FILE *save_file = fopen(filename.c_str(), "w+");
2660 
2661 	// file version
2662 	fprintf(save_file, "%s \n", MOD_LEARN_VERSION);
2663 
2664 	// update attacked_by values
2665 	// FIXME: using t two times as the for-loop-var?
2666 	for(int t = 0; t < 4; ++t)
2667 	{
2668 		for(int cat = 0; cat < combat_categories; ++cat)
2669 		{
2670 			for(int t = 0; t < game_period; ++t)
2671 			{
2672 				attacked_by_category_learned[map_type][t][cat] =
2673 						0.75f * attacked_by_category_learned[map_type][t][cat] +
2674 						0.25f * attacked_by_category_current[t][cat];
2675 			}
2676 		}
2677 	}
2678 
2679 	// save attacked_by table
2680 	for(int map = 0; map <= WATER_MAP; ++map)
2681 	{
2682 		for(int t = 0; t < 4; ++t)
2683 		{
2684 			for(int cat = 0; cat < combat_categories; ++cat)
2685 			{
2686 				fprintf(save_file, "%f ", attacked_by_category_learned[map][t][cat]);
2687 				fprintf(save_file, "\n");
2688 			}
2689 		}
2690 	}
2691 
2692 //	int tmp;
2693 
2694 	for(int i = 1; i < unitList.size(); ++i)
2695 	{
2696 //		tmp = units_static[i].canBuildList.size();
2697 
2698 		fprintf(save_file, "%i %i %u %u %f %f %f %i " _STPF_ " " _STPF_ " ", units_static[i].def_id, units_static[i].side,
2699 								units_static[i].unit_type, units_static[i].movement_type, units_static[i].range,
2700 								units_static[i].cost, units_static[i].builder_cost, (int) units_static[i].category,
2701 								units_static[i].canBuildList.size(), units_static[i].builtByList.size());
2702 
2703 		// save combat eff
2704 		for(int k = 0; k < combat_categories; ++k)
2705 			fprintf(save_file, "%f ", units_static[i].efficiency[k]);
2706 
2707 		// save buildoptions
2708 		for(list<int>::iterator j = units_static[i].canBuildList.begin(); j != units_static[i].canBuildList.end(); ++j)
2709 			fprintf(save_file, "%i ", *j);
2710 
2711 		// save builtby-list
2712 		for(list<int>::iterator k = units_static[i].builtByList.begin(); k != units_static[i].builtByList.end(); ++k)
2713 			fprintf(save_file, "%i ", *k);
2714 
2715 		fprintf(save_file, "\n");
2716 	}
2717 
2718 	for(int s = 0; s < numOfSides; ++s)
2719 	{
2720 		// now save unit lists
2721 		for(int cat = 0; cat <= MOBILE_CONSTRUCTOR; ++cat)
2722 		{
2723 			// save number of units
2724 			fprintf(save_file,  _STPF_ " ", units_of_category[cat][s].size());
2725 
2726 			for(list<int>::iterator unit = units_of_category[cat][s].begin(); unit != units_of_category[cat][s].end(); ++unit)
2727 				fprintf(save_file, "%i ", *unit);
2728 
2729 			fprintf(save_file, "\n");
2730 
2731 			// save pre cached values
2732 			fprintf(save_file, "%f %f %f %f %f %f %f %f %f \n",
2733 						max_cost[cat][s], min_cost[cat][s], avg_cost[cat][s],
2734 						max_buildtime[cat][s], min_buildtime[cat][s], avg_buildtime[cat][s],
2735 						max_value[cat][s], min_value[cat][s], avg_value[cat][s]);
2736 
2737 			fprintf(save_file, "\n");
2738 		}
2739 
2740 		// save cached speed stats
2741 		for(int cat = 0; cat < combat_categories; ++cat)
2742 		{
2743 			fprintf(save_file, "%f %f %f %f \n", min_speed[cat][s], max_speed[cat][s], group_speed[cat][s], avg_speed[cat][s]);
2744 
2745 			fprintf(save_file, "\n");
2746 		}
2747 	}
2748 
2749 	fclose(save_file);
2750 }
2751 
DebugPrint()2752 void AAIBuildTable::DebugPrint()
2753 {
2754 	if(unitList.empty())
2755 		return;
2756 
2757 	// for debugging
2758 	UnitType unitType;
2759 	// this size equals the one used in "AIAICallback::GetValue(AIVAL_LOCATE_FILE_..."
2760 	const std::string filename = cfg->GetFileName(cfg->getUniqueName(true, true, true, true), MOD_LEARN_PATH, "_buildtable.txt", true);
2761 
2762 	FILE *file = fopen(filename.c_str(), "w");
2763 
2764 	if(file)
2765 	{
2766 
2767 	for(int i = 1; i < unitList.size(); i++)
2768 	{
2769 		// unit type
2770 		unitType = GetUnitType(i);
2771 
2772 		if(cfg->AIR_ONLY_MOD)
2773 		{
2774 			fprintf(file, "ID: %-3i %-16s %-40s %-25s %s\n", i, GetUnitDef(i).name.c_str(), GetUnitDef(i).humanName.c_str(), GetCategoryString(i), sideNames[units_static[i].side].c_str());
2775 		}
2776 		else
2777 		{
2778 			fprintf(file, "ID: %-3i %-16s %-40s %-25s %-8s", i, GetUnitDef(i).name.c_str(), GetUnitDef(i).humanName.c_str(), GetCategoryString(i), sideNames[units_static[i].side].c_str());
2779 
2780 			if(units_static[i].category == GROUND_ASSAULT ||units_static[i].category == SEA_ASSAULT || units_static[i].category == HOVER_ASSAULT)
2781 			{
2782 				if(unitType == ANTI_AIR_UNIT)
2783 					fprintf(file, " anti air unit");
2784 				else if(unitType == ASSAULT_UNIT)
2785 					fprintf(file, " assault unit");
2786 			}
2787 			else if(units_static[i].category == AIR_ASSAULT)
2788 			{
2789 				if(unitType == ANTI_AIR_UNIT)
2790 					fprintf(file, " fighter");
2791 				else if(unitType == ASSAULT_UNIT)
2792 					fprintf(file, " gunship");
2793 				else
2794 					fprintf(file, " bomber");
2795 			}
2796 			else if(units_static[i].category == SUBMARINE_ASSAULT)
2797 				fprintf(file, " assault unit");
2798 
2799 			if(IsBuilder(i))
2800 				fprintf(file, " builder");
2801 
2802 			if(IsFactory(i))
2803 				fprintf(file, " factory");
2804 
2805 			if(IsCommander(i))
2806 				fprintf(file, " commander");
2807 
2808 			if(units_static[i].movement_type & MOVE_TYPE_AMPHIB)
2809 				fprintf(file, " amphibious");
2810 
2811 			fprintf(file, "\n");
2812 		}
2813 
2814 		//fprintf(file, "Max damage: %f\n", GetMaxDamage(i));
2815 
2816 		/*fprintf(file, "Can Build:\n");
2817 
2818 		for(list<int>::iterator j = units_static[i].canBuildList.begin(); j != units_static[i].canBuildList.end(); ++j)
2819 			fprintf(file, "%s ", GetUnitDef(*j)->humanName.c_str());
2820 
2821 		fprintf(file, "\n Built by: ");
2822 
2823 		for(list<int>::iterator k = units_static[i].builtByList.begin(); k != units_static[i].builtByList.end(); ++k)
2824 			fprintf(file, "%s ", GetUnitDef(*k)->humanName.c_str());
2825 
2826 		fprintf(file, "\n \n");
2827 		*/
2828 	}
2829 
2830 	for(int s = 1; s <= numOfSides; s++)
2831 	{
2832 		// print unit lists
2833 		for(int cat = 1; cat <= MOBILE_CONSTRUCTOR; ++cat)
2834 		{
2835 			if(units_of_category[cat][s-1].size() > 0)
2836 			{
2837 				fprintf(file, "\n%s %s:\n",GetCategoryString2((UnitCategory) cat), sideNames[s].c_str());
2838 
2839 				for(list<int>::iterator unit = units_of_category[cat][s-1].begin(); unit != units_of_category[cat][s-1].end(); ++unit)
2840 					fprintf(file, "%s    ", GetUnitDef(*unit).humanName.c_str());
2841 
2842 				fprintf(file, "\n");
2843 			}
2844 		}
2845 
2846 		// print average costs/speed, etc
2847 		/*if(units_of_category[cat][s-1].size() > 0)
2848 		{
2849 			fprintf(file, "\nMin/Max/Average unit cost\n");
2850 
2851 			for(int cat = 0; cat <= SEA_BUILDER; ++cat)
2852 				fprintf(file, "\n%s %s: %f, %f, %f \n",GetCategoryString2((UnitCategory) cat), sideNames[s].c_str(), min_cost[cat][s-1], max_cost[cat][s-1], avg_cost[cat][s-1]);
2853 
2854 			fprintf(file, "\nMin/Max/Average unit buildtime\n");
2855 
2856 			for(int cat = 0; cat <= SEA_BUILDER; ++cat)
2857 				fprintf(file, "\n%s %s: %f, %f, %f \n",GetCategoryString2((UnitCategory) cat), sideNames[s].c_str(), min_buildtime[cat][s-1], max_buildtime[cat][s-1], avg_buildtime[cat][s-1]);
2858 
2859 			fprintf(file, "\nMin/Max/Average unit value\n");
2860 
2861 			for(int cat = 0; cat <= SEA_BUILDER; ++cat)
2862 				fprintf(file, "\n%s %s: %f, %f, %f \n",GetCategoryString2((UnitCategory) cat), sideNames[s].c_str(), min_value[cat][s-1], max_value[cat][s-1], avg_value[cat][s-1]);
2863 		}*/
2864 
2865 		//fprintf(file, "\nAverage/Min speed\n\n");
2866 		//fprintf(file, "\n%s %s: %f, %f \n",GetCategoryString2(GROUND_ASSAULT), sideNames[s].c_str(), avg_value[GROUND_ASSAULT][s-1], max_value[GROUND_ASSAULT][s-1]);
2867 		//fprintf(file, "\n%s %s: %f, %f \n",GetCategoryString2(SEA_ASSAULT), sideNames[s].c_str(), avg_value[SEA_ASSAULT][s-1], max_value[SEA_ASSAULT][s-1]);
2868 	}
2869 
2870 	fclose(file);
2871 	}
2872 	// error
2873 	else
2874 	{
2875 	}
2876 }
2877 
GetMaxRange(int unit_id)2878 float AAIBuildTable::GetMaxRange(int unit_id)
2879 {
2880 	float max_range = 0;
2881 
2882 	for(vector<UnitDef::UnitDefWeapon>::const_iterator i = GetUnitDef(unit_id).weapons.begin(); i != GetUnitDef(unit_id).weapons.end(); ++i)
2883 	{
2884 		if((*i).def->range > max_range)
2885 			max_range = (*i).def->range;
2886 	}
2887 
2888 	return max_range;
2889 }
2890 
GetMaxDamage(int unit_id)2891 float AAIBuildTable::GetMaxDamage(int unit_id)
2892 {
2893 	float max_damage = 0;
2894 
2895 	int armor_types;
2896 	ai->Getcb()->GetValue(AIVAL_NUMDAMAGETYPES,&armor_types);
2897 
2898 	for(vector<UnitDef::UnitDefWeapon>::const_iterator i = GetUnitDef(unit_id).weapons.begin(); i != GetUnitDef(unit_id).weapons.end(); ++i)
2899 	{
2900 		for(int k = 0; k < armor_types; ++k)
2901 		{
2902 			if((*i).def->damages[k] > max_damage)
2903 				max_damage = (*i).def->damages[k];
2904 		}
2905 	}
2906 
2907 	return max_damage;
2908 }
2909 
2910 // declaration is in aidef.h
ReplaceExtension(const char * n,char * dst,int s,const char * ext)2911 void ReplaceExtension(const char *n, char *dst, int s, const char *ext)
2912 {
2913 	unsigned int l = strlen (n);
2914 
2915 	unsigned int a=l-1;
2916 	while (n[a] && n[a]!='.' && a>0)
2917 		a--;
2918 
2919 	strncpy (dst, "", s);
2920 	if (a>s-sizeof("")) a=s-sizeof("");
2921 	memcpy (&dst [sizeof ("")-1], n, a);
2922 	dst[a+sizeof("")]=0;
2923 
2924 	strncat (dst, ext, s);
2925 }
2926 
GetFactoryRating(int def_id)2927 float AAIBuildTable::GetFactoryRating(int def_id)
2928 {
2929 	// check if value already chached
2930 	if(units_static[def_id].efficiency[5] != -1)
2931 		return units_static[def_id].efficiency[5];
2932 
2933 	// calculate rating and cache result
2934 	bool builder = false;
2935 	bool scout = false;
2936 	float rating = 1.0f;
2937 	float combat_units = 0;
2938 	float ground = 0.1f + 0.01f * (attacked_by_category_learned[ai->Getmap()->map_type][0][0] + attacked_by_category_learned[ai->Getmap()->map_type][1][0] + attacked_by_category_learned[ai->Getmap()->map_type][2][0]);
2939 	float air = 0.1f + 0.01f * (attacked_by_category_learned[ai->Getmap()->map_type][0][1] + attacked_by_category_learned[ai->Getmap()->map_type][1][1] + attacked_by_category_learned[ai->Getmap()->map_type][2][1]);
2940 	float hover = 0.1f + 0.01f * (attacked_by_category_learned[ai->Getmap()->map_type][0][2] + attacked_by_category_learned[ai->Getmap()->map_type][1][2] + attacked_by_category_learned[ai->Getmap()->map_type][2][2]);
2941 	float sea = 0.1f + 0.01f * (attacked_by_category_learned[ai->Getmap()->map_type][0][3] + attacked_by_category_learned[ai->Getmap()->map_type][1][3] + attacked_by_category_learned[ai->Getmap()->map_type][2][3]);
2942 	float submarine = 0.1f + 0.01f * (attacked_by_category_learned[ai->Getmap()->map_type][0][4] + attacked_by_category_learned[ai->Getmap()->map_type][1][4] + attacked_by_category_learned[ai->Getmap()->map_type][2][4]);
2943 
2944 	if(cfg->AIR_ONLY_MOD)
2945 	{
2946 		for(list<int>::iterator unit = units_static[def_id].canBuildList.begin(); unit != units_static[def_id].canBuildList.end(); ++unit)
2947 		{
2948 			if(units_static[*unit].category >= GROUND_ASSAULT && units_static[*unit].category <= SEA_ASSAULT)
2949 			{
2950 				rating += ground * units_static[*unit].efficiency[0] + air * units_static[*unit].efficiency[1] + hover * units_static[*unit].efficiency[2] + sea * units_static[*unit].efficiency[3];
2951 				combat_units += 1;
2952 			}
2953 			else if(IsBuilder(def_id))
2954 			{
2955 				rating += 256.0 * GetBuilderRating(*unit);
2956 				builder = true;
2957 			}
2958 			else if(IsScout(def_id))
2959 			{
2960 				scout = true;
2961 			}
2962 		}
2963 	}
2964 	else if(ai->Getmap()->map_type == LAND_MAP)
2965 	{
2966 		for(list<int>::iterator unit = units_static[def_id].canBuildList.begin(); unit != units_static[def_id].canBuildList.end(); ++unit)
2967 		{
2968 			if(units_static[*unit].category == GROUND_ASSAULT || units_static[*unit].category == HOVER_ASSAULT)
2969 			{
2970 				if(units_static[*unit].unit_type == ANTI_AIR_UNIT)
2971 					rating += air * units_static[*unit].efficiency[1];
2972 				else
2973 					rating += 0.5f * (ground * units_static[*unit].efficiency[0] + hover * units_static[*unit].efficiency[2]);
2974 
2975 				combat_units += 1;
2976 			}
2977 			else if(units_static[*unit].category == AIR_ASSAULT)
2978 			{
2979 				if(units_static[*unit].unit_type == ANTI_AIR_UNIT)
2980 					rating += air * units_static[*unit].efficiency[1];
2981 				else
2982 					rating += 0.5f * (ground * units_static[*unit].efficiency[0] + hover * units_static[*unit].efficiency[2]);
2983 
2984 				combat_units += 1;
2985 			}
2986 			else if(IsBuilder(def_id) && !IsSea(def_id))
2987 			{
2988 				rating += 256 * GetBuilderRating(*unit);
2989 				builder = true;
2990 			}
2991 			else if(units_static[*unit].category == SCOUT && !(units_static[*unit].movement_type & MOVE_TYPE_SEA) )
2992 			{
2993 				scout = true;
2994 			}
2995 		}
2996 	}
2997 	else if(ai->Getmap()->map_type == LAND_WATER_MAP)
2998 	{
2999 		for(list<int>::iterator unit = units_static[def_id].canBuildList.begin(); unit != units_static[def_id].canBuildList.end(); ++unit)
3000 		{
3001 			if(units_static[*unit].category == GROUND_ASSAULT)
3002 			{
3003 				if(units_static[*unit].unit_type == ANTI_AIR_UNIT)
3004 					rating += air * units_static[*unit].efficiency[1];
3005 				else
3006 					rating += 0.5f * (ground * units_static[*unit].efficiency[0] + hover * units_static[*unit].efficiency[2]);
3007 
3008 				combat_units += 1;
3009 			}
3010 			else if(units_static[*unit].category == HOVER_ASSAULT)
3011 			{
3012 				if(units_static[*unit].unit_type == ANTI_AIR_UNIT)
3013 					rating += air * units_static[*unit].efficiency[1];
3014 				else
3015 					rating += 0.33f * (ground * units_static[*unit].efficiency[0] + hover * units_static[*unit].efficiency[2] + sea * units_static[*unit].efficiency[3]);
3016 
3017 				combat_units += 1;
3018 			}
3019 			else if(units_static[*unit].category == AIR_ASSAULT)
3020 			{
3021 				if(units_static[*unit].unit_type == ANTI_AIR_UNIT)
3022 					rating += air * units_static[*unit].efficiency[1];
3023 				else
3024 					rating += 0.33f * (ground * units_static[*unit].efficiency[0] + hover * units_static[*unit].efficiency[2] + sea * units_static[*unit].efficiency[3]);
3025 
3026 				combat_units += 1;
3027 			}
3028 			else if(units_static[*unit].category == SEA_ASSAULT)
3029 			{
3030 				if(units_static[*unit].unit_type == ANTI_AIR_UNIT)
3031 					rating += air * units_static[*unit].efficiency[1];
3032 				else
3033 					rating += 0.33f * (hover * units_static[*unit].efficiency[2] + sea * units_static[*unit].efficiency[3] + submarine * units_static[*unit].efficiency[4]);
3034 
3035 				combat_units += 1;
3036 			}
3037 			else if(units_static[*unit].category == SUBMARINE_ASSAULT)
3038 			{
3039 				rating += 0.5f * (sea * units_static[*unit].efficiency[3] + submarine * units_static[*unit].efficiency[4]);
3040 				combat_units += 1;
3041 			}
3042 			else if(IsBuilder(def_id))
3043 			{
3044 				rating += 256 * GetBuilderRating(*unit);
3045 				builder = true;
3046 			}
3047 			else if(units_static[*unit].category == SCOUT)
3048 			{
3049 				scout = true;
3050 			}
3051 		}
3052 	}
3053 	else if(ai->Getmap()->map_type == WATER_MAP)
3054 	{
3055 		for(list<int>::iterator unit = units_static[def_id].canBuildList.begin(); unit != units_static[def_id].canBuildList.end(); ++unit)
3056 		{
3057 			if(units_static[*unit].category == HOVER_ASSAULT)
3058 			{
3059 				if(units_static[*unit].unit_type == ANTI_AIR_UNIT)
3060 					rating += air * units_static[*unit].efficiency[1];
3061 				else
3062 					rating += 0.5f * (hover * units_static[*unit].efficiency[2] + sea * units_static[*unit].efficiency[3]);
3063 
3064 				combat_units += 1;
3065 			}
3066 			else if(units_static[*unit].category == AIR_ASSAULT)
3067 			{
3068 				if(units_static[*unit].unit_type == ANTI_AIR_UNIT)
3069 					rating += air * units_static[*unit].efficiency[1];
3070 				else
3071 					rating += 0.5f * (hover * units_static[*unit].efficiency[2] + sea * units_static[*unit].efficiency[3]);
3072 
3073 				combat_units += 1;
3074 			}
3075 			else if(units_static[*unit].category == SEA_ASSAULT)
3076 			{
3077 				if(units_static[*unit].unit_type == ANTI_AIR_UNIT)
3078 					rating += air * units_static[*unit].efficiency[1];
3079 				else
3080 					rating += 0.33f * (hover * units_static[*unit].efficiency[2] + sea * units_static[*unit].efficiency[3] + submarine * units_static[*unit].efficiency[4]);
3081 
3082 				combat_units += 1;
3083 			}
3084 			else if(units_static[*unit].category == SUBMARINE_ASSAULT)
3085 			{
3086 				rating += 0.5f * (sea * units_static[*unit].efficiency[3] + submarine * units_static[*unit].efficiency[4]);
3087 				combat_units += 1;
3088 			}
3089 			else if(IsBuilder(def_id))
3090 			{
3091 				rating += 256 * GetBuilderRating(*unit);
3092 				builder = true;
3093 			}
3094 			else if(units_static[*unit].category == SCOUT)
3095 			{
3096 				scout = true;
3097 			}
3098 		}
3099 	}
3100 
3101 	if(combat_units > 0)
3102 	{
3103 		rating /= (combat_units * units_static[def_id].cost);
3104 		rating *= fastmath::apxsqrt((float) (4 + combat_units) );
3105 
3106 		if(scout)
3107 			rating += 8.0f;
3108 
3109 		units_static[def_id].efficiency[5] = rating;
3110 		return rating;
3111 	}
3112 	else if(builder)
3113 	{
3114 		if(scout)
3115 		{
3116 
3117 			units_static[def_id].efficiency[5] = 1.0f;
3118 			return 1.0f;
3119 		}
3120 		else
3121 		{
3122 			units_static[def_id].efficiency[5] = 0.5f;
3123 			return 0.5f;
3124 		}
3125 	}
3126 	else
3127 	{
3128 		units_static[def_id].efficiency[5] = 0;
3129 		return 0;
3130 	}
3131 }
3132 
GetBuilderRating(int def_id)3133 float AAIBuildTable::GetBuilderRating(int def_id)
3134 {
3135 	// check if value already chached
3136 	if(units_static[def_id].efficiency[5] != -1)
3137 		return units_static[def_id].efficiency[5];
3138 	else
3139 	{
3140 		// calculate rating and cache result
3141 		int buildings = 10;
3142 
3143 		// only cout buildings that are likely to be built on that type of map
3144 		if(ai->Getmap()->map_type == LAND_MAP)
3145 		{
3146 			for(list<int>::iterator building = units_static[def_id].canBuildList.begin(); building != units_static[def_id].canBuildList.end(); ++building)
3147 			{
3148 				if(GetUnitDef(*building).minWaterDepth <= 0)
3149 					++buildings;
3150 			}
3151 		}
3152 		else if(ai->Getmap()->map_type == WATER_MAP)
3153 		{
3154 			for(list<int>::iterator building = units_static[def_id].canBuildList.begin(); building != units_static[def_id].canBuildList.end(); ++building)
3155 			{
3156 				if(GetUnitDef(*building).minWaterDepth > 0)
3157 					++buildings;
3158 			}
3159 		}
3160 		else
3161 		{
3162 			buildings = units_static[def_id].canBuildList.size();
3163 		}
3164 
3165 		units_static[def_id].efficiency[5] = sqrt((double)buildings);
3166 
3167 		return units_static[def_id].efficiency[5];
3168 	}
3169 }
3170 
BuildFactoryFor(int unit_def_id)3171 void AAIBuildTable::BuildFactoryFor(int unit_def_id)
3172 {
3173 	int constructor = 0;
3174 	float best_rating = -100000.0f, my_rating;
3175 
3176 	float cost = 1.0f;
3177 	float buildspeed = 1.0f;
3178 
3179 	// determine max values
3180 	float max_buildtime = 0;
3181 	float max_buildspeed = 0;
3182 	float max_cost = 0;
3183 
3184 	for(list<int>::iterator factory = units_static[unit_def_id].builtByList.begin();  factory != units_static[unit_def_id].builtByList.end(); ++factory)
3185 	{
3186 		if(units_static[*factory].cost > max_cost)
3187 			max_cost = units_static[*factory].cost;
3188 
3189 		if(GetUnitDef(*factory).buildTime > max_buildtime)
3190 			max_buildtime = GetUnitDef(*factory).buildTime;
3191 
3192 		if(GetUnitDef(*factory).buildSpeed > max_buildspeed)
3193 			max_buildspeed = GetUnitDef(*factory).buildSpeed;
3194 	}
3195 
3196 	// look for best builder to do the job
3197 	for(list<int>::iterator factory = units_static[unit_def_id].builtByList.begin();  factory != units_static[unit_def_id].builtByList.end(); ++factory)
3198 	{
3199 		if(units_dynamic[*factory].active + units_dynamic[*factory].requested + units_dynamic[*factory].under_construction < cfg->MAX_FACTORIES_PER_TYPE)
3200 		{
3201 			my_rating = buildspeed * (GetUnitDef(*factory).buildSpeed / max_buildspeed)
3202 				- (GetUnitDef(*factory).buildTime / max_buildtime)
3203 				- cost * (units_static[*factory].cost / max_cost);
3204 
3205 			//my_rating += GetBuilderRating(*unit, cost, buildspeed) / ;
3206 
3207 			// prefer builders that can be built atm
3208 			if(units_dynamic[*factory].constructorsAvailable > 0)
3209 				my_rating += 2.0f;
3210 
3211 			// prevent AAI from requesting factories that cannot be built within the current base
3212 			if(units_static[*factory].movement_type & MOVE_TYPE_STATIC_LAND)
3213 			{
3214 				if(ai->Getbrain()->baseLandRatio > 0.1f)
3215 					my_rating *= ai->Getbrain()->baseLandRatio;
3216 				else
3217 					my_rating = -100000.0f;
3218 			}
3219 			else if(units_static[*factory].movement_type & MOVE_TYPE_STATIC_WATER)
3220 			{
3221 				if(ai->Getbrain()->baseWaterRatio > 0.1f)
3222 					my_rating *= ai->Getbrain()->baseWaterRatio;
3223 				else
3224 					my_rating = -100000.0f;
3225 			}
3226 
3227 			if(my_rating > best_rating)
3228 			{
3229 				best_rating = my_rating;
3230 				constructor = *factory;
3231 			}
3232 		}
3233 	}
3234 
3235 	if(constructor && units_dynamic[constructor].requested + units_dynamic[constructor].under_construction <= 0)
3236 	{
3237 		for(list<int>::iterator j = units_static[constructor].canBuildList.begin(); j != units_static[constructor].canBuildList.end(); ++j)
3238 		{
3239 			//// only set to true, if the factory is not built by that unit itself
3240 			//if(!MemberOf(*j, units_static[*i].builtByList))
3241 			units_dynamic[*j].constructorsRequested += 1;
3242 		}
3243 
3244 		units_dynamic[constructor].requested += 1;
3245 
3246 		// factory requested
3247 		if(IsStatic(constructor))
3248 		{
3249 			if(units_dynamic[constructor].constructorsAvailable + units_dynamic[constructor].constructorsRequested <= 0)
3250 			{
3251 				ai->Log("BuildFactoryFor(%s) is requesting builder for %s\n", GetUnitDef(unit_def_id).humanName.c_str(), GetUnitDef(constructor).humanName.c_str());
3252 				BuildBuilderFor(constructor);
3253 			}
3254 
3255 			// debug
3256 			ai->Log("BuildFactoryFor(%s) requested %s\n", GetUnitDef(unit_def_id).humanName.c_str(), GetUnitDef(constructor).humanName.c_str());
3257 		}
3258 		// mobile constructor requested
3259 		else
3260 		{
3261 			if(ai->Getexecute()->AddUnitToBuildqueue(constructor, 1, true))
3262 			{
3263 				// increase counter if mobile factory is a builder as well
3264 				if(units_static[constructor].unit_type & UNIT_TYPE_BUILDER)
3265 					ai->Getut()->futureBuilders += 1;
3266 
3267 				if(units_dynamic[constructor].constructorsAvailable + units_dynamic[constructor].constructorsRequested <= 0)
3268 				{
3269 					ai->Log("BuildFactoryFor(%s) is requesting factory for %s\n", GetUnitDef(unit_def_id).humanName.c_str(), GetUnitDef(constructor).humanName.c_str());
3270 					BuildFactoryFor(constructor);
3271 				}
3272 
3273 				// debug
3274 				ai->Log("BuildFactoryFor(%s) requested %s\n", GetUnitDef(unit_def_id).humanName.c_str(), GetUnitDef(constructor).humanName.c_str());
3275 			}
3276 			else
3277 			{
3278 				//something went wrong -> decrease values
3279 				units_dynamic[constructor].requested -= 1;
3280 
3281 				for(list<int>::iterator j = units_static[constructor].canBuildList.begin(); j != units_static[constructor].canBuildList.end(); ++j)
3282 					units_dynamic[*j].constructorsRequested -= 1;
3283 			}
3284 		}
3285 	}
3286 }
3287 
3288 // tries to build another builder for a certain building
BuildBuilderFor(int building_def_id)3289 void AAIBuildTable::BuildBuilderFor(int building_def_id)
3290 {
3291 	int constructor = 0;
3292 	float best_rating = -10000.0f, my_rating;
3293 
3294 	float cost = 1.0f;
3295 	float buildspeed = 1.0f;
3296 
3297 	// determine max values
3298 	float max_buildtime = 0;
3299 	float max_buildspeed = 0;
3300 	float max_cost = 0;
3301 
3302 	for(list<int>::iterator builder = units_static[building_def_id].builtByList.begin();  builder != units_static[building_def_id].builtByList.end(); ++builder)
3303 	{
3304 		if(units_static[*builder].cost > max_cost)
3305 			max_cost = units_static[*builder].cost;
3306 
3307 		if(GetUnitDef(*builder).buildTime > max_buildtime)
3308 			max_buildtime = GetUnitDef(*builder).buildTime;
3309 
3310 		if(GetUnitDef(*builder).buildSpeed > max_buildspeed)
3311 			max_buildspeed = GetUnitDef(*builder).buildSpeed;
3312 	}
3313 
3314 	// look for best builder to do the job
3315 	for(list<int>::iterator builder = units_static[building_def_id].builtByList.begin();  builder != units_static[building_def_id].builtByList.end(); ++builder)
3316 	{
3317 		// prevent ai from ordering too many builders of the same type/commanders/builders that cant be built atm
3318 		if(units_dynamic[*builder].active + units_dynamic[*builder].under_construction + units_dynamic[*builder].requested < cfg->MAX_BUILDERS_PER_TYPE)
3319 		{
3320 			my_rating = buildspeed * SafeDivide(GetUnitDef(*builder).buildSpeed, max_buildspeed)
3321 				- SafeDivide(GetUnitDef(*builder).buildTime, max_buildtime)
3322 				- cost * SafeDivide(units_static[*builder].cost, max_cost);
3323 
3324 			// prefer builders that can be built atm
3325 			if(units_dynamic[*builder].constructorsAvailable > 0)
3326 				my_rating += 1.5f;
3327 
3328 			if(my_rating > best_rating)
3329 			{
3330 				best_rating = my_rating;
3331 				constructor = *builder;
3332 			}
3333 		}
3334 	}
3335 
3336 	if(constructor && units_dynamic[constructor].under_construction + units_dynamic[constructor].requested <= 0)
3337 	{
3338 		// build factory if necessary
3339 		if(units_dynamic[constructor].constructorsAvailable + units_dynamic[constructor].constructorsRequested <= 0)
3340 		{
3341 			ai->Log("BuildBuilderFor(%s) is requesting factory for %s\n", GetUnitDef(building_def_id).humanName.c_str(), GetUnitDef(constructor).humanName.c_str());
3342 
3343 			BuildFactoryFor(constructor);
3344 		}
3345 
3346 		if(ai->Getexecute()->AddUnitToBuildqueue(constructor, 1, true))
3347 		{
3348 			units_dynamic[constructor].requested += 1;
3349 			ai->Getut()->futureBuilders += 1;
3350 			ai->Getut()->UnitRequested(MOBILE_CONSTRUCTOR);
3351 
3352 			// set all its buildoptions buildable
3353 			for(list<int>::iterator j = units_static[constructor].canBuildList.begin(); j != units_static[constructor].canBuildList.end(); ++j)
3354 				units_dynamic[*j].constructorsRequested += 1;
3355 
3356 			// debug
3357 			ai->Log("BuildBuilderFor(%s) requested %s\n", GetUnitDef(building_def_id).humanName.c_str(), GetUnitDef(constructor).humanName.c_str());
3358 		}
3359 	}
3360 }
3361 
3362 
AddAssistant(unsigned int allowed_movement_types,bool canBuild)3363 void AAIBuildTable::AddAssistant(unsigned int allowed_movement_types, bool canBuild)
3364 {
3365 	int builder = 0;
3366 	float best_rating = -10000, my_rating;
3367 
3368 	int side = ai->Getside()-1;
3369 
3370 	float cost = 1.0f;
3371 	float buildspeed = 2.0f;
3372 	float urgency = 1.0f;
3373 
3374 	for(list<int>::iterator unit = units_of_category[MOBILE_CONSTRUCTOR][side].begin();  unit != units_of_category[MOBILE_CONSTRUCTOR][side].end(); ++unit)
3375 	{
3376 		if(units_static[*unit].movement_type & allowed_movement_types)
3377 		{
3378 			if( (!canBuild || units_dynamic[*unit].constructorsAvailable > 0)
3379 				&& units_dynamic[*unit].active + units_dynamic[*unit].under_construction + units_dynamic[*unit].requested < cfg->MAX_BUILDERS_PER_TYPE)
3380 			{
3381 				if( GetUnitDef(*unit).buildSpeed >= (float)cfg->MIN_ASSISTANCE_BUILDTIME && GetUnitDef(*unit).canAssist)
3382 				{
3383 					my_rating = cost * (units_static[*unit].cost / max_cost[MOBILE_CONSTRUCTOR][ai->Getside()-1])
3384 								+ buildspeed * (GetUnitDef(*unit).buildSpeed / max_value[MOBILE_CONSTRUCTOR][ai->Getside()-1])
3385 								- urgency * (GetUnitDef(*unit).buildTime / max_buildtime[MOBILE_CONSTRUCTOR][ai->Getside()-1]);
3386 
3387 					if(my_rating > best_rating)
3388 					{
3389 						best_rating = my_rating;
3390 						builder = *unit;
3391 					}
3392 				}
3393 			}
3394 		}
3395 	}
3396 
3397 	if(builder && units_dynamic[builder].under_construction + units_dynamic[builder].requested < 1)
3398 	{
3399 		// build factory if necessary
3400 		if(units_dynamic[builder].constructorsAvailable <= 0)
3401 			BuildFactoryFor(builder);
3402 
3403 		if(ai->Getexecute()->AddUnitToBuildqueue(builder, 1, true))
3404 		{
3405 			units_dynamic[builder].requested += 1;
3406 			ai->Getut()->futureBuilders += 1;
3407 			ai->Getut()->UnitRequested(MOBILE_CONSTRUCTOR);
3408 
3409 			// increase number of requested builders of all buildoptions
3410 			for(list<int>::iterator j = units_static[builder].canBuildList.begin(); j != units_static[builder].canBuildList.end(); ++j)
3411 				units_dynamic[*j].constructorsRequested += 1;
3412 
3413 			//ai->Log("AddAssister() requested: %s %i \n", GetUnitDef(builder).humanName.c_str(), units_dynamic[builder].requested);
3414 		}
3415 	}
3416 }
3417 
3418 
IsArty(int id)3419 bool AAIBuildTable::IsArty(int id)
3420 {
3421 	if(!GetUnitDef(id).weapons.empty())
3422 	{
3423 		float max_range = 0;
3424 //		const WeaponDef *longest = 0;
3425 
3426 		for(vector<UnitDef::UnitDefWeapon>::const_iterator weapon = GetUnitDef(id).weapons.begin(); weapon != GetUnitDef(id).weapons.end(); ++weapon)
3427 		{
3428 			if(weapon->def->range > max_range)
3429 			{
3430 				max_range = weapon->def->range;
3431 //				longest = weapon->def;
3432 			}
3433 		}
3434 
3435 		// veh, kbot, hover or ship
3436 		if(GetUnitDef(id).movedata)
3437 		{
3438 			if(GetUnitDef(id).movedata->moveFamily == MoveData::Tank || GetUnitDef(id).movedata->moveFamily == MoveData::KBot)
3439 			{
3440 				if(max_range > cfg->GROUND_ARTY_RANGE)
3441 					return true;
3442 			}
3443 			else if(GetUnitDef(id).movedata->moveFamily == MoveData::Ship)
3444 			{
3445 				if(max_range > cfg->SEA_ARTY_RANGE)
3446 					return true;
3447 			}
3448 			else if(GetUnitDef(id).movedata->moveFamily == MoveData::Hover)
3449 			{
3450 				if(max_range > cfg->HOVER_ARTY_RANGE)
3451 					return true;
3452 			}
3453 		}
3454 		else // aircraft
3455 		{
3456 			if(cfg->AIR_ONLY_MOD)
3457 			{
3458 				if(max_range > cfg->GROUND_ARTY_RANGE)
3459 					return true;
3460 			}
3461 		}
3462 
3463 		if(GetUnitDef(id).highTrajectoryType == 1)
3464 			return true;
3465 	}
3466 
3467 	return false;
3468 }
3469 
IsScout(int id)3470 bool AAIBuildTable::IsScout(int id)
3471 {
3472 	if(GetUnitDef(id).speed > cfg->SCOUT_SPEED && !GetUnitDef(id).canfly)
3473 		return true;
3474 	else
3475 	{
3476 		for(list<int>::iterator i = cfg->SCOUTS.begin(); i != cfg->SCOUTS.end(); ++i)
3477 		{
3478 			if(*i == id)
3479 				return true;
3480 		}
3481 	}
3482 
3483 	return false;
3484 }
3485 
IsAttacker(int id)3486 bool AAIBuildTable::IsAttacker(int id)
3487 {
3488 	for(list<int>::iterator i = cfg->ATTACKERS.begin(); i != cfg->ATTACKERS.end(); ++i)
3489 	{
3490 		if(*i == id)
3491 			return true;
3492 	}
3493 
3494 	return false;
3495 }
3496 
3497 
IsTransporter(int id)3498 bool AAIBuildTable::IsTransporter(int id)
3499 {
3500 	for(list<int>::iterator i = cfg->TRANSPORTERS.begin(); i != cfg->TRANSPORTERS.end(); ++i)
3501 	{
3502 		if(*i == id)
3503 			return true;
3504 	}
3505 
3506 	return false;
3507 }
3508 
AllowedToBuild(int id)3509 bool AAIBuildTable::AllowedToBuild(int id)
3510 {
3511 	for(list<int>::iterator i = cfg->DONT_BUILD.begin(); i != cfg->DONT_BUILD.end(); ++i)
3512 	{
3513 		if(*i == id)
3514 			return false;
3515 	}
3516 
3517 	return true;
3518 }
3519 
IsMetalMaker(int id)3520 bool AAIBuildTable::IsMetalMaker(int id)
3521 {
3522 	for(list<int>::iterator i = cfg->METAL_MAKERS.begin(); i != cfg->METAL_MAKERS.end(); ++i)
3523 	{
3524 		if(*i == id)
3525 			return true;
3526 	}
3527 
3528 	return false;
3529 }
3530 
IsMissileLauncher(int def_id)3531 bool AAIBuildTable::IsMissileLauncher(int def_id)
3532 {
3533 	for(vector<UnitDef::UnitDefWeapon>::const_iterator weapon = GetUnitDef(def_id).weapons.begin(); weapon != GetUnitDef(def_id).weapons.end(); ++weapon)
3534 	{
3535 		if(weapon->def->stockpile)
3536 			return true;
3537 	}
3538 
3539 	return false;
3540 }
3541 
IsDeflectionShieldEmitter(int def_id)3542 bool AAIBuildTable::IsDeflectionShieldEmitter(int def_id)
3543 {
3544 	for(vector<UnitDef::UnitDefWeapon>::const_iterator weapon = GetUnitDef(def_id).weapons.begin(); weapon != GetUnitDef(def_id).weapons.end(); ++weapon)
3545 	{
3546 		if(weapon->def->isShield)
3547 			return true;
3548 	}
3549 
3550 	return false;
3551 }
3552 
IsCommander(int def_id)3553 bool AAIBuildTable::IsCommander(int def_id)
3554 {
3555 	if(units_static[def_id].unit_type & UNIT_TYPE_COMMANDER)
3556 		return true;
3557 	else
3558 		return false;
3559 }
3560 
IsGround(int def_id)3561 bool AAIBuildTable::IsGround(int def_id)
3562 {
3563 	if(units_static[def_id].movement_type & (MOVE_TYPE_GROUND + MOVE_TYPE_AMPHIB) )
3564 		return true;
3565 	else
3566 		return false;
3567 }
3568 
IsAir(int def_id)3569 bool AAIBuildTable::IsAir(int def_id)
3570 {
3571 	if(units_static[def_id].movement_type & MOVE_TYPE_AIR)
3572 		return true;
3573 	else
3574 		return false;
3575 }
3576 
IsHover(int def_id)3577 bool AAIBuildTable::IsHover(int def_id)
3578 {
3579 	if(units_static[def_id].movement_type & MOVE_TYPE_HOVER)
3580 		return true;
3581 	else
3582 		return false;
3583 }
3584 
IsSea(int def_id)3585 bool AAIBuildTable::IsSea(int def_id)
3586 {
3587 	if(units_static[def_id].movement_type & MOVE_TYPE_SEA)
3588 		return true;
3589 	else
3590 		return false;
3591 }
3592 
IsStatic(int def_id)3593 bool AAIBuildTable::IsStatic(int def_id)
3594 {
3595 	if(units_static[def_id].movement_type & MOVE_TYPE_STATIC)
3596 		return true;
3597 	else
3598 		return false;
3599 }
3600 
CanMoveLand(int def_id)3601 bool AAIBuildTable::CanMoveLand(int def_id)
3602 {
3603 	if( !(units_static[def_id].movement_type & MOVE_TYPE_SEA)
3604 		&& !(units_static[def_id].movement_type & MOVE_TYPE_STATIC))
3605 		return true;
3606 	else
3607 		return false;
3608 }
3609 
CanMoveWater(int def_id)3610 bool AAIBuildTable::CanMoveWater(int def_id)
3611 {
3612 	if( !(units_static[def_id].movement_type & MOVE_TYPE_GROUND) && !(units_static[def_id].movement_type & MOVE_TYPE_STATIC))
3613 		return true;
3614 	else
3615 		return false;
3616 }
3617 
CanPlacedLand(int def_id)3618 bool AAIBuildTable::CanPlacedLand(int def_id)
3619 {
3620 	if(units_static[def_id].movement_type & MOVE_TYPE_STATIC_LAND)
3621 		return true;
3622 	else
3623 		return false;
3624 }
3625 
CanPlacedWater(int def_id)3626 bool AAIBuildTable::CanPlacedWater(int def_id)
3627 {
3628 	if(units_static[def_id].movement_type & MOVE_TYPE_STATIC_WATER)
3629 		return true;
3630 	else
3631 		return false;
3632 }
3633 
GetEfficiencyAgainst(int unit_def_id,UnitCategory category)3634 float AAIBuildTable::GetEfficiencyAgainst(int unit_def_id, UnitCategory category)
3635 {
3636 	if(category == GROUND_ASSAULT)
3637 		return units_static[unit_def_id].efficiency[0];
3638 	else if(category == AIR_ASSAULT)
3639 		return units_static[unit_def_id].efficiency[1];
3640 	else if(category == HOVER_ASSAULT)
3641 		return units_static[unit_def_id].efficiency[2];
3642 	else if(category == SEA_ASSAULT)
3643 		return units_static[unit_def_id].efficiency[3];
3644 	else if(category == SUBMARINE_ASSAULT)
3645 		return units_static[unit_def_id].efficiency[4];
3646 	else if(category >= STATIONARY_DEF && category <= METAL_MAKER)
3647 		return units_static[unit_def_id].efficiency[5];
3648 	else
3649 		return 0;
3650 }
3651 
GetCategoryString(int def_id)3652 const char* AAIBuildTable::GetCategoryString(int def_id)
3653 {
3654 	def_id = units_static[def_id].category;
3655 
3656 	if(def_id == UNKNOWN)
3657 		return "unknown";
3658 	else if(def_id == GROUND_ASSAULT)
3659 	{
3660 		if(cfg->AIR_ONLY_MOD)
3661 			return "light air assault";
3662 		else
3663 			return "ground assault";
3664 	}
3665 	else if(def_id == AIR_ASSAULT)
3666 		return "air assault";
3667 	else if(def_id == HOVER_ASSAULT)
3668 	{
3669 		if(cfg->AIR_ONLY_MOD)
3670 			return "heavy air assault";
3671 		else
3672 			return "hover assault";
3673 	}
3674 	else if(def_id == SEA_ASSAULT)
3675 	{
3676 		if(cfg->AIR_ONLY_MOD)
3677 			return "super heavy air assault";
3678 		else
3679 			return "sea assault";
3680 	}
3681 	else if(def_id == SUBMARINE_ASSAULT)
3682 		return "submarine assault";
3683 	else if(def_id == MOBILE_CONSTRUCTOR)
3684 		return "builder";
3685 
3686 	else if(def_id == SCOUT)
3687 		return "scout";
3688 	else if(def_id == MOBILE_TRANSPORT)
3689 		return "transport";
3690 	else if(def_id == GROUND_ARTY)
3691 	{
3692 		if(cfg->AIR_ONLY_MOD)
3693 			return "mobile artillery";
3694 		else
3695 			return "ground artillery";
3696 	}
3697 	else if(def_id == SEA_ARTY)
3698 		return "naval artillery";
3699 	else if(def_id == HOVER_ARTY)
3700 		return "hover artillery";
3701 	else if(def_id == STATIONARY_DEF)
3702 		return "defence building";
3703 	else if(def_id == STATIONARY_ARTY)
3704 		return "stationary arty";
3705 	else if(def_id == EXTRACTOR)
3706 		return "metal extractor";
3707 	else if(def_id == POWER_PLANT)
3708 		return "power plant";
3709 	else if(def_id == STORAGE)
3710 		return "storage";
3711 	else if(def_id == METAL_MAKER)
3712 		return "metal maker";
3713 	else if(def_id == STATIONARY_CONSTRUCTOR)
3714 		return "stationary constructor";
3715 	else if(def_id == AIR_BASE)
3716 		return "air base";
3717 	else if(def_id == DEFLECTION_SHIELD)
3718 		return "deflection shield";
3719 	else if(def_id == STATIONARY_JAMMER)
3720 		return "stationary jammer";
3721 	else if(def_id == STATIONARY_RECON)
3722 		return "stationary radar/sonar";
3723 	else if(def_id == STATIONARY_LAUNCHER)
3724 		return "stationary launcher";
3725 	else if(def_id == MOBILE_JAMMER)
3726 		return "mobile jammer";
3727 	else if(def_id == MOBILE_LAUNCHER)
3728 		return "mobile launcher";
3729 	else if(def_id== COMMANDER)
3730 		return "commander";
3731 
3732 	return "unknown";
3733 }
3734 
GetCategoryString2(UnitCategory category)3735 const char* AAIBuildTable::GetCategoryString2(UnitCategory category)
3736 {
3737 	if(category == UNKNOWN)
3738 		return "unknown";
3739 	else if(category == GROUND_ASSAULT)
3740 	{
3741 		if(cfg->AIR_ONLY_MOD)
3742 			return "light air assault";
3743 		else
3744 			return "ground assault";
3745 	}
3746 	else if(category == AIR_ASSAULT)
3747 		return "air assault";
3748 	else if(category == HOVER_ASSAULT)
3749 	{
3750 		if(cfg->AIR_ONLY_MOD)
3751 			return "heavy air assault";
3752 		else
3753 			return "hover assault";
3754 	}
3755 	else if(category == SEA_ASSAULT)
3756 	{
3757 		if(cfg->AIR_ONLY_MOD)
3758 			return "super heavy air assault";
3759 		else
3760 			return "sea assault";
3761 	}
3762 	else if(category == SUBMARINE_ASSAULT)
3763 		return "submarine assault";
3764 	else if(category == MOBILE_CONSTRUCTOR)
3765 		return "builder";
3766 	else if(category == SCOUT)
3767 		return "scout";
3768 	else if(category == MOBILE_TRANSPORT)
3769 		return "transport";
3770 	else if(category == GROUND_ARTY)
3771 	{
3772 		if(cfg->AIR_ONLY_MOD)
3773 			return "mobile artillery";
3774 		else
3775 			return "ground artillery";
3776 	}
3777 	else if(category == SEA_ARTY)
3778 		return "naval artillery";
3779 	else if(category == HOVER_ARTY)
3780 		return "hover artillery";
3781 	else if(category == STATIONARY_DEF)
3782 		return "defence building";
3783 	else if(category == STATIONARY_ARTY)
3784 		return "stationary arty";
3785 	else if(category == EXTRACTOR)
3786 		return "metal extractor";
3787 	else if(category == POWER_PLANT)
3788 		return "power plant";
3789 	else if(category == STORAGE)
3790 		return "storage";
3791 	else if(category == METAL_MAKER)
3792 		return "metal maker";
3793 	else if(category == STATIONARY_CONSTRUCTOR)
3794 		return "stationary constructor";
3795 	else if(category == AIR_BASE)
3796 		return "air base";
3797 	else if(category == DEFLECTION_SHIELD)
3798 		return "deflection shield";
3799 	else if(category == STATIONARY_JAMMER)
3800 		return "stationary jammer";
3801 	else if(category == STATIONARY_RECON)
3802 		return "stationary radar/sonar";
3803 	else if(category == STATIONARY_LAUNCHER)
3804 		return "stationary launcher";
3805 	else if(category == MOBILE_JAMMER)
3806 		return "mobile jammer";
3807 	else if(category == MOBILE_LAUNCHER)
3808 		return "mobile launcher";
3809 	else if(category == COMMANDER)
3810 		return "commander";
3811 
3812 	return "unknown";
3813 }
3814 
IsStartingUnit(int def_id)3815 bool AAIBuildTable::IsStartingUnit(int def_id)
3816 {
3817 	for(int i = 0; i < numOfSides; i++)
3818 	{
3819 		if(startUnits[i] == def_id)
3820 			return true;
3821 	}
3822 
3823 	return false;
3824 }
3825 
IsBuilder(int def_id)3826 bool AAIBuildTable::IsBuilder(int def_id)
3827 {
3828 	if(units_static[def_id].unit_type & UNIT_TYPE_BUILDER)
3829 		return true;
3830 	else
3831 		return false;
3832 }
3833 
IsFactory(int def_id)3834 bool AAIBuildTable::IsFactory(int def_id)
3835 {
3836 	assert(def_id >= 0);
3837 	assert(def_id < units_static.size());
3838 	if(units_static[def_id].unit_type & UNIT_TYPE_FACTORY)
3839 		return true;
3840 	else
3841 		return false;
3842 }
3843 
GetIDOfAssaultCategory(UnitCategory category)3844 int AAIBuildTable::GetIDOfAssaultCategory(UnitCategory category)
3845 {
3846 	if(category == GROUND_ASSAULT)
3847 		return 0;
3848 	else if(category == AIR_ASSAULT)
3849 		return 1;
3850 	else if(category == HOVER_ASSAULT)
3851 		return 2;
3852 	else if(category == SEA_ASSAULT)
3853 		return 3;
3854 	else if(category == SUBMARINE_ASSAULT)
3855 		return 4;
3856 	else if(category >= STATIONARY_DEF && category <= METAL_MAKER)
3857 		return 5;
3858 	else
3859 		return -1;
3860 }
3861 
GetAssaultCategoryOfID(int id)3862 UnitCategory AAIBuildTable::GetAssaultCategoryOfID(int id)
3863 {
3864 	if(id == 0)
3865 		return GROUND_ASSAULT;
3866 	else if(id == 1)
3867 		return AIR_ASSAULT;
3868 	else if(id == 2)
3869 		return HOVER_ASSAULT;
3870 	else if(id == 3)
3871 		return SEA_ASSAULT;
3872 	else if(id == 4)
3873 		return SUBMARINE_ASSAULT;
3874 	else if(id == 5)
3875 		return STATIONARY_DEF;
3876 	else
3877 		return UNKNOWN;
3878 }
3879 
GetAllowedMovementTypesForAssister(int building)3880 unsigned int AAIBuildTable::GetAllowedMovementTypesForAssister(int building)
3881 {
3882 	// determine allowed movement types
3883 	// alwas allowed: MOVE_TYPE_AIR, MOVE_TYPE_HOVER, MOVE_TYPE_AMPHIB
3884 	unsigned int allowed_movement_types = 22;
3885 
3886 	if(units_static[building].movement_type & MOVE_TYPE_STATIC_LAND)
3887 		allowed_movement_types |= MOVE_TYPE_GROUND;
3888 	else
3889 		allowed_movement_types |= MOVE_TYPE_SEA;
3890 
3891 	return allowed_movement_types;
3892 }
3893 
GetUnitRating(int unit,float ground_eff,float air_eff,float hover_eff,float sea_eff,float submarine_eff)3894 float AAIBuildTable::GetUnitRating(int unit, float ground_eff, float air_eff, float hover_eff, float sea_eff, float submarine_eff)
3895 {
3896 	float rating = 0.1f + ground_eff * units_static[unit].efficiency[0] +  air_eff * units_static[unit].efficiency[1] +  hover_eff * units_static[unit].efficiency[2] +  sea_eff * units_static[unit].efficiency[3] + submarine_eff * units_static[unit].efficiency[4];
3897 	rating /= units_static[unit].cost;
3898 	return rating;
3899 }
3900 
DetermineBetterUnit(int unit1,int unit2,float ground_eff,float air_eff,float hover_eff,float sea_eff,float submarine_eff,float speed,float range,float cost)3901 int AAIBuildTable::DetermineBetterUnit(int unit1, int unit2, float ground_eff, float air_eff, float hover_eff, float sea_eff, float submarine_eff, float speed, float range, float cost)
3902 {
3903 	float rating1 = GetUnitRating(unit1, ground_eff, air_eff, hover_eff, sea_eff, submarine_eff);
3904 	float rating2 = GetUnitRating(unit2, ground_eff, air_eff, hover_eff, sea_eff, submarine_eff);
3905 	float rating3 = 0.0f;
3906 	if (units_static[unit2].range > 0) { //compare unit weapon range
3907 		rating3 = range * units_static[unit1].range / units_static[unit2].range;
3908 	}
3909 	float rating4 = 0.0f;
3910 	if (GetUnitDef(unit2).speed > 0) { //compare unit speeds
3911 		rating4 = (speed * GetUnitDef(unit1).speed / GetUnitDef(unit2).speed);
3912 	}
3913 
3914 	if (((rating2 == 0.0f) || (units_static[unit2].range == 0.0f) || (GetUnitDef(unit2).speed == 0.0f))
3915 			|| ((cost * rating1 / rating2) + rating3 + rating4 > 0.0f)) {
3916 		return unit1;
3917 	} else {
3918 		return unit2;
3919 	}
3920 }
3921 
3922 
3923