1 #include <sstream>
2 
3 #include "IncCREG.h"
4 #include "IncEngine.h"
5 #include "IncExternAI.h"
6 #include "IncGlobalAI.h"
7 
8 CR_BIND(CUnitHandler, (NULL))
9 CR_REG_METADATA(CUnitHandler, (
10 	CR_MEMBER(IdleUnits),
11 	CR_MEMBER(BuildTasks),
12 	CR_MEMBER(TaskPlans),
13 	CR_MEMBER(AllUnitsByCat),
14 	CR_MEMBER(AllUnitsByType),
15 
16 	CR_MEMBER(Factories),
17 	CR_MEMBER(NukeSilos),
18 	CR_MEMBER(MetalExtractors),
19 
20 	CR_MEMBER(Limbo),
21 	CR_MEMBER(BuilderTrackers),
22 
23 	CR_MEMBER(metalMaker),
24 
25 	CR_MEMBER(ai),
26 	CR_MEMBER(taskPlanCounter),
27 	CR_RESERVED(16)
28 ))
29 
30 
CUnitHandler(AIClasses * ai)31 CUnitHandler::CUnitHandler(AIClasses* ai) {
32 	this->ai = ai;
33 
34 	taskPlanCounter = 1;
35 	lastCapturedUnitFrame = -1;
36 	lastCapturedUnitID = -1;
37 
38 	IdleUnits.resize(CAT_LAST);
39 	BuildTasks.resize(CAT_LAST);
40 	TaskPlans.resize(CAT_LAST);
41 	AllUnitsByCat.resize(CAT_LAST);
42 
43 	if (ai) {
44 		AllUnitsByType.resize(ai->cb->GetNumUnitDefs() + 1);
45 		metalMaker = new CMetalMaker(ai);
46 	}
47 }
48 
~CUnitHandler()49 CUnitHandler::~CUnitHandler() {
50 	for (std::list<BuilderTracker*>::iterator i = BuilderTrackers.begin(); i != BuilderTrackers.end(); i++) {
51 		delete *i;
52 	}
53 }
54 
IdleUnitUpdate(int frame)55 void CUnitHandler::IdleUnitUpdate(int frame) {
56 	std::list<integer2> limboremoveunits;
57 
58 	for (std::list<integer2>::iterator i = Limbo.begin(); i != Limbo.end(); i++) {
59 		if (i->y > 0) {
60 			i->y--;
61 		} else {
62 			if (ai->cb->GetUnitDef(i->x) == NULL) {
63 				// ignore dead unit
64 			} else {
65 				IdleUnits[ai->ut->GetCategory(i->x)].push_back(i->x);
66 			}
67 
68 			limboremoveunits.push_back(*i);
69 		}
70 	}
71 
72 	if (limboremoveunits.size()) {
73 		for (std::list<integer2>::iterator i = limboremoveunits.begin(); i != limboremoveunits.end(); i++) {
74 			Limbo.remove(*i);
75 		}
76 	}
77 
78 	// make sure that all the builders are in action (hack?)
79 	if (frame % 15 == 0) {
80 		for (std::list<BuilderTracker*>::iterator i = BuilderTrackers.begin(); i != BuilderTrackers.end(); i++) {
81 			BuilderTracker* bTracker = *i;
82 
83 			if (bTracker->idleStartFrame != -2) {
84 				// the brand new builders must be filtered still
85 				const bool ans = VerifyOrder(bTracker);
86 				const int builderID = bTracker->builderID;
87 				const CCommandQueue* myCommands = ai->cb->GetCurrentUnitCommands(builderID);
88 
89 				// two sec delay is ok
90 				if ((bTracker->commandOrderPushFrame + LAG_ACCEPTANCE) < frame) {
91 					if (!ans) {
92 						//float3 pos = ai->cb->GetUnitPos(builderID);
93 
94 						std::stringstream msg;
95 							msg << "[CUnitHandler::IdleUnitUpdate()][frame=" << frame << "]\n";
96 							msg << "\tfailed to verify order for builder " << builderID;
97 							msg << " with " << (myCommands->size()) << " remaining commands\n";
98 						ai->GetLogger()->Log(msg.str());
99 
100 						ClearOrder(*i, false);
101 
102 						// due to the Legacy AI Wrapper inner workings,
103 						// the command-queue we fetched above is now invalid,
104 						// because it got changed in ClearOrder()
105 						myCommands = ai->cb->GetCurrentUnitCommands(builderID);
106 
107 						if (!myCommands->empty()) {
108 							DecodeOrder(bTracker, true);
109 						} else {
110 							IdleUnitAdd(builderID, frame);
111 						}
112 					}
113 				}
114 			}
115 		}
116 	}
117 }
118 
UnitMoveFailed(int unitID)119 void CUnitHandler::UnitMoveFailed(int unitID) {
120 }
121 
122 // called when unit nanoframe first created
123 // (CEconomyTracker deals with UnitFinished())
UnitCreated(int unitID)124 void CUnitHandler::UnitCreated(int unitID) {
125 	UnitCategory ucat = ai->ut->GetCategory(unitID);
126 	const UnitDef* udef = ai->cb->GetUnitDef(unitID);
127 
128 	if (ucat != CAT_LAST) {
129 		assert(ai->GetUnit(unitID)->isDead);
130 		ai->GetUnit(unitID)->isDead = false;
131 
132 		AllUnitsByCat[ucat].push_back(unitID);
133 		AllUnitsByType[udef->id].push_back(unitID);
134 
135 		if (ucat == CAT_FACTORY) {
136 			FactoryAdd(unitID);
137 		}
138 
139 		BuildTaskCreate(unitID);
140 
141 		if (ucat == CAT_BUILDER) {
142 			// add the new builder
143 			BuilderTracker* builderTracker = new BuilderTracker();
144 			builderTracker->builderID      = unitID;
145 			builderTracker->buildTaskId    = 0;
146 			builderTracker->taskPlanId     = 0;
147 			builderTracker->factoryId      = 0;
148 			builderTracker->stuckCount     = 0;
149 			// under construction
150 			builderTracker->commandOrderPushFrame = -2;
151 			builderTracker->categoryMaker = CAT_LAST;
152 			// wait for the first idle call, as this unit might be under construction
153 			builderTracker->idleStartFrame = -2;
154 			BuilderTrackers.push_back(builderTracker);
155 		}
156 
157 		if (ucat == CAT_MMAKER) {
158 			MMakerAdd(unitID);
159 		}
160 		if (ucat == CAT_MEX) {
161 			MetalExtractorAdd(unitID);
162 		}
163 
164 		if (ucat == CAT_NUKE) {
165 			NukeSiloAdd(unitID);
166 		}
167 	}
168 
169 	if (udef->isCommander && udef->canDGun) {
170 		ai->dgunConHandler->AddController(unitID);
171 	} else {
172 		ai->GetUnit(unitID)->SetFireState(2);
173 	}
174 }
175 
UnitDestroyed(int unitID)176 void CUnitHandler::UnitDestroyed(int unitID) {
177 	UnitCategory category = ai->ut->GetCategory(unitID);
178 	const UnitDef* unitDef = ai->cb->GetUnitDef(unitID);
179 
180 	if (category != CAT_LAST) {
181 		assert(!ai->GetUnit(unitID)->isDead);
182 		ai->GetUnit(unitID)->isDead = true;
183 
184 		AllUnitsByType[unitDef->id].remove(unitID);
185 		AllUnitsByCat[category].remove(unitID);
186 		IdleUnitRemove(unitID);
187 		BuildTaskRemove(unitID);
188 
189 		if (category == CAT_DEFENCE) {
190 			ai->dm->RemoveDefense(ai->cb->GetUnitPos(unitID), unitDef);
191 		}
192 		if (category == CAT_MMAKER) {
193 			MMakerRemove(unitID);
194 		}
195 		if (category == CAT_FACTORY) {
196 			FactoryRemove(unitID);
197 		}
198 
199 		if (category == CAT_BUILDER) {
200 			// remove the builder
201 			for (std::list<BuilderTracker*>::iterator i = BuilderTrackers.begin(); i != BuilderTrackers.end(); i++) {
202 				if ((*i)->builderID == unitID) {
203 					if ((*i)->buildTaskId)
204 						BuildTaskRemove(*i);
205 					if ((*i)->taskPlanId)
206 						TaskPlanRemove(*i);
207 					if ((*i)->factoryId)
208 						FactoryBuilderRemove(*i);
209 
210 					BuilderTracker* builderTracker = *i;
211 					BuilderTrackers.erase(i);
212 					delete builderTracker;
213 					break;
214 				}
215 			}
216 		}
217 
218 		if (category == CAT_MEX) {
219 			MetalExtractorRemove(unitID);
220 		}
221 		if (category == CAT_NUKE) {
222 			NukeSiloRemove(unitID);
223 		}
224 	}
225 }
226 
IdleUnitAdd(int unit,int frame)227 void CUnitHandler::IdleUnitAdd(int unit, int frame) {
228 	UnitCategory category = ai->ut->GetCategory(unit);
229 
230 	if (category != CAT_LAST) {
231 		const CCommandQueue* myCommands = ai->cb->GetCurrentUnitCommands(unit);
232 
233 		if (myCommands->empty()) {
234 			if (category == CAT_BUILDER) {
235 				BuilderTracker* builderTracker = GetBuilderTracker(unit);
236 				// add clear here
237 				ClearOrder(builderTracker, true);
238 
239 				if (builderTracker->idleStartFrame == -2) {
240 					// it was in the idle list already?
241 					IdleUnitRemove(builderTracker->builderID);
242 				}
243 
244 				builderTracker->idleStartFrame = -2;
245 
246 				if (builderTracker->commandOrderPushFrame == -2) {
247 					// make sure that if the unit was just built
248 					// it will have some time to leave the factory
249 					builderTracker->commandOrderPushFrame = frame + 30 * 3;
250 				}
251 			}
252 
253 			integer2 myunit(unit, LIMBOTIME);
254 			Limbo.remove(myunit);
255 			Limbo.push_back(myunit);
256 		} else {
257 			// the unit has orders still, so should not be idle
258 			if (category == CAT_BUILDER) {
259 				if (false) {
260 					// KLOOTNOTE: somehow we are reaching this branch
261 					// on initialization when USE_CREG is not defined,
262 					// mycommands->size() returns garbage?
263 					//
264 					// BuilderTracker* builderTracker = GetBuilderTracker(unit);
265 					// DecodeOrder(builderTracker, true);
266 				}
267 			}
268 		}
269 	}
270 }
271 
VerifyOrder(BuilderTracker * builderTracker)272 bool CUnitHandler::VerifyOrder(BuilderTracker* builderTracker) {
273 	// if it's without orders then try to find the lost command (TODO)
274 	const CCommandQueue* mycommands = ai->cb->GetCurrentUnitCommands(builderTracker->builderID);
275 	bool commandFound = false;
276 	bool hit = false;
277 
278 	if (!mycommands->empty()) {
279 		// it has orders
280 		const Command* c = &mycommands->front();
281 
282 		if (mycommands->size() == 2) {
283 			// it might have a reclaim order, or terrain change order
284 			// take command nr. 2
285 			c = &mycommands->back();
286 		}
287 
288 		if (builderTracker->buildTaskId != 0) {
289 			hit = true;
290 			// test that this builder is on repair on this unit
291 			BuildTask* buildTask = GetBuildTask(builderTracker->buildTaskId);
292 
293 			commandFound =
294 				((c->id        == CMD_REPAIR                 ) &&
295 				 (c->params[0] == builderTracker->buildTaskId)) ||
296 
297 				((c->id       == -buildTask->def->id) &&
298 				 (c->params[0] == buildTask->pos.x  ) &&
299 				 (c->params[2] == buildTask->pos.z  ));
300 
301 			if (!commandFound) {
302 				return false;
303 			}
304 		}
305 
306 		if (builderTracker->taskPlanId != 0) {
307 			assert(!hit);
308 			hit = true;
309 			TaskPlan* taskPlan = GetTaskPlan(builderTracker->taskPlanId);
310 
311 			if ((c->id == -taskPlan->def->id)
312 					&& (c->params[0] == taskPlan->pos.x)
313 					&& (c->params[2] == taskPlan->pos.z))
314 				commandFound = true;
315 			else
316 				return false;
317 		}
318 
319 		if (builderTracker->factoryId != 0) {
320 			assert(!hit);
321 			hit = true;
322 			if (c->id == CMD_GUARD && c->params[0] == builderTracker->factoryId)
323 				commandFound = true;
324 			else
325 				return false;
326 		}
327 
328 		if (!commandFound) {
329 			hit = true;
330 			commandFound = (c->id == CMD_RECLAIM || c->id == CMD_MOVE || c->id == CMD_REPAIR);
331 		}
332 
333 		if (hit && commandFound) {
334 			// it's on the right job
335 			return true;
336 		}
337 	}
338 
339 	else  {
340 		if (builderTracker->idleStartFrame == -2) {
341 			return true;
342 		}
343 	}
344 
345 	return false;
346 }
347 
348 // use this only if the unit does not have any orders at the moment
ClearOrder(BuilderTracker * builderTracker,bool reportError)349 void CUnitHandler::ClearOrder(BuilderTracker* builderTracker, bool reportError) {
350 	bool hit = false;
351 
352 	const int frame        = ai->cb->GetCurrentFrame();
353 	const int builderID    = builderTracker->builderID;
354 	const int buildTaskID  = builderTracker->buildTaskId;
355 	const int factoryID    = builderTracker->factoryId;
356 	const CCommandQueue* q = ai->cb->GetCurrentUnitCommands(builderID);
357 
358 	assert(q->empty() || !reportError);
359 
360 	if (buildTaskID != 0) {
361 		hit = true;
362 		BuildTask* buildTask = GetBuildTask(buildTaskID);
363 
364 		std::stringstream msg;
365 			msg << "[CUnitHandler::ClearOrder()][frame=" << frame << "]\n";
366 			msg << "\tbuilder " << builderID << " is reported idle but";
367 			msg << " still has a build-task with ID " << (buildTaskID) << "\n";
368 		ai->GetLogger()->Log(msg.str());
369 
370 		if (buildTask->builderTrackers.size() > 1) {
371 			BuildTaskRemove(builderTracker);
372 		} else {
373 			// only builder of this thing, and now idle
374 			BuildTaskRemove(builderTracker);
375 		}
376 	}
377 
378 	if (builderTracker->taskPlanId != 0) {
379 		assert(!hit);
380 		hit = true;
381 
382 		const TaskPlan*    taskPlan = GetTaskPlan(builderTracker->taskPlanId);
383 		const std::string& taskName = taskPlan->def->humanName;
384 
385 		std::stringstream msg;
386 			msg << "[CUnitHandler::ClearOrder()][frame=" << frame << "]\n";
387 			msg << "\tbuilder " << builderID << " is reported idle but";
388 			msg << " still has a task-plan named \"" << (taskName) << "\"\n";
389 		ai->GetLogger()->Log(msg.str());
390 
391 		// mask this build-spot as bad
392 		ai->dm->MaskBadBuildSpot(taskPlan->pos);
393 
394 		// TODO: remove all builders from this plan
395 		if (reportError) {
396 			std::list<BuilderTracker*> builderTrackers = taskPlan->builderTrackers;
397 			std::list<BuilderTracker*>::iterator i;
398 
399 			for (i = builderTrackers.begin(); i != builderTrackers.end(); i++) {
400 				TaskPlanRemove(*i);
401 				ai->GetUnit((*i)->builderID)->Stop();
402 			}
403 		} else {
404 			TaskPlanRemove(builderTracker);
405 		}
406 	}
407 
408 	if (factoryID != 0) {
409 		assert(!hit);
410 		hit = true;
411 
412 		std::stringstream msg;
413 			msg << "[CUnitHandler::ClearOrder()][frame=" << frame << "]\n";
414 			msg << "\tbuilder " << builderID << " is reported idle but";
415 			msg << " still has a factory ID of " << factoryID << "\n";
416 		ai->GetLogger()->Log(msg.str());
417 
418 		// remove the builder from its job
419 		FactoryBuilderRemove(builderTracker);
420 	}
421 
422 	assert(builderTracker->buildTaskId == 0);
423 	assert(builderTracker->taskPlanId == 0);
424 	assert(builderTracker->factoryId == 0);
425 }
426 
DecodeOrder(BuilderTracker * builderTracker,bool reportError)427 void CUnitHandler::DecodeOrder(BuilderTracker* builderTracker, bool reportError) {
428 	const int            frame     = ai->cb->GetCurrentFrame();
429 	const int            builderID = builderTracker->builderID;
430 	const CCommandQueue* builderQ  = ai->cb->GetCurrentUnitCommands(builderID);
431 
432 	if (builderQ->size() > 0) {
433 		// builder has orders
434 		const Command* c   = &builderQ->front();
435 		int n = c->params.size();
436 		int cID = c->id;
437 
438 		if (builderQ->size() == 2 && cID == CMD_MOVE) {
439 			// it might have a move order before the real order,
440 			// take command nr. 2 if nr. 1 is a move order
441 			c = &builderQ->back();
442 			n = c->params.size();
443 			cID = c->id;
444 		}
445 
446 		if (reportError) {
447 			std::stringstream msg;
448 				msg << "[CUnitHandler::DecodeOrder()][frame=" << frame << "]\n";
449 				msg << "\tbuilder " << builderID << " claimed idle, but has";
450 				msg << " command " << cID << " with " << n << " parameters";
451 				msg << " (params[0]: " << ((n > 0)? c->params[0]: -1) << ")\n";
452 			ai->GetLogger()->Log(msg.str());
453 		}
454 
455 		if (cID < 0) {
456 			assert(n >= 3);
457 
458 			// it's building a unit
459 			float3 newUnitPos;
460 			newUnitPos.x = c->params[0];
461 			newUnitPos.y = c->params[1];
462 			newUnitPos.z = c->params[2];
463 
464 			const UnitDef* newUnitDef = ai->ut->unitTypes[-cID].def;
465 			// make sure that no BuildTasks exists there
466 			BuildTask* buildTask = BuildTaskExist(newUnitPos, newUnitDef);
467 
468 			if (buildTask) {
469 				BuildTaskAddBuilder(buildTask, builderTracker);
470 			} else {
471 				// make a new TaskPlan (or join an existing one)
472 				TaskPlanCreate(builderID, newUnitPos, newUnitDef);
473 			}
474 		}
475 
476 		if (cID == CMD_REPAIR) {
477 			assert(n >= 1);
478 
479 			// it's repairing, find the unit being repaired
480 			int guardingID = int(c->params[0]);
481 			bool found = false;
482 
483 			UnitCategory cat = ai->ut->GetCategory(guardingID);
484 			std::list<BuildTask>::iterator i;
485 
486 			if (cat == CAT_LAST) {
487 				return;
488 			}
489 
490 			for (i = BuildTasks[cat].begin(); i != BuildTasks[cat].end(); i++) {
491 				if (i->id == guardingID) {
492 					// whatever the old order was, update it now
493 					bool hit = false;
494 					if (builderTracker->buildTaskId != 0) {
495 						hit = true;
496 						// why is this builder idle?
497 						BuildTask* buildTask = GetBuildTask(builderTracker->buildTaskId);
498 
499 						if (buildTask->builderTrackers.size() > 1) {
500 							BuildTaskRemove(builderTracker);
501 						} else {
502 							// only builder of this thing, and now idle?
503 							BuildTaskRemove(builderTracker);
504 						}
505 					}
506 					if (builderTracker->taskPlanId != 0) {
507 						assert(!hit);
508 						hit = true;
509 						TaskPlanRemove(builderTracker);
510 					}
511 					if (builderTracker->factoryId != 0) {
512 						assert(!hit);
513 						hit = true;
514 						FactoryBuilderRemove(builderTracker);
515 					}
516 
517 					BuildTask* bt = &*i;
518 					BuildTaskAddBuilder(bt, builderTracker);
519 					found = true;
520 				}
521 			}
522 			if (!found) {
523 				// not found, just make a custom order
524 				builderTracker->idleStartFrame = -1;
525 			}
526 		}
527 	} else {
528 		// error: this function needs a builder with orders
529 		// should not be possible because IdleUnitUpdate()
530 		// calls us only if a unit's command-queue is NOT
531 		// empty?
532 		// assert(false);
533 		std::stringstream msg;
534 			msg << "[CUnitHandler::DecodeOrder()][frame=" << frame << "]\n";
535 			msg << "\tbuilder " << builderID << " should not have an empty queue!\n";
536 		ai->GetLogger()->Log(msg.str());
537 	}
538 }
539 
IdleUnitRemove(int unit)540 void CUnitHandler::IdleUnitRemove(int unit) {
541 	UnitCategory category = ai->ut->GetCategory(unit);
542 
543 	if (category != CAT_LAST) {
544 		IdleUnits[category].remove(unit);
545 
546 		if (category == CAT_BUILDER) {
547 			BuilderTracker* builderTracker = GetBuilderTracker(unit);
548 			builderTracker->idleStartFrame = -1;
549 
550 			if (builderTracker->commandOrderPushFrame == -2) {
551 				// bad
552 			}
553 
554 			// update the order start frame
555 			builderTracker->commandOrderPushFrame = ai->cb->GetCurrentFrame();
556 			// assert(builderTracker->buildTaskId == 0);
557 			// assert(builderTracker->taskPlanId == 0);
558 			// assert(builderTracker->factoryId == 0);
559 		}
560 
561 		std::list<integer2>::iterator tempunit;
562 		bool found = false;
563 
564 		for (std::list<integer2>::iterator i = Limbo.begin(); i != Limbo.end(); i++) {
565 			if (i->x == unit) {
566 				tempunit = i;
567 				found = true;
568 			}
569 		}
570 
571 		if (found) {
572 			Limbo.erase(tempunit);
573 		}
574 	}
575 }
576 
GetIU(UnitCategory category)577 int CUnitHandler::GetIU(UnitCategory category) {
578 	assert(IdleUnits[category].size() > 0);
579 	int unitID = IdleUnits[category].front();
580 
581 	// move the returned unitID to the back
582 	// of the list, so CBuildUp::FactoryCycle()
583 	// doesn't keep examining the same factory
584 	// if a build order fails and we have more
585 	// than one idle factory
586 	IdleUnits[category].pop_front();
587 	IdleUnits[category].push_back(unitID);
588 
589 	return unitID;
590 }
591 
NumIdleUnits(UnitCategory category)592 int CUnitHandler::NumIdleUnits(UnitCategory category) {
593 	assert(category < CAT_LAST);
594 	IdleUnits[category].sort();
595 	IdleUnits[category].unique();
596 	return IdleUnits[category].size();
597 }
598 
MMakerAdd(int unit)599 void CUnitHandler::MMakerAdd(int unit) {
600 	metalMaker->Add(unit);
601 }
602 
MMakerRemove(int unit)603 void CUnitHandler::MMakerRemove(int unit) {
604 	metalMaker->Remove(unit);
605 }
606 
MMakerUpdate(int frame)607 void CUnitHandler::MMakerUpdate(int frame) {
608 	metalMaker->Update(frame);
609 }
610 
BuildTaskCreate(int id)611 void CUnitHandler::BuildTaskCreate(int id) {
612 	const UnitDef* newUnitDef = ai->cb->GetUnitDef(id);
613 	const UnitCategory category = ai->ut->GetCategory(id);
614 	const float3 pos = ai->cb->GetUnitPos(id);
615 
616 	if ((!newUnitDef->movedata || category == CAT_DEFENCE) && !newUnitDef->canfly && category != CAT_LAST) {
617 		// this needs to change, so that it can make more stuff
618 		if (category >= CAT_LAST) {
619 			return;
620 		}
621 
622 		BuildTask bt;
623 		bt.id = -1;
624 
625 		std::list<TaskPlan>::iterator i;
626 
627 		for (i = TaskPlans[category].begin(); i != TaskPlans[category].end(); i++) {
628 			if (pos.distance2D(i->pos) < 1.0f && newUnitDef == i->def) {
629 				bt.category = category;
630 				bt.id       = id;
631 				bt.pos      = i->pos;
632 				bt.def      = newUnitDef;
633 
634 				std::list<BuilderTracker*> moveList;
635 
636 				for (std::list<BuilderTracker*>::iterator builder = i->builderTrackers.begin(); builder != i->builderTrackers.end(); builder++) {
637 					moveList.push_back(*builder);
638 				}
639 
640 				for (std::list<BuilderTracker*>::iterator builder = moveList.begin(); builder != moveList.end(); builder++) {
641 					TaskPlanRemove(*builder);
642 					BuildTaskAddBuilder(&bt, *builder);
643 				}
644 
645 				// there can not be more than one found TaskPlan
646 				break;
647 			}
648 		}
649 
650 		if (bt.id == -1) {
651 			// buildtask creation error (can happen if builder manages
652 			// to restart a dead building, or a human has taken control),
653 			// make one anyway
654 			std::stringstream msg;
655 				msg << "[CUnitHandler::BuildTaskCreate()][frame=" << ai->cb->GetCurrentFrame() << "]\n";
656 				msg << "\tBuildTask Creation Error for task with ID " << id << "\n";
657 			ai->GetLogger()->Log(msg.str());
658 
659 			if (category == CAT_DEFENCE) {
660 				ai->dm->AddDefense(pos, newUnitDef);
661 			}
662 
663 			bt.category = category;
664 			bt.id = id;
665 			bt.pos = pos;
666 			bt.def = newUnitDef;
667 
668 			// if we have any friendly builders
669 			for (std::list<BuilderTracker*>::iterator i = BuilderTrackers.begin(); i != BuilderTrackers.end(); i++) {
670 				BuilderTracker* builderTracker = *i;
671 
672 				// check what builder is doing
673 				const CCommandQueue* cq = ai->cb->GetCurrentUnitCommands(builderTracker->builderID);
674 
675 				if (!cq->empty()) {
676 					Command c = cq->front();
677 
678 					const bool b0 = (c.id == -newUnitDef->id && c.params[0] == pos.x && c.params[2] == pos.z); // at this pos
679 					const bool b1 = (c.id == CMD_REPAIR && c.params[0] == id); // at this unit (id)
680 					const bool b2 = (c.id == CMD_GUARD  && c.params[0] == id); // at this unit (id)
681 					const bool b3 = b0 || b1 || b2;
682 
683 					if (b3) {
684 						if (builderTracker->buildTaskId != 0) {
685 							BuildTask* buildTask = GetBuildTask(builderTracker->buildTaskId);
686 
687 							if (buildTask->builderTrackers.size() > 1) {
688 								BuildTaskRemove(builderTracker);
689 							} else {
690 								// only builder of this thing, and now idle
691 								BuildTaskRemove(builderTracker);
692 							}
693 						}
694 
695 						if (builderTracker->taskPlanId != 0) {
696 							GetTaskPlan(builderTracker->taskPlanId);
697 							TaskPlanRemove(builderTracker);
698 						}
699 						if (builderTracker->factoryId != 0) {
700 							FactoryBuilderRemove(builderTracker);
701 						}
702 
703 						// this builder is now free
704 						if (builderTracker->idleStartFrame == -2) {
705 							IdleUnitRemove(builderTracker->builderID);
706 						}
707 
708 						// add it to this task
709 						BuildTaskAddBuilder(&bt, builderTracker);
710 
711 						msg.str("");
712 							msg << "\tadded builder " << builderTracker->builderID << " to";
713 							msg << " build-task with ID " << builderTracker->buildTaskId << "\n";
714 						ai->GetLogger()->Log(msg.str());
715 					}
716 				}
717 			}
718 
719 			// add the task anyway
720 			BuildTasks[category].push_back(bt);
721 		}
722 		else {
723 			if (category == CAT_DEFENCE)
724 				ai->dm->AddDefense(pos,newUnitDef);
725 
726 			BuildTasks[category].push_back(bt);
727 		}
728 	}
729 }
730 
BuildTaskRemove(int id)731 void CUnitHandler::BuildTaskRemove(int id) {
732 	UnitCategory category = ai->ut->GetCategory(id);
733 
734 	if (category < CAT_LAST) {
735 		std::list<BuildTask>::iterator killtask;
736 		bool found = false;
737 
738 		for (std::list<BuildTask>::iterator i = BuildTasks[category].begin(); i != BuildTasks[category].end(); i++) {
739 			if (i->id == id) {
740 				killtask = i;
741 				assert(!found);
742 				found = true;
743 			}
744 		}
745 		if (found) {
746 			// remove the builders from this BuildTask
747 			std::list<BuilderTracker*> removeList;
748 			for (std::list<BuilderTracker*>::iterator builder = killtask->builderTrackers.begin(); builder != killtask->builderTrackers.end(); builder++) {
749 				removeList.push_back(*builder);
750 			}
751 			for (std::list<BuilderTracker*>::iterator builder = removeList.begin(); builder != removeList.end(); builder++) {
752 				BuildTaskRemove(*builder);
753 			}
754 
755 			BuildTasks[category].erase(killtask);
756 		}
757 	}
758 }
759 
GetBuilderTracker(int builderID)760 BuilderTracker* CUnitHandler::GetBuilderTracker(int builderID) {
761 	for (std::list<BuilderTracker*>::iterator i = BuilderTrackers.begin(); i != BuilderTrackers.end(); i++) {
762 		if ((*i)->builderID == builderID) {
763 			return (*i);
764 		}
765 	}
766 
767 	return NULL;
768 }
769 
BuildTaskRemove(BuilderTracker * builderTracker)770 void CUnitHandler::BuildTaskRemove(BuilderTracker* builderTracker) {
771 	if (builderTracker->buildTaskId == 0) {
772 		assert(false);
773 		return;
774 	}
775 
776 	UnitCategory category = ai->ut->GetCategory(builderTracker->buildTaskId);
777 
778 	// TODO: Hack fix
779 	if (category == CAT_LAST) {
780 		return;
781 	}
782 
783 	assert(category >= 0);
784 	assert(category < CAT_LAST);
785 	assert(builderTracker->buildTaskId != 0);
786 	assert(builderTracker->taskPlanId == 0);
787 	assert(builderTracker->factoryId == 0);
788 
789 	bool found = false;
790 	bool found2 = false;
791 
792 	for (std::list<BuildTask>::iterator i = BuildTasks[category].begin(); i != BuildTasks[category].end(); i++) {
793 		if (i->id == builderTracker->buildTaskId){
794 			// killtask = i;
795 			assert(!found);
796 			for (std::list<int>::iterator builder = i->builders.begin(); builder != i->builders.end(); builder++) {
797 				if (builderTracker->builderID == *builder) {
798 					assert(!found2);
799 					i->builders.erase(builder);
800 					builderTracker->buildTaskId = 0;
801 					found2 = true;
802 					break;
803 				}
804 			}
805 			for (std::list<BuilderTracker*>::iterator builder = i->builderTrackers.begin(); builder != i->builderTrackers.end(); builder++) {
806 				if (builderTracker == *builder) {
807 					assert(!found);
808 					i->builderTrackers.erase(builder);
809 					builderTracker->buildTaskId = 0;
810 					// give it time to change command
811 					builderTracker->commandOrderPushFrame = ai->cb->GetCurrentFrame();
812 					found = true;
813 					break;
814 				}
815 			}
816 
817 		}
818 	}
819 
820 	assert(found);
821 }
822 
BuildTaskAddBuilder(BuildTask * buildTask,BuilderTracker * builderTracker)823 void CUnitHandler::BuildTaskAddBuilder(BuildTask* buildTask, BuilderTracker* builderTracker) {
824 	buildTask->builders.push_back(builderTracker->builderID);
825 	buildTask->builderTrackers.push_back(builderTracker);
826 	buildTask->currentBuildPower += ai->cb->GetUnitDef(builderTracker->builderID)->buildSpeed;
827 	assert(builderTracker->buildTaskId == 0);
828 	assert(builderTracker->taskPlanId == 0);
829 	assert(builderTracker->factoryId == 0);
830 
831 	builderTracker->buildTaskId = buildTask->id;
832 }
833 
GetBuildTask(int buildTaskId)834 BuildTask* CUnitHandler::GetBuildTask(int buildTaskId) {
835 	for (int k = 0; k < CAT_LAST; k++) {
836 		for (std::list<BuildTask>::iterator i = BuildTasks[k].begin(); i != BuildTasks[k].end(); i++) {
837 			if (i->id == buildTaskId)
838 				return  &*i;
839 		}
840 	}
841 
842 	// this better not happen
843 	assert(false);
844 	return 0;
845 }
846 
BuildTaskExist(float3 pos,const UnitDef * builtdef)847 BuildTask* CUnitHandler::BuildTaskExist(float3 pos, const UnitDef* builtdef) {
848 	UnitCategory category = ai->ut->GetCategory(builtdef);
849 
850 	if (category >= CAT_LAST) {
851 		return NULL;
852 	}
853 
854 	for (std::list<BuildTask>::iterator i = BuildTasks[category].begin(); i != BuildTasks[category].end(); i++) {
855 		if (i->pos.distance2D(pos) < 1 && ai->ut->GetCategory(i->def) == category) {
856 			return &*i;
857 		}
858 	}
859 
860 	return NULL;
861 }
862 
BuildTaskAddBuilder(int builderID,UnitCategory category)863 bool CUnitHandler::BuildTaskAddBuilder(int builderID, UnitCategory category) {
864 	assert(category < CAT_LAST);
865 	assert(builderID >= 0);
866 	assert(ai->GetUnit(builderID) != NULL);
867 
868 	CUNIT* u = ai->GetUnit(builderID);
869 	BuilderTracker* builderTracker = GetBuilderTracker(builderID);
870 
871 	const UnitDef* builderDef = ai->cb->GetUnitDef(builderID);
872 	const int frame = ai->cb->GetCurrentFrame();
873 
874 	// make sure this builder is free
875 	const bool b1 = (builderTracker->taskPlanId == 0);
876 	const bool b2 = (builderTracker->buildTaskId == 0);
877 	const bool b3 = (builderTracker->factoryId == 0);
878 	const bool b4 = builderDef->canAssist;
879 	const bool b5 = (category == CAT_FACTORY && frame >= 18000);
880 
881 	if (!b1 || !b2 || !b3 || !b4) {
882 		if (b5) {
883 			// note that FactoryBuilderAdd() asserts b1 through b4
884 			// immediately after BuildTaskAddBuilder() is tried and
885 			// fails in BuildUp(), so at least those must be true
886 			// (and so should b5 in most of the *A mods)
887 
888 			std::stringstream msg;
889 				msg << "[CUnitHandler::BuildTaskAddBuilder()][frame=" << frame << "]\n";
890 				msg << "\tbuilder " << builderID << " not able to be added to CAT_FACTORY build-task\n";
891 				msg << "\tb1: " << b1 << ", b2: " << b2 << ", b3: " << b3;
892 				msg << ", b4: " << b4 << ", b5: " << b5;
893 			ai->GetLogger()->Log(msg.str());
894 		}
895 
896 		return false;
897 	}
898 
899 	// see if there are any BuildTasks that it can join
900 	if (BuildTasks[category].size() > 0) {
901 		float largestTime = 0.0f;
902 		std::list<BuildTask>::iterator task;
903 		std::list<BuildTask>::iterator bestTask;
904 
905 		for (task = BuildTasks[category].begin(); task != BuildTasks[category].end(); task++) {
906 			float buildTime = ai->math->ETT(*task) - ai->math->ETA(builderID, ai->cb->GetUnitPos(task->id));
907 
908 			if (buildTime > largestTime) {
909 				largestTime = buildTime;
910 				bestTask = task;
911 			}
912 		}
913 
914 		if (largestTime > 0.0f) {
915 			BuildTaskAddBuilder(&*bestTask, builderTracker);
916 			u->Repair(bestTask->id);
917 			return true;
918 		}
919 	}
920 
921 	// see if there any joinable TaskPlans
922 	if (TaskPlans[category].size() > 0) {
923 		float largestTime = 0.0f;
924 		std::list<TaskPlan>::iterator plan;
925 		std::list<TaskPlan>::iterator bestPlan;
926 
927 		for (plan = TaskPlans[category].begin(); plan != TaskPlans[category].end(); plan++) {
928 			float buildTime = (plan->def->buildTime / plan->currentBuildPower) - ai->math->ETA(builderID, plan->pos);
929 
930 			// must test if this builder can make this unit/building too
931 			if (buildTime > largestTime) {
932 				const std::vector<int>* canBuildList = &ai->ut->unitTypes[builderDef->id].canBuildList;
933 				const int buildListSize = canBuildList->size();
934 
935 				for (int j = 0; j < buildListSize; j++) {
936 					if (canBuildList->at(j) == plan->def->id) {
937 						largestTime = buildTime;
938 						bestPlan = plan;
939 						break;
940 					}
941 				}
942 			}
943 		}
944 
945 		if (largestTime > 10.0f) {
946 			assert(builderID >= 0);
947 
948 			// bad, CUNIT::Build() uses TaskPlanCreate()
949 			// should we really give build orders here?
950 			// return u->Build(bestPlan->pos, bestPlan->def, -1);
951 			// TaskPlanCreate(builderID, bestPlan->pos, bestPlan->def);
952 			return true;
953 		}
954 	}
955 
956 	if (b5) {
957 		std::stringstream msg;
958 			msg << "[CUnitHandler::BuildTaskAddBuilder()][frame=" << frame << "]\n";
959 			msg << "\tno joinable CAT_FACTORY build-tasks or task-plans for builder " << builderID;
960 		ai->GetLogger()->Log(msg.str());
961 	}
962 
963 	return false;
964 }
965 
TaskPlanCreate(int builder,float3 pos,const UnitDef * builtdef)966 void CUnitHandler::TaskPlanCreate(int builder, float3 pos, const UnitDef* builtdef) {
967 	UnitCategory category = ai->ut->GetCategory(builtdef);
968 
969 	// HACK
970 	if (category >= CAT_LAST) {
971 		return;
972 	}
973 
974 	// find this builder
975 	BuilderTracker* builderTracker = GetBuilderTracker(builder);
976 
977 	// make sure this builder is free
978 	bool b1 = (builderTracker->taskPlanId == 0);
979 	bool b2 = (builderTracker->buildTaskId == 0);
980 	bool b3 = (builderTracker->factoryId == 0);
981 
982 	if (!b1 || !b2 || !b3) {
983 		return;
984 	}
985 
986 
987 	bool existingTP = false;
988 	for (std::list<TaskPlan>::iterator i = TaskPlans[category].begin(); i != TaskPlans[category].end(); i++) {
989 		if (pos.distance2D(i->pos) < 20.0f && builtdef == i->def) {
990 			// make sure there are no other TaskPlans
991 			if (!existingTP) {
992 				existingTP = true;
993 				TaskPlanAdd(&*i, builderTracker);
994 			} else {
995 				std::stringstream msg;
996 					msg << "[CUnitHandler::TaskPlanCreate()][frame=" << ai->cb->GetCurrentFrame() << "]\n";
997 					msg << "\ttask-plan for \"" << builtdef->humanName << "\" already present";
998 					msg << " at position <" << pos.x << ", " << pos.y << ", " << pos.z << ">\n";
999 				ai->GetLogger()->Log(msg.str());
1000 			}
1001 		}
1002 	}
1003 	if (!existingTP) {
1004 		TaskPlan tp;
1005 		tp.pos = pos;
1006 		tp.def = builtdef;
1007 		tp.defName = builtdef->name;
1008 		tp.currentBuildPower = 0;
1009 		tp.id = taskPlanCounter++;
1010 		TaskPlanAdd(&tp, builderTracker);
1011 
1012 		if (category == CAT_DEFENCE)
1013 			ai->dm->AddDefense(pos, builtdef);
1014 
1015 		TaskPlans[category].push_back(tp);
1016 	}
1017 }
1018 
TaskPlanAdd(TaskPlan * taskPlan,BuilderTracker * builderTracker)1019 void CUnitHandler::TaskPlanAdd(TaskPlan* taskPlan, BuilderTracker* builderTracker) {
1020 	taskPlan->builders.push_back(builderTracker->builderID);
1021 	taskPlan->builderTrackers.push_back(builderTracker);
1022 	taskPlan->currentBuildPower += ai->cb->GetUnitDef(builderTracker->builderID)->buildSpeed;
1023 	assert(builderTracker->buildTaskId == 0);
1024 	assert(builderTracker->taskPlanId == 0);
1025 	assert(builderTracker->factoryId == 0);
1026 
1027 	builderTracker->taskPlanId = taskPlan->id;
1028 }
1029 
TaskPlanRemove(BuilderTracker * builderTracker)1030 void CUnitHandler::TaskPlanRemove(BuilderTracker* builderTracker) {
1031 	std::list<TaskPlan>::iterator killplan;
1032 	std::list<int>::iterator killBuilder;
1033 	// make sure this builder is in a TaskPlan
1034 	assert(builderTracker->buildTaskId == 0);
1035 	assert(builderTracker->taskPlanId != 0);
1036 	assert(builderTracker->factoryId == 0);
1037 
1038 	builderTracker->taskPlanId = 0;
1039 	int builder = builderTracker->builderID;
1040 	bool found = false;
1041 	bool found2 = false;
1042 
1043 	for (int k = 0; k < CAT_LAST; k++) {
1044 		for (std::list<TaskPlan>::iterator i = TaskPlans[k].begin(); i != TaskPlans[k].end(); i++) {
1045 			for (std::list<int>::iterator j = i->builders.begin(); j != i->builders.end(); j++) {
1046 				if (*j == builder) {
1047 					killplan = i;
1048 					killBuilder = j;
1049 					assert(!found);
1050 					found = true;
1051 					found2 = true;
1052 				}
1053 			}
1054 		}
1055 		if (found2) {
1056 			for (std::list<BuilderTracker*>::iterator i = killplan->builderTrackers.begin(); i != killplan->builderTrackers.end(); i++) {
1057 				if (builderTracker == *i) {
1058 					// give it time to change command
1059 					builderTracker->commandOrderPushFrame = ai->cb->GetCurrentFrame();
1060 					killplan->builderTrackers.erase(i);
1061 					break;
1062 				}
1063 			}
1064 
1065 			killplan->builders.erase(killBuilder);
1066 
1067 			if (killplan->builders.size() == 0) {
1068 				// TaskPlan lost all its workers, remove
1069 				if (ai->ut->GetCategory(killplan->def) == CAT_DEFENCE)
1070 					ai->dm->RemoveDefense(killplan->pos, killplan->def);
1071 
1072 				TaskPlans[k].erase(killplan);
1073 			}
1074 
1075 			found2 = false;
1076 		}
1077 	}
1078 
1079 	if (!found) {
1080 		assert(false);
1081 	}
1082 }
1083 
GetTaskPlan(int taskPlanId)1084 TaskPlan* CUnitHandler::GetTaskPlan(int taskPlanId) {
1085 	for (int k = 0; k < CAT_LAST;k++) {
1086 		for (std::list<TaskPlan>::iterator i = TaskPlans[k].begin(); i != TaskPlans[k].end(); i++) {
1087 			if (i->id == taskPlanId)
1088 				return  &*i;
1089 		}
1090 	}
1091 
1092 	// this better not happen
1093 	assert(false);
1094 	return 0;
1095 }
1096 
TaskPlanExist(float3 pos,const UnitDef * builtdef)1097 bool CUnitHandler::TaskPlanExist(float3 pos, const UnitDef* builtdef) {
1098 	UnitCategory category = ai->ut->GetCategory(builtdef);
1099 
1100 	if (category >= CAT_LAST) {
1101 		return false;
1102 	}
1103 
1104 	for (std::list<TaskPlan>::iterator i = TaskPlans[category].begin(); i != TaskPlans[category].end(); i++) {
1105 		if (i->pos.distance2D(pos) < 100 && ai->ut->GetCategory(i->def) == category) {
1106 			return true;
1107 		}
1108 	}
1109 
1110 	return false;
1111 }
1112 
MetalExtractorAdd(int extractorID)1113 void CUnitHandler::MetalExtractorAdd(int extractorID) {
1114 	if (ai->ut->GetCategory(extractorID) == CAT_MEX) {
1115 		MetalExtractor newMex;
1116 		newMex.id = extractorID;
1117 		newMex.buildFrame = ai->cb->GetCurrentFrame();
1118 		MetalExtractors.push_back(newMex);
1119 	} else {
1120 		assert(false);
1121 	}
1122 }
1123 
MetalExtractorRemove(int extractorID)1124 void CUnitHandler::MetalExtractorRemove(int extractorID) {
1125 	for (std::vector<MetalExtractor>::iterator i = MetalExtractors.begin(); i != MetalExtractors.end(); i++) {
1126 		if (i->id == extractorID) {
1127 			MetalExtractors.erase(i);
1128 			break;
1129 		}
1130 	}
1131 }
1132 
CompareExtractors(const MetalExtractor & l,const MetalExtractor & r)1133 inline bool CompareExtractors(const MetalExtractor& l, const MetalExtractor& r) {
1134 	// higher frame means more recently built
1135 	return (l.buildFrame < r.buildFrame);
1136 }
1137 
1138 // returns the ID of the oldest (in
1139 // frames) metal extractor built
GetOldestMetalExtractor(void)1140 int CUnitHandler::GetOldestMetalExtractor(void) {
1141 	std::sort(MetalExtractors.begin(), MetalExtractors.end(), &CompareExtractors);
1142 
1143 	return (MetalExtractors.size() > 0)? (MetalExtractors.begin())->id: -1;
1144 }
1145 
NukeSiloAdd(int siloID)1146 void CUnitHandler::NukeSiloAdd(int siloID) {
1147 	if (ai->ut->GetCategory(siloID) == CAT_NUKE) {
1148 		NukeSilo newSilo;
1149 		newSilo.id = siloID;
1150 		newSilo.numNukesReady = 0;
1151 		newSilo.numNukesQueued = 0;
1152 		NukeSilos.push_back(newSilo);
1153 	} else {
1154 		assert(false);
1155 	}
1156 }
1157 
NukeSiloRemove(int siloID)1158 void CUnitHandler::NukeSiloRemove(int siloID) {
1159 	for (std::list<NukeSilo>::iterator i = NukeSilos.begin(); i != NukeSilos.end(); i++) {
1160 		if (i->id == siloID) {
1161 			NukeSilos.erase(i);
1162 			break;
1163 		}
1164 	}
1165 }
1166 
1167 // add a new factory
FactoryAdd(int factory)1168 void CUnitHandler::FactoryAdd(int factory) {
1169 	if (ai->ut->GetCategory(factory) == CAT_FACTORY) {
1170 		Factory newFact;
1171 		newFact.id = factory;
1172 		Factories.push_back(newFact);
1173 	} else {
1174 		assert(false);
1175 	}
1176 }
1177 
FactoryRemove(int id)1178 void CUnitHandler::FactoryRemove(int id) {
1179 	std::list<Factory>::iterator iter;
1180 	bool factoryFound = false;
1181 
1182 	for (std::list<Factory>::iterator i = Factories.begin(); i != Factories.end(); i++) {
1183 		if (i->id == id) {
1184 			iter = i;
1185 			factoryFound = true;
1186 			break;
1187 		}
1188 	}
1189 	if (factoryFound) {
1190 		// remove all builders from this plan
1191 		std::list<BuilderTracker*> builderTrackers = iter->supportBuilderTrackers;
1192 
1193 		for (std::list<BuilderTracker*>::iterator i = builderTrackers.begin(); i != builderTrackers.end(); i++) {
1194 			FactoryBuilderRemove(*i);
1195 		}
1196 
1197 		Factories.erase(iter);
1198 	}
1199 }
1200 
FactoryBuilderAdd(int builderID)1201 bool CUnitHandler::FactoryBuilderAdd(int builderID) {
1202 	CUNIT* unit = ai->GetUnit(builderID);
1203 	BuilderTracker* builderTracker = GetBuilderTracker(builderID);
1204 	return ((unit->def()->canAssist) && FactoryBuilderAdd(builderTracker));
1205 }
1206 
FactoryBuilderAdd(BuilderTracker * builderTracker)1207 bool CUnitHandler::FactoryBuilderAdd(BuilderTracker* builderTracker) {
1208 	assert(builderTracker->buildTaskId == 0);
1209 	assert(builderTracker->taskPlanId == 0);
1210 	assert(builderTracker->factoryId == 0);
1211 
1212 	for (std::list<Factory>::iterator i = Factories.begin(); i != Factories.end(); i++) {
1213 		CUNIT* u = ai->GetUnit(i->id);
1214 
1215 		// don't assist hubs (or factories that cannot be assisted)
1216 		if ((u->def())->canBeAssisted && !u->isHub()) {
1217 			float totalBuilderCost = 0.0f;
1218 
1219 			// HACK: get the sum of the heuristic costs of every
1220 			// builder that is already assisting this factory
1221 			for (std::list<int>::iterator j = i->supportbuilders.begin(); j != i->supportbuilders.end(); j++) {
1222 				if ((ai->GetUnit(*j)->def())->isCommander) {
1223 					continue;
1224 				}
1225 
1226 				totalBuilderCost += ai->math->GetUnitCost(*j);
1227 			}
1228 
1229 			// if this sum is less than the heuristic cost of the
1230 			// factory itself, add the builder to this factory
1231 			//
1232 			// this is based purely on the metal and energy costs
1233 			// of all involved parties, and silently expects that
1234 			// building _another_ factory would always be better
1235 			// than assisting it further
1236 			if (totalBuilderCost < (ai->math->GetUnitCost(i->id) * BUILDERFACTORYCOSTRATIO * 2.5f)) {
1237 				builderTracker->factoryId = i->id;
1238 				i->supportbuilders.push_back(builderTracker->builderID);
1239 				i->supportBuilderTrackers.push_back(builderTracker);
1240 				ai->GetUnit(builderTracker->builderID)->Guard(i->id);
1241 				return true;
1242 			}
1243 		}
1244 	}
1245 
1246 	return false;
1247 }
1248 
FactoryBuilderRemove(BuilderTracker * builderTracker)1249 void CUnitHandler::FactoryBuilderRemove(BuilderTracker* builderTracker) {
1250 	assert(builderTracker->buildTaskId == 0);
1251 	assert(builderTracker->taskPlanId == 0);
1252 	assert(builderTracker->factoryId != 0);
1253 
1254 	std::list<Factory>::iterator killfactory;
1255 
1256 	for (std::list<Factory>::iterator i = Factories.begin(); i != Factories.end(); i++) {
1257 		if (builderTracker->factoryId == i->id) {
1258 			i->supportbuilders.remove(builderTracker->builderID);
1259 			i->supportBuilderTrackers.remove(builderTracker);
1260 			builderTracker->factoryId = 0;
1261 			// give it time to change command
1262 			builderTracker->commandOrderPushFrame = ai->cb->GetCurrentFrame();
1263 		}
1264 	}
1265 }
1266 
BuilderReclaimOrder(int builderId,const float3 &)1267 void CUnitHandler::BuilderReclaimOrder(int builderId, const float3&) {
1268 	BuilderTracker* builderTracker = GetBuilderTracker(builderId);
1269 	assert(builderTracker->buildTaskId == 0);
1270 	assert(builderTracker->taskPlanId == 0);
1271 	assert(builderTracker->factoryId == 0);
1272 
1273 	taskPlanCounter++;
1274 }
1275 
CreateUpgradeTask(int oldBuildingID,const float3 & oldBuildingPos,const UnitDef * newBuildingDef)1276 UpgradeTask* CUnitHandler::CreateUpgradeTask(int oldBuildingID, const float3& oldBuildingPos, const UnitDef* newBuildingDef) {
1277 	assert(FindUpgradeTask(oldBuildingID) == NULL);
1278 
1279 	// UpdateTasks() handles the deletion
1280 	UpgradeTask* task = new UpgradeTask(oldBuildingID, ai->cb->GetCurrentFrame(), oldBuildingPos, newBuildingDef);
1281 	upgradeTasks[oldBuildingID] = task;
1282 
1283 	return task;
1284 }
1285 
FindUpgradeTask(int oldBuildingID)1286 UpgradeTask* CUnitHandler::FindUpgradeTask(int oldBuildingID) {
1287 	std::map<int, UpgradeTask*>::iterator it = upgradeTasks.find(oldBuildingID);
1288 
1289 	if (it != upgradeTasks.end()) {
1290 		return (it->second);
1291 	}
1292 
1293 	return NULL;
1294 }
1295 
RemoveUpgradeTask(UpgradeTask * task)1296 void CUnitHandler::RemoveUpgradeTask(UpgradeTask* task) {
1297 	assert(task != NULL);
1298 	assert(FindUpgradeTask(task->oldBuildingID) != NULL);
1299 
1300 	upgradeTasks.erase(task->oldBuildingID);
1301 	delete task;
1302 }
1303 
AddUpgradeTaskBuilder(UpgradeTask * task,int builderID)1304 bool CUnitHandler::AddUpgradeTaskBuilder(UpgradeTask* task, int builderID) {
1305 	std::set<int>::iterator it = task->builderIDs.find(builderID);
1306 
1307 	if (it == task->builderIDs.end()) {
1308 		task->builderIDs.insert(builderID);
1309 		return true;
1310 	}
1311 
1312 	return false;
1313 }
1314 
UpdateUpgradeTasks(int frame)1315 void CUnitHandler::UpdateUpgradeTasks(int frame) {
1316 	std::map<int, UpgradeTask*>::iterator upgradeTaskIt;
1317 
1318 	std::list<UpgradeTask*> deadTasks;
1319 	std::list<UpgradeTask*>::iterator deadTasksIt;
1320 
1321 	for (upgradeTaskIt = upgradeTasks.begin(); upgradeTaskIt != upgradeTasks.end(); upgradeTaskIt++) {
1322 		UpgradeTask* task = upgradeTaskIt->second;
1323 
1324 		const int oldBuildingID = task->oldBuildingID;
1325 		const bool oldBuildingDead =
1326 			(ai->cb->GetUnitDef(oldBuildingID) == NULL) ||
1327 			(ai->cb->GetUnitHealth(oldBuildingID) < 0.0f);
1328 
1329 		std::set<int>::iterator builderIDsIt;
1330 		std::list<int> deadBuilderIDs;
1331 		std::list<int>::iterator deadBuilderIDsIt;
1332 
1333 		for (builderIDsIt = task->builderIDs.begin(); builderIDsIt != task->builderIDs.end(); builderIDsIt++) {
1334 			const int builderID = *builderIDsIt;
1335 			const bool builderDead =
1336 				(ai->cb->GetUnitDef(builderID) == NULL) ||
1337 				(ai->cb->GetUnitHealth(builderID) <= 0.0f);
1338 
1339 			if (builderDead) {
1340 				deadBuilderIDs.push_back(builderID);
1341 			} else {
1342 				const CCommandQueue* cq = ai->cb->GetCurrentUnitCommands(builderID);
1343 
1344 				if (oldBuildingDead) {
1345 					if (!task->reclaimStatus) {
1346 						task->creationFrame = frame;
1347 						task->reclaimStatus = true;
1348 					}
1349 
1350 					// give a build order for the replacement structure
1351 					if (cq->size() == 0 || ((cq->front()).id != -task->newBuildingDef->id)) {
1352 						std::stringstream msg;
1353 							msg << "[CUnitHandler::UpdateUpgradeTasks()][frame=" << frame << "]\n";
1354 							msg << "\tgiving build order for \"" << task->newBuildingDef->humanName;
1355 							msg << "\" to builder " << builderID << "\n";
1356 						ai->GetLogger()->Log(msg.str());
1357 
1358 						ai->GetUnit(builderID)->Build_ClosestSite(task->newBuildingDef, task->oldBuildingPos);
1359 					}
1360 				} else {
1361 					// give a reclaim order for the original structure
1362 					if (cq->size() == 0 || ((cq->front()).id != CMD_RECLAIM)) {
1363 						std::stringstream msg;
1364 							msg << "[CUnitHandler::UpdateUpgradeTasks()][frame=" << frame << "]\n";
1365 							msg << "\tgiving reclaim order for \"" << ai->cb->GetUnitDef(oldBuildingID)->humanName;
1366 							msg << "\" to builder " << builderID << "\n";
1367 						ai->GetLogger()->Log(msg.str());
1368 
1369 						ai->GetUnit(builderID)->Reclaim(oldBuildingID);
1370 					}
1371 				}
1372 			}
1373 		}
1374 
1375 
1376 		// filter any dead builders from the upgrade task
1377 		// (probably should tie this into UnitDestroyed())
1378 		for (deadBuilderIDsIt = deadBuilderIDs.begin(); deadBuilderIDsIt != deadBuilderIDs.end(); deadBuilderIDsIt++) {
1379 			task->builderIDs.erase(*deadBuilderIDsIt);
1380 		}
1381 
1382 		if (oldBuildingDead) {
1383 			// all builders have been given a build order
1384 			// for the replacement structure at this point,
1385 			// so the task itself is no longer needed
1386 			deadTasks.push_back(task);
1387 		}
1388 	}
1389 
1390 	for (deadTasksIt = deadTasks.begin(); deadTasksIt != deadTasks.end(); deadTasksIt++) {
1391 		RemoveUpgradeTask(*deadTasksIt);
1392 	}
1393 }
1394