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