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