1 #include "CUnitTable.h"
2
3 #include <iostream>
4 #include <fstream>
5 #include <string>
6
7 #include "CAI.h"
8 #include "CUnit.h"
9 #include "CConfigParser.h"
10 #include "Util.hpp"
11 #include "ReusableObjectFactory.hpp"
12
13 std::map<std::string, unitCategory> CUnitTable::str2cat;
14 CUnitTable::UnitCategory2StrMap CUnitTable::cat2str;
15 std::vector<unitCategory> CUnitTable::cats;
16
CUnitTable(AIClasses * ai)17 CUnitTable::CUnitTable(AIClasses *ai): ARegistrar(100) {
18 this->ai = ai;
19
20 if (cat2str.empty()) {
21 /* techlevels */
22 cat2str[TECH1] = "TECH1";
23 cat2str[TECH2] = "TECH2";
24 cat2str[TECH3] = "TECH3";
25 cat2str[TECH4] = "TECH4";
26 cat2str[TECH5] = "TECH5";
27
28 /* main categories */
29 cat2str[AIR] = "AIR";
30 cat2str[SEA] = "SEA";
31 cat2str[LAND] = "LAND";
32 cat2str[SUB] = "SUB";
33
34 cat2str[STATIC] = "STATIC";
35 cat2str[MOBILE] = "MOBILE";
36
37 /* builders */
38 cat2str[FACTORY] = "FACTORY";
39 cat2str[BUILDER] = "BUILDER";
40 cat2str[ASSISTER] = "ASSISTER";
41 cat2str[RESURRECTOR] = "RESURRECTOR";
42
43 /* offensives */
44 cat2str[COMMANDER] = "COMMANDER";
45 cat2str[ATTACKER] = "ATTACKER";
46 cat2str[ANTIAIR] = "ANTIAIR";
47 cat2str[SCOUTER] = "SCOUTER";
48 cat2str[ARTILLERY] = "ARTILLERY";
49 cat2str[SNIPER] = "SNIPER";
50 cat2str[ASSAULT] = "ASSAULT";
51
52 /* economic */
53 cat2str[MEXTRACTOR] = "MEXTRACTOR";
54 cat2str[MMAKER] = "MMAKER";
55 cat2str[EMAKER] = "EMAKER";
56 cat2str[MSTORAGE] = "MSTORAGE";
57 cat2str[ESTORAGE] = "ESTORAGE";
58
59 /* factory types */
60 cat2str[KBOT] = "KBOT";
61 cat2str[VEHICLE] = "VEHICLE";
62 cat2str[HOVER] = "HOVER";
63 cat2str[AIRCRAFT] = "AIRCRAFT";
64 cat2str[NAVAL] = "NAVAL";
65
66 cat2str[DEFENSE] = "DEFENSE";
67
68 cat2str[JAMMER] = "JAMMER";
69 cat2str[NUKE] = "NUKE";
70 cat2str[ANTINUKE] = "ANTINUKE";
71 cat2str[PARALYZER] = "PARALYZER";
72 cat2str[TORPEDO] = "TORPEDO";
73 cat2str[TRANSPORT] = "TRANSPORT";
74 cat2str[EBOOSTER] = "EBOOSTER";
75 cat2str[MBOOSTER] = "MBOOSTER";
76 cat2str[SHIELD] = "SHIELD";
77 cat2str[NANOTOWER] = "NANOTOWER";
78 cat2str[REPAIRPAD] = "REPAIRPAD";
79
80 cat2str[WIND] = "WIND";
81 cat2str[TIDAL] = "TIDAL";
82
83 assert(cat2str.size() == MAX_CATEGORIES);
84 }
85
86 if (str2cat.empty()) {
87 /* Create the str2cat table and cats vector */
88 UnitCategory2StrMap::iterator i;
89 for (i = cat2str.begin(); i != cat2str.end(); ++i) {
90 cats.push_back(i->first);
91 str2cat[i->second] = i->first;
92 }
93 }
94
95 maxUnitPower = 0.0f;
96 numUnits = ai->cb->GetNumUnitDefs();
97
98 /* Build the techtree, note that this is actually a graph in XTA */
99 buildTechTree();
100
101 bool success = false;
102 unsigned int lastFlags = GET_CAT;
103 unsigned int flagsOrder[] = { GET_CAT|GET_VER|GET_TEAM, GET_CAT|GET_VER, GET_CAT|GET_TEAM, GET_CAT };
104 std::string filename;
105
106 for (int i = 0; i < sizeof(flagsOrder) / sizeof(unsigned int); i++) {
107 lastFlags = flagsOrder[i];
108 filename = ai->cfgparser->getFilename(lastFlags);
109 if (ai->cfgparser->fileExists(filename)) {
110 success = ai->cfgparser->parseCategories(filename, units);
111 if (success)
112 break;
113 }
114 else
115 LOG_WW("CUnitTable::CUnitTable Categorization file not found: " << filename)
116 }
117
118 if (!success) {
119 filename = util::GetAbsFileName(ai->cb, std::string(CFG_FOLDER) + filename, false);
120 generateCategorizationFile(filename);
121 }
122
123 filename = ai->cfgparser->getFilename(lastFlags|GET_PATCH);
124 if (ai->cfgparser->fileExists(filename))
125 ai->cfgparser->parseCategories(filename, units, true);
126 else
127 LOG_WW("CUnitTable::CUnitTable Categorization patch-file not found: " << filename)
128
129 /* Generate the buildBy and canBuild lists per UnitType */
130 /*
131 std::map<int, UnitType*>::iterator l;
132 std::string buildBy, canBuild;
133 std::map<int, UnitType>::iterator j;
134 for (j = units.begin(); j != units.end(); j++) {
135 UnitType *utParent = &(j->second);
136
137 debugCategories(utParent);
138 debugUnitDefs(utParent);
139 debugWeapons(utParent);
140 canBuild = buildBy = "";
141 for (l = utParent->buildBy.begin(); l != utParent->buildBy.end(); l++) {
142 std::stringstream out;
143 out << l->first;
144 buildBy += l->second->def->name + "(" + out.str() + "), ";
145 }
146 buildBy = buildBy.substr(0, buildBy.length() - 2);
147 for (l = utParent->canBuild.begin(); l != utParent->canBuild.end(); l++) {
148 std::stringstream out;
149 out << l->first;
150 canBuild += l->second->def->name + "(" + out.str() + "), ";
151 }
152 canBuild = canBuild.substr(0, canBuild.length() - 2);
153 }
154 */
155
156 LOG_II("CUnitTable::CUnitTable Number of unit types: " << numUnits);
157 LOG_II("CUnitTable::CUnitTable Max unit power: " << maxUnitPower);
158 }
159
~CUnitTable()160 CUnitTable::~CUnitTable()
161 {
162 }
163
generateCategorizationFile(std::string & fileName)164 void CUnitTable::generateCategorizationFile(std::string& fileName) {
165 const std::string modShortName(ai->cb->GetModShortName());
166 const std::string modVersion(ai->cb->GetModVersion());
167
168 std::ofstream file(fileName.c_str(), std::ios::trunc);
169
170 file << "# Unit basic categorization file for " << AI_NAME << "\n\n";
171 file << "# Based on game " << modShortName << "-" << modVersion << "\n\n";
172 file << "# Autogenerated by " << AI_VERSION << "\n# DO NOT MODIFY!\n\n";
173 file << "# Available categories:\n";
174
175 for (UnitCategory2StrMap::iterator i = cat2str.begin(); i != cat2str.end(); ++i) {
176 file << "# " << i->second << "\n";
177 }
178
179 file << "\n\n# Total number of unit definitions: " << numUnits << "\n\n";
180
181 for (std::map<int, UnitType>::iterator j = units.begin(); j != units.end(); ++j) {
182 UnitType* utParent = &(j->second);
183 file << "# " << utParent->def->humanName << "\n";
184 file << utParent->def->name;
185 for (unsigned int i = 0; i < cats.size(); i++)
186 if ((cats[i]&utParent->cats).any())
187 file << "," << cat2str[cats[i]];
188 file << "\n\n";
189 }
190
191 file.close();
192
193 LOG_II("CUnitTable::generateCategorizationFile " << fileName)
194 }
195
remove(ARegistrar & object)196 void CUnitTable::remove(ARegistrar& object) {
197 CUnit *unit = dynamic_cast<CUnit*>(&object);
198
199 LOG_II("CUnitTable::remove " << (*unit))
200
201 builders.erase(unit->key);
202 idle.erase(unit->key);
203 metalMakers.erase(unit->key);
204 activeUnits.erase(unit->key);
205 factories.erase(unit->key);
206 defenses.erase(unit->key);
207 energyStorages.erase(unit->key);
208 unitsUnderPlayerControl.erase(unit->key);
209 unitsUnderConstruction.erase(unit->key);
210 unitsBuilding.erase(unit->key);
211 staticUnits.erase(unit->key);
212 staticWaterUnits.erase(unit->key);
213 staticEconomyUnits.erase(unit->key);
214
215 unit->unreg(*this);
216
217 ReusableObjectFactory<CUnit>::Release(unit);
218 }
219
getUnit(int uid)220 CUnit* CUnitTable::getUnit(int uid) {
221 std::map<int, CUnit*>::iterator u = activeUnits.find(uid);
222 if (u == activeUnits.end())
223 return NULL;
224 else
225 return u->second;
226 }
227
requestUnit(int uid,int bid)228 CUnit* CUnitTable::requestUnit(int uid, int bid) {
229 CUnit *unit = ReusableObjectFactory<CUnit>::Instance();
230
231 unit->ai = ai;
232 unit->reset(uid, bid);
233 unit->reg(*this);
234
235 const unitCategory cats = unit->type->cats;
236
237 if (bid > 0)
238 builders[bid] = false;
239
240 activeUnits[uid] = unit;
241
242 idle[bid] = false;
243 idle[uid] = false;
244
245 if ((cats&MOBILE).any() && bid >= 0) {
246 const unitCategory bcats = activeUnits[bid]->type->cats;
247 unit->techlvl = (bcats&TECH1).any() ? TECH1 : unit->techlvl;
248 unit->techlvl = (bcats&TECH2).any() ? TECH2 : unit->techlvl;
249 unit->techlvl = (bcats&TECH3).any() ? TECH3 : unit->techlvl;
250 unit->techlvl = (bcats&TECH4).any() ? TECH4 : unit->techlvl;
251 unit->techlvl = (bcats&TECH5).any() ? TECH5 : unit->techlvl;
252 }
253 // NOTE: remember that NOTA has mobile defenses
254 if (((cats&STATIC).any() && (cats&ATTACKER).any()) || (cats&DEFENSE).any())
255 defenses[unit->key] = unit;
256 if ((cats&ESTORAGE).any())
257 energyStorages[unit->key] = unit;
258 if ((cats&FACTORY).any())
259 factories[unit->key] = unit;
260 if ((cats&MMAKER).any())
261 metalMakers[unit->key] = unit;
262 if ((cats&STATIC).any()) {
263 staticUnits[unit->key] = unit;
264 if (unit->isEconomy())
265 staticEconomyUnits[unit->key] = unit;
266 if ((cats&(SEA|SUB)).any())
267 staticWaterUnits[unit->key] = unit;
268 }
269
270 return unit;
271 }
272
update()273 void CUnitTable::update() {
274 CUnit* unit;
275 std::map<int, CUnit*>::iterator i;
276
277 for (i = activeUnits.begin(); i != activeUnits.end(); ++i) {
278 unit = i->second;
279 if (unit->isMicroing())
280 unit->microingFrames += MULTIPLEXER;
281 else
282 unit->aliveFrames += MULTIPLEXER;
283 }
284 }
285
buildTechTree()286 void CUnitTable::buildTechTree() {
287 if (!units.empty())
288 return; // alreay initialized
289
290 std::map<int, std::string> buildOptions;
291 std::map<int, std::string>::iterator j;
292 std::vector<const UnitDef*> unitdefs(numUnits);
293
294 ai->cb->GetUnitDefList(&unitdefs[0]);
295
296 // NOTE: -1 movetype means a graph for aircraft
297 moveTypes[-1] = NULL;
298
299 for (int i = 0; i < numUnits; i++) {
300 const UnitDef *ud = unitdefs[i];
301 if (ud == NULL) continue;
302 std::map<int, UnitType>::iterator u = units.find(ud->id);
303
304 UnitType *utParent, *utChild;
305
306 if (u == units.end())
307 utParent = insertUnit(ud);
308 else
309 utParent = &(u->second);
310
311 buildOptions = ud->buildOptions;
312 for (j = buildOptions.begin(); j != buildOptions.end(); ++j) {
313 ud = ai->cb->GetUnitDef(j->second.c_str());
314 u = units.find(ud->id);
315
316 if (u == units.end())
317 utChild = insertUnit(ud);
318 else
319 utChild = &(u->second);
320
321 utChild->buildBy[utParent->def->id] = utParent;
322 utParent->canBuild[utChild->def->id] = utChild;
323 }
324 }
325
326 for (int i = 0; i < numUnits; i++) {
327 const UnitDef *ud = unitdefs[i];
328 if (ud == NULL) continue;
329 units[ud->id].cats = categorizeUnit(&units[ud->id]);
330 }
331 }
332
insertUnit(const UnitDef * ud)333 UnitType* CUnitTable::insertUnit(const UnitDef *ud) {
334 UnitType ut;
335
336 ut.def = ud;
337 ut.cost = ud->metalCost*METAL2ENERGY + ud->energyCost;
338 ut.costMetal = ud->metalCost;
339 ut.energyMake = ud->energyMake - ud->energyUpkeep;
340 ut.metalMake = ud->metalMake - ud->metalUpkeep;
341 ut.dps = calcUnitDps(&ut);
342 units[ud->id] = ut;
343
344 // also register pathtype...
345 MoveData* md = ud->movedata;
346 if (md)
347 moveTypes[md->pathType] = md;
348
349 if (maxUnitPower < ut.dps)
350 maxUnitPower = ut.dps;
351
352 return &units[ud->id];
353 }
354
categorizeUnit(UnitType * ut)355 unitCategory CUnitTable::categorizeUnit(UnitType *ut) {
356 const UnitDef* ud = ut->def;
357 unitCategory cats = 0;
358
359 //assert(ud->name != "armsonar");
360 //assert(ud->humanName != "Shark");
361
362 if (ud->isCommander)
363 cats |= COMMANDER;
364
365 if (ud->speed > EPS)
366 cats |= MOBILE;
367 else
368 cats |= STATIC;
369
370 if (ud->canfly)
371 cats |= AIR;
372
373 if (ud->canhover)
374 cats |= SEA;
375 else if (ud->canSubmerge)
376 cats |= SUB;
377 else if (ud->movedata) {
378 if (ud->movedata->subMarine)
379 cats |= SUB; // submarine
380 else if (ud->movedata->moveType == MoveData::Ship_Move) {
381 float heightAboveWater = ai->cb->GetUnitDefHeight(ud->id) - ud->waterline;
382 if (heightAboveWater < EPS)
383 cats |= SUB;
384 else
385 cats |= SEA;
386 }
387 else if (ud->movedata->depth > 100.0f)
388 cats |= SUB; // amphibious unit
389 }
390 else if (ud->floater || ud->waterline > 0.0f || ud->minWaterDepth > 0.0f) {
391 float heightAboveWater;
392
393 if (ud->waterline > 0.0f || ud->floater)
394 //heightAboveWater = ai->cb->GetUnitDefHeight(ud->id) - ud->waterline;
395 heightAboveWater = ai->cb->GetUnitDefRadius(ud->id) / 2.0f - ud->waterline;
396 else
397 heightAboveWater = -1.0f; // force to SUB
398
399 if (heightAboveWater < EPS)
400 cats |= SUB;
401 else
402 cats |= SEA;
403 }
404
405 if ((ud->canhover || ud->minWaterDepth < 0.0f) && !ud->canfly)
406 cats |= LAND;
407
408 if (ud->canAssist)
409 cats |= ASSISTER;
410
411 if (ud->metalStorage / ut->cost > 0.1f)
412 cats |= MSTORAGE;
413
414 if (ud->energyStorage / ut->cost > 0.2f)
415 cats |= ESTORAGE;
416
417 if (ud->makesMetal >= 0.5f && (ud->energyUpkeep > (ud->makesMetal * 40.0f)))
418 cats |= MMAKER;
419
420 if ((ud->energyMake - ud->energyUpkeep) / ut->cost > 0.002
421 || ud->tidalGenerator || ud->windGenerator) {
422 cats |= EMAKER;
423 if (ud->tidalGenerator)
424 cats |= TIDAL;
425 if (ud->windGenerator)
426 cats |= WIND;
427 }
428
429 if (ud->extractsMetal)
430 cats |= MEXTRACTOR;
431 /*
432 if (ud->radarRadius > 0)
433 cats |= RADAR;
434
435 if (ud->sonarRadius > 0)
436 cats |= SONAR;
437 */
438 // NOTE: BA's Dragonfly (transport aircraft) jammer radius = 75
439 if (ud->jammerRadius > 100) {
440 cats |= JAMMER;
441 if ((cats&STATIC).any())
442 cats |= DEFENSE;
443 }
444
445 if (!ud->weapons.empty()) {
446 cats |= ATTACKER;
447
448 if (CUnit::hasTorpedoWeapon(ud->weapons))
449 cats |= TORPEDO;
450
451 if (CUnit::hasParalyzerWeapon(ud->weapons))
452 cats |= PARALYZER;
453 else if ((cats&AIR).any() && ud->hoverAttack)
454 cats |= ASSAULT;
455
456 /* 0 = only low, 1 = only high, 2 both */
457 if ((cats&AIR).none() && ud->highTrajectoryType >= 1)
458 cats |= ARTILLERY;
459
460 if (CUnit::hasAntiAirWeapon(ud->weapons))
461 cats |= ANTIAIR;
462 else if (CUnit::hasNukeWeapon(ud->weapons))
463 cats |= NUKE;
464 else if (CUnit::hasInterceptorWeapon(ud->weapons))
465 cats |= ANTINUKE; // TODO: distinguish from EMP
466 else if (CUnit::hasShield(ud->weapons))
467 cats |= SHIELD;
468
469 if ((cats&STATIC).any() && (cats&NUKE).none())
470 cats |= DEFENSE;
471 }
472
473 if (ud->canResurrect)
474 cats |= RESURRECTOR;
475
476 // NOTE: we aren't checking for "canMove" because it is usually used
477 // to set rally point for factory
478 if (!ud->buildOptions.empty()) {
479 int kamikazeUnitCount = 0;
480 std::map<int, std::string>::const_iterator j;
481
482 cats |= BUILDER;
483 if ((cats&STATIC).any())
484 cats |= FACTORY;
485
486 // preprocessing stage...
487 for (j = ud->buildOptions.begin(); j != ud->buildOptions.end(); ++j) {
488 const UnitDef* canbuild = ai->cb->GetUnitDef(j->second.c_str());
489
490 if (canbuild == NULL)
491 continue;
492
493 if (canbuild->canKamikaze)
494 kamikazeUnitCount++;
495
496 if (canbuild->speed < EPS && (cats&FACTORY).any())
497 // this is a static builder, not a factory
498 cats &= ~FACTORY;
499 }
500
501 if (kamikazeUnitCount > 4)
502 cats &= ~(FACTORY|BUILDER);
503
504 if ((cats&FACTORY).any()) {
505 // precise factory type...
506 for (j = ud->buildOptions.begin(); j != ud->buildOptions.end(); ++j) {
507 const UnitDef* canbuild = ai->cb->GetUnitDef(j->second.c_str());
508
509 if (canbuild == NULL)
510 continue;
511
512 if (canbuild->canfly) {
513 cats |= AIRCRAFT;
514 break;
515 }
516
517 if (canbuild->movedata == NULL)
518 continue;
519
520 if (canbuild->movedata->moveFamily == MoveData::KBot
521 && ud->minWaterDepth < 0.0f) {
522 cats |= KBOT;
523 break;
524 }
525
526 if (canbuild->movedata->moveFamily == MoveData::Tank
527 && ud->minWaterDepth < 0.0f) {
528 cats |= VEHICLE;
529 break;
530 }
531
532 if (canbuild->movedata->moveFamily == MoveData::Hover) {
533 cats |= HOVER;
534 break;
535 }
536
537 if (canbuild->movedata->moveFamily == MoveData::Ship) {
538 cats |= NAVAL;
539 break;
540 }
541 }
542 }
543
544 /*
545 // TODO: improve heuristic estimator then uncomment the code below
546 if ((cats&BUILDER).any()) {
547 if (ud->metalCost < 2000.0f)
548 cats |= TECH1;
549 else
550 cats |= TECH2;
551 }
552 */
553 }
554
555 if ((cats&ASSISTER).any() && (cats&(BUILDER|FACTORY)).none()) {
556 // NOTE: default value for "buildDistance" = 128
557 if (ud->buildDistance < 130.0f)
558 cats |= REPAIRPAD;
559 else
560 cats |= NANOTOWER;
561 }
562
563 if (ud->loadingRadius > 0.0f && ud->transportCapacity > 0)
564 cats |= TRANSPORT;
565
566 /*
567 // TODO: improve heuristic estimator then uncomment the code below
568 if ((cats&ATTACKER).any() && (cats&MOBILE).any() && (cats&BUILDER).none() && ud->speed >= 50.0f) {
569 std::map<int, UnitType*>::iterator i,j;
570 for (i = ut->buildBy.begin(); i != ut->buildBy.end(); ++i) {
571 bool isCheapest = true;
572 UnitType *bb = i->second;
573 for (j = bb->canBuild.begin(); j != bb->canBuild.end(); ++j) {
574 if (ut->cost > j->second->cost && !j->second->def->weapons.empty()) {
575 isCheapest = false;
576 break;
577 }
578 }
579 if (isCheapest) {
580 cats |= SCOUTER;
581 break;
582 }
583 }
584 }
585 */
586
587 return cats;
588 }
589
calcUnitDps(UnitType * ut)590 float CUnitTable::calcUnitDps(UnitType *ut) {
591 // FIXME: make our own *briljant* dps calc here
592 return ut->def->power;
593 }
594
unitCount(unitCategory c)595 int CUnitTable::unitCount(unitCategory c) {
596 int result = 0;
597 std::map<int, CUnit*>::iterator i;
598
599 for (i = activeUnits.begin(); i != activeUnits.end(); ++i) {
600 if ((c&i->second->type->cats) == c)
601 result++;
602 }
603
604 return result;
605 }
606
factoryCount(unitCategory c)607 int CUnitTable::factoryCount(unitCategory c) {
608 int result = 0;
609 std::map<int, CUnit*>::iterator i;
610
611 for (i = factories.begin(); i != factories.end(); ++i) {
612 if ((c&i->second->type->cats) == c)
613 result++;
614 }
615
616 return result;
617 }
618
gotFactory(unitCategory c)619 bool CUnitTable::gotFactory(unitCategory c) {
620 return factoryCount(c) > 0;
621 }
622
getBuildables(UnitType * ut,unitCategory include,unitCategory exclude,std::multimap<float,UnitType * > & candidates)623 void CUnitTable::getBuildables(UnitType* ut, unitCategory include, unitCategory exclude, std::multimap<float, UnitType*>& candidates) {
624 if (include.none())
625 return;
626
627 unitCategory incEnvCats = (CATS_ENV&include);
628 std::vector<unitCategory> incCats, excCats;
629
630 // split categories...
631 for (unsigned int i = 0; i < cats.size(); i++) {
632 // NOTE: excluding tags have priority over including tags
633 if ((exclude&cats[i]).any())
634 excCats.push_back(cats[i]);
635 else if ((include&cats[i]).any())
636 incCats.push_back(cats[i]);
637 }
638
639 std::map<int, UnitType*>::iterator j;
640 for (j = ut->canBuild.begin(); j != ut->canBuild.end(); ++j) {
641 bool valid = true;
642 unitCategory cat = j->second->cats;
643 for (unsigned int i = 0; i < incCats.size(); i++) {
644 // NOTE: evironment tags are handled differently: if requested
645 // AIR, LAND, SEA & SUB in any combination that means having
646 // at least one match automatically qualifies unit as valid
647 if ((incCats[i]&CATS_ENV).any()) {
648 if (incEnvCats.any()) {
649 // filter by environment tags is active
650 if ((incEnvCats&cat).none()) {
651 valid = false;
652 break;
653 }
654 }
655 }
656 else if ((incCats[i]&cat).none()) {
657 valid = false;
658 break;
659 }
660 }
661
662 if (valid) {
663 /* Filter out excludes */
664 for (unsigned int i = 0; i < excCats.size(); i++) {
665 if ((excCats[i]&cat).any()) {
666 valid = false;
667 break;
668 }
669 }
670
671 if (valid) {
672 float cost = j->second->cost;
673 candidates.insert(std::pair<float,UnitType*>(cost, j->second));
674 }
675 }
676 }
677
678 if (candidates.empty())
679 LOG_WW("CUnitTable::getBuildables no candidates found INCLUDE(" << debugCategories(include) << ") EXCLUDE("<<debugCategories(exclude)<<") for unitdef(" << ut->def->humanName << ")")
680 }
681
canBuild(UnitType * ut,unitCategory c)682 UnitType* CUnitTable::canBuild(UnitType *ut, unitCategory c) {
683 std::map<int, UnitType*>::iterator it;
684 // TODO: make it compatible with environment tags
685 for (it = ut->canBuild.begin(); it != ut->canBuild.end(); it++) {
686 if ((it->second->cats & c) == c)
687 return it->second;
688 }
689
690 //LOG_WW("CUnitTable::canBuild failed to build " << debugCategories(c))
691
692 return NULL;
693 }
694
getUnitByDef(std::map<int,CUnit * > & dic,const UnitDef * udef)695 CUnit* CUnitTable::getUnitByDef(std::map<int, CUnit*>& dic, const UnitDef *udef) {
696 return CUnitTable::getUnitByDef(dic, udef->id);
697 }
698
getUnitByDef(std::map<int,CUnit * > & dic,int did)699 CUnit* CUnitTable::getUnitByDef(std::map<int, CUnit*>& dic, int did) {
700 CUnit* unit;
701 std::map<int, CUnit*>::const_iterator i;
702 for(i = dic.begin(); i != dic.end(); i++) {
703 unit = i->second;
704 if(unit->def->id == did) {
705 return unit;
706 }
707 }
708 return NULL;
709 }
710
getUnitTypeByCats(unitCategory c)711 UnitType* CUnitTable::getUnitTypeByCats(unitCategory c) {
712 std::map<int, UnitType>::iterator it;
713 for (it = units.begin(); it != units.end(); ++it) {
714 if ((it->second.cats&c) == c)
715 return &(it->second);
716 }
717 return NULL;
718 }
719
setOnOff(std::map<int,CUnit * > & list,bool value)720 int CUnitTable::setOnOff(std::map<int, CUnit*>& list, bool value) {
721 int result = 0;
722 std::map<int, CUnit*>::iterator i;
723
724 for (i = list.begin(); i != list.end(); ++i) {
725 CUnit* unit = i->second;
726 if (value != unit->isOn()) {
727 unit->setOnOff(value);
728 result++;
729 }
730 }
731
732 return result;
733 }
734
debugCategories(const unitCategory & categories)735 std::string CUnitTable::debugCategories(const unitCategory& categories) {
736 std::string cats("");
737 UnitCategory2StrMap::iterator i;
738 for (i = cat2str.begin(); i != cat2str.end(); ++i) {
739 unitCategory v = categories & i->first;
740 if (v == i->first)
741 cats += i->second + " | ";
742 }
743 cats = cats.substr(0, cats.length() - 3);
744 return cats;
745 }
746
debugCategories(UnitType * ut)747 std::string CUnitTable::debugCategories(UnitType *ut) {
748 std::string cats("");
749 UnitCategory2StrMap::iterator i;
750 for (i = cat2str.begin(); i != cat2str.end(); ++i) {
751 unitCategory v = ut->cats & i->first;
752 if (v == i->first)
753 cats += i->second + " | ";
754 }
755 cats = cats.substr(0, cats.length() - 3);
756 return cats;
757 }
758
debugUnitDefs(UnitType * ut)759 void CUnitTable::debugUnitDefs(UnitType *ut) {
760 const UnitDef *ud = ut->def;
761 sprintf(buf, "metalUpKeep(%0.2f), metalMake(%0.2f), makesMetal(%0.2f), energyUpkeep(%0.2f), energyMake(%0.2f)\n", ud->metalUpkeep, ud->metalMake, ud->makesMetal, ud->energyUpkeep, ud->energyMake);
762 sprintf(buf, "buildTime(%0.2f), mCost(%0.2f), eCost(%0.2f)\n", ud->buildTime, ud->metalCost, ud->energyCost);
763 }
764
debugWeapons(UnitType * ut)765 void CUnitTable::debugWeapons(UnitType *ut) {
766 const UnitDef *ud = ut->def;
767 for (unsigned int i = 0; i < ud->weapons.size(); i++) {
768 const UnitDef::UnitDefWeapon *w = &(ud->weapons[i]);
769 sprintf(buf, "Weapon name = %s\n", w->def->type.c_str());
770 }
771 }
772