1 #include "CUnit.h"
2 
3 #include "CAI.h"
4 #include "CUnitTable.h"
5 #include "CRNG.h"
6 
remove()7 void CUnit::remove() {
8 	remove(*this);
9 }
10 
remove(ARegistrar & reg)11 void CUnit::remove(ARegistrar &reg) {
12 	LOG_II("CUnit::remove " << (*this))
13 
14 	std::list<ARegistrar*>::iterator i = records.begin();
15 	while(i != records.end()) {
16 		ARegistrar *regobj = *i; ++i;
17 		// remove from CUnitTable, CGroup
18 		regobj->remove(reg);
19 	}
20 
21 	assert(records.empty());
22 
23 	// TODO: remove next line when prev assertion is never raised
24 	records.clear();
25 }
26 
reset(int uid,int bid)27 void CUnit::reset(int uid, int bid) {
28 	records.clear();
29 	this->key     = uid;
30 	this->def     = ai->cb->GetUnitDef(uid);
31 	this->type    = UT(def->id);
32 	this->builtBy = bid;
33 	this->waiting = false;
34 	this->microing= false;
35 	this->techlvl = TECH1;
36 	this->group = NULL;
37 	this->aliveFrames = 0;
38 	this->microingFrames = 0;
39 }
40 
isEconomy()41 bool CUnit::isEconomy() {
42 	static const unitCategory economic =
43 		FACTORY|BUILDER|ASSISTER|RESURRECTOR|COMMANDER|MEXTRACTOR|MMAKER
44 		|EMAKER|MSTORAGE|ESTORAGE;
45 	return (type->cats&economic).any();
46 }
47 
reclaim(float3 pos,float radius)48 bool CUnit::reclaim(float3 pos, float radius) {
49 	Command c = createPosCommand(CMD_RECLAIM, pos, radius);
50 	if (c.id != 0) {
51 		ai->cb->GiveOrder(key, &c);
52 		ai->unittable->idle[key] = false;
53 		return true;
54 	}
55 	return false;
56 }
57 
reclaim(int target,bool enqueue)58 bool CUnit::reclaim(int target, bool enqueue) {
59 	Command c = createTargetCommand(CMD_RECLAIM, target);
60 	if (c.id != 0) {
61 		if (enqueue)
62 			c.options |= SHIFT_KEY;
63 		ai->cb->GiveOrder(key, &c);
64 		ai->unittable->idle[key] = false;
65 		return true;
66 	}
67 	return false;
68 }
69 
queueSize()70 int CUnit::queueSize() {
71 	return ai->cb->GetCurrentUnitCommands(key)->size();
72 }
73 
attack(int target,bool enqueue)74 bool CUnit::attack(int target, bool enqueue) {
75 	Command c = createTargetCommand(CMD_ATTACK, target);
76 	if (c.id != 0) {
77 		if (enqueue)
78 			c.options |= SHIFT_KEY;
79 		ai->cb->GiveOrder(key, &c);
80 		ai->unittable->idle[key] = false;
81 		return true;
82 	}
83 	return false;
84 }
85 
moveForward(float dist,bool enqueue)86 bool CUnit::moveForward(float dist, bool enqueue) {
87 	return move(getForwardPos(dist), enqueue);
88 }
89 
90 
setOnOff(bool on)91 bool CUnit::setOnOff(bool on) {
92 	Command c = createTargetCommand(CMD_ONOFF, on);
93 
94 	if (c.id != 0) {
95 		ai->cb->GiveOrder(key, &c);
96 		return true;
97 	}
98 
99 	return false;
100 }
101 
moveRandom(float radius,bool enqueue)102 bool CUnit::moveRandom(float radius, bool enqueue) {
103 	float3 pos = ai->cb->GetUnitPos(key);
104 	float3 newpos(rng.RandFloat(2.0f) - 1.0f, 0.0f, rng.RandFloat(2.0f) - 1.0f);
105 	newpos.Normalize();
106 	newpos   *= radius;
107 	newpos.x += pos.x;
108 	newpos.z += pos.z;
109 	newpos.y  = ai->cb->GetElevation(pos.x, pos.z);
110 	return move(newpos, enqueue);
111 }
112 
move(const float3 & pos,bool enqueue)113 bool CUnit::move(const float3 &pos, bool enqueue) {
114 	Command c = createPosCommand(CMD_MOVE, pos);
115 
116 	if (c.id != 0) {
117 		if (enqueue)
118 			c.options |= SHIFT_KEY;
119 		ai->cb->GiveOrder(key, &c);
120 		ai->unittable->idle[key] = false;
121 		return true;
122 	}
123 	return false;
124 }
125 
guard(int target,bool enqueue)126 bool CUnit::guard(int target, bool enqueue) {
127 	Command c = createTargetCommand(CMD_GUARD, target);
128 
129 	if (c.id != 0) {
130 		if (enqueue)
131 			c.options |= SHIFT_KEY;
132 		ai->cb->GiveOrder(key, &c);
133 		ai->unittable->idle[key] = false;
134 		return true;
135 	}
136 	return false;
137 }
138 
patrol(const float3 & pos,bool enqueue)139 bool CUnit::patrol(const float3& pos, bool enqueue) {
140 	Command c = createPosCommand(CMD_PATROL, pos);
141 
142 	if (c.id != 0) {
143 		if (enqueue)
144 			c.options |= SHIFT_KEY;
145 		ai->cb->GiveOrder(key, &c);
146 		ai->unittable->idle[key] = false;
147 		return true;
148 	}
149 	return false;
150 }
151 
cloak(bool on)152 bool CUnit::cloak(bool on) {
153 	Command c = createTargetCommand(CMD_CLOAK, on);
154 
155 	if (c.id != 0) {
156 		ai->cb->GiveOrder(key, &c);
157 		return true;
158 	}
159 	return false;
160 }
161 
repair(int target)162 bool CUnit::repair(int target) {
163 	Command c = createTargetCommand(CMD_REPAIR, target);
164 
165 	if (c.id != 0) {
166 		ai->cb->GiveOrder(key, &c);
167 		ai->unittable->idle[key] = false;
168 		return true;
169 	}
170 	return false;
171 }
172 
build(UnitType * toBuild,const float3 & pos)173 bool CUnit::build(UnitType* toBuild, const float3& pos) {
174 	bool staticBuilder = (type->cats&STATIC).any();
175 	int mindist = 8;
176 
177 	// FIXME: remove hardcoding; we need analyzer of the largest footprint
178 	// per tech level
179 	if ((toBuild->cats&(FACTORY|EMAKER)).any()) {
180 		mindist = 10;
181 		if ((toBuild->cats&(TECH3|TECH4|TECH5|VEHICLE|NAVAL)).any())
182 			mindist = 15;
183 	}
184 	else if((toBuild->cats&MEXTRACTOR).any())
185 		mindist = 0;
186 
187 	float startRadius  = staticBuilder ? def->buildDistance : def->buildDistance*2.0f;
188 	facing f           = getBestFacing(pos);
189 	float3 goal        = ai->cb->ClosestBuildSite(toBuild->def, pos, startRadius, mindist, f);
190 
191 	if (goal.x < 0.0f) {
192 		if (staticBuilder)
193 			return false;
194 
195 		int i = 0;
196 		while (goal.x < 0.0f) {
197 			startRadius += def->buildDistance;
198 			goal = ai->cb->ClosestBuildSite(toBuild->def, pos, startRadius, mindist, f);
199 			i++;
200 			if (i > 10) {
201 				/* Building in this area seems impossible, relocate unit */
202 				//moveRandom(startRadius);
203 				return false;
204 			}
205 		}
206 	}
207 
208 	// NOTE: using of negative unit id is valid for Legacy C++ API only
209 	Command c = createPosCommand(-(toBuild->def->id), goal, -1.0f, f);
210 	if (c.id != 0) {
211 		ai->cb->GiveOrder(key, &c);
212 		ai->unittable->idle[key] = false;
213 		return true;
214 	}
215 
216 	return false;
217 }
218 
stop()219 bool CUnit::stop() {
220 	Command c;
221 	c.id = CMD_STOP;
222 	ai->cb->GiveOrder(key, &c);
223 	return true;
224 }
225 
wait()226 bool CUnit::wait() {
227 	if (!waiting) {
228 		Command c;
229 		c.id = CMD_WAIT;
230 		ai->cb->GiveOrder(key, &c);
231 		waiting = true;
232 	}
233 	return waiting;
234 }
235 
unwait()236 bool CUnit::unwait() {
237 	if (waiting) {
238 		Command c;
239 		c.id = CMD_WAIT;
240 		ai->cb->GiveOrder(key, &c);
241 		waiting = false;
242 	}
243 	return waiting;
244 }
245 
stockpile()246 bool CUnit::stockpile() {
247 	if (!type->def->stockpileWeaponDef)
248 		return false;
249 
250 	Command c;
251 	c.id = CMD_STOCKPILE;
252 	ai->cb->GiveOrder(key, &c);
253 	return true;
254 }
255 
getStockpileReady()256 int CUnit::getStockpileReady() {
257 	int result;
258 	ai->cb->GetProperty(key, AIVAL_STOCKPILED, &result);
259 	return result;
260 }
261 
getStockpileQueued()262 int CUnit::getStockpileQueued() {
263 	int result;
264 	ai->cb->GetProperty(key, AIVAL_STOCKPILE_QUED, &result);
265 	return result;
266 }
267 
micro(bool on)268 bool CUnit::micro(bool on) {
269 	if (on && !microing)
270 		microingFrames = 0;
271 	microing = on;
272 	return microing;
273 }
274 
factoryBuild(UnitType * ut,bool enqueue)275 bool CUnit::factoryBuild(UnitType *ut, bool enqueue) {
276 	Command c;
277 	if (enqueue)
278 		c.options |= SHIFT_KEY;
279 	c.id = -(ut->def->id);
280 	ai->cb->GiveOrder(key, &c);
281 	ai->unittable->idle[key] = false;
282 	return true;
283 }
284 
createTargetCommand(int cmd,int target)285 Command CUnit::createTargetCommand(int cmd, int target) {
286 	Command c;
287 	c.id = cmd;
288 	c.params.push_back(target);
289 	return c;
290 }
291 
createPosCommand(int cmd,float3 pos,float radius,facing f)292 Command CUnit::createPosCommand(int cmd, float3 pos, float radius, facing f) {
293 	if (pos.x > ai->cb->GetMapWidth() * 8)
294 		pos.x = ai->cb->GetMapWidth() * 8;
295 
296 	if (pos.z > ai->cb->GetMapHeight() * 8)
297 		pos.z = ai->cb->GetMapHeight() * 8;
298 
299 	if (pos.x < 0)
300 		pos.x = 0;
301 
302 	if (pos.y < 0)
303 		pos.y = 0;
304 
305 	Command c;
306 	c.id = cmd;
307 	c.params.push_back(pos.x);
308 	c.params.push_back(pos.y);
309 	c.params.push_back(pos.z);
310 
311 	if (f != NONE)
312 		c.params.push_back(f);
313 
314 	if (radius > 0.0f)
315 		c.params.push_back(radius);
316 	return c;
317 }
318 
getQuadrant(const float3 & pos) const319 quadrant CUnit::getQuadrant(const float3& pos) const {
320 	int mapWidth = ai->cb->GetMapWidth() * 8;
321 	int mapHeight = ai->cb->GetMapHeight() * 8;
322 	quadrant mapQuadrant = NORTH_EAST;
323 
324 	if (pos.x < (mapWidth >> 1)) {
325 		/* left half of map */
326 		if (pos.z < (mapHeight >> 1)) {
327 			mapQuadrant = NORTH_WEST;
328 		} else {
329 			mapQuadrant = SOUTH_WEST;
330 		}
331 	}
332 	else {
333 		/* right half of map */
334 		if (pos.z < (mapHeight >> 1)) {
335 			mapQuadrant = NORTH_EAST;
336 		} else {
337 			mapQuadrant = SOUTH_EAST;
338 		}
339 	}
340 	return mapQuadrant;
341 }
342 
getBestFacing(const float3 & pos) const343 facing CUnit::getBestFacing(const float3& pos) const {
344 	int mapWidth = ai->cb->GetMapWidth() * 8;
345 	int mapHeight = ai->cb->GetMapHeight() * 8;
346 	quadrant mapQuadrant = getQuadrant(pos);
347 	facing f = NONE;
348 
349 	switch (mapQuadrant) {
350 		case NORTH_WEST:
351 			f = (mapHeight > mapWidth) ? SOUTH: EAST;
352 			break;
353 		case NORTH_EAST:
354 			f = (mapHeight > mapWidth) ? SOUTH: WEST;
355 			break;
356 		case SOUTH_WEST:
357 			f = (mapHeight > mapWidth) ? NORTH: EAST;
358 			break;
359 		case SOUTH_EAST:
360 			f = (mapHeight > mapWidth) ? NORTH: WEST;
361 			break;
362 	}
363 
364 	return f;
365 }
366 
getRange() const367 float CUnit::getRange() const {
368 	if ((type->cats&BUILDER).any())
369 		return type->def->buildDistance;
370 	else if((type->cats&TRANSPORT).any())
371 		return type->def->loadingRadius;
372 	return ai->cb->GetUnitMaxRange(key);
373 }
374 
hasAntiAirWeapon(const std::vector<UnitDef::UnitDefWeapon> & weapons)375 bool CUnit::hasAntiAirWeapon(const std::vector<UnitDef::UnitDefWeapon>& weapons) {
376 	for (unsigned int i = 0; i < weapons.size(); i++) {
377 		const UnitDef::UnitDefWeapon *weapon = &weapons[i];
378 		if (weapon->def->tracks && !weapon->def->waterweapon
379 		&& !weapon->def->stockpile && !weapon->def->canAttackGround)
380 			return true;
381 	}
382 	return false;
383 }
384 
hasNukeWeapon(const std::vector<UnitDef::UnitDefWeapon> & weapons)385 bool CUnit::hasNukeWeapon(const std::vector<UnitDef::UnitDefWeapon> &weapons) {
386 	for (unsigned int i = 0; i < weapons.size(); i++) {
387 		const UnitDef::UnitDefWeapon *weapon = &weapons[i];
388 		if (weapon->def->stockpile && weapon->def->range > 10000
389 		/*&& weapon->def->fixedLauncher*/ && !weapon->def->paralyzer
390 		&& !weapon->def->interceptor && weapon->def->targetable
391 		&& weapon->def->damages.GetDefaultDamage() > 1000.0f)
392 			return true;
393 	}
394 	return false;
395 }
396 
hasParalyzerWeapon(const std::vector<UnitDef::UnitDefWeapon> & weapons)397 bool CUnit::hasParalyzerWeapon(const std::vector<UnitDef::UnitDefWeapon> &weapons) {
398 	for (unsigned int i = 0; i < weapons.size(); i++) {
399 		const UnitDef::UnitDefWeapon *weapon = &weapons[i];
400 		if (weapon->def->paralyzer)
401 			return true;
402 	}
403 	return false;
404 }
405 
hasInterceptorWeapon(const std::vector<UnitDef::UnitDefWeapon> & weapons)406 bool CUnit::hasInterceptorWeapon(const std::vector<UnitDef::UnitDefWeapon>& weapons) {
407 	for (unsigned int i = 0; i < weapons.size(); i++) {
408 		const UnitDef::UnitDefWeapon *weapon = &weapons[i];
409 		if (weapon->def->stockpile && weapon->def->interceptor
410 		/*&& weapon->def->fixedLauncher*/)
411 			return true;
412 	}
413 	return false;
414 }
415 
hasTorpedoWeapon(const std::vector<UnitDef::UnitDefWeapon> & weapons)416 bool CUnit::hasTorpedoWeapon(const std::vector<UnitDef::UnitDefWeapon>& weapons) {
417 	for (unsigned int i = 0; i < weapons.size(); i++) {
418 		const UnitDef::UnitDefWeapon *weapon = &weapons[i];
419 		if (weapon->def->submissile || weapon->def->waterweapon /*&& weapon->def->tracks*/)
420 			return true;
421 	}
422 	return false;
423 }
424 
hasShield(const std::vector<UnitDef::UnitDefWeapon> & weapons)425 bool CUnit::hasShield(const std::vector<UnitDef::UnitDefWeapon>& weapons) {
426 	for (unsigned int i = 0; i < weapons.size(); i++) {
427 		const UnitDef::UnitDefWeapon* weapon = &weapons[i];
428 		if (weapon->def->isShield)
429 			return true;
430 	}
431 	return false;
432 }
433 
getForwardPos(float distance) const434 float3 CUnit::getForwardPos(float distance) const {
435 	float3 result = pos();
436 	facing f = getBestFacing(result);
437 
438 	switch(f) {
439 		case NORTH:
440 			result.z -= distance;
441 			break;
442 		case SOUTH:
443 			result.z += distance;
444 			break;
445 		case EAST:
446 			result.x += distance;
447 			break;
448 		case WEST:
449 			result.x -= distance;
450 			break;
451 		default: break;
452 	}
453 
454 	result.y = ai->cb->GetElevation(result.x, result.z);
455 
456 	return result;
457 }
458 
operator <<(std::ostream & out,const CUnit & unit)459 std::ostream& operator<<(std::ostream &out, const CUnit &unit) {
460 	if (unit.def == NULL)
461 		out << "Unknown" << "(" << unit.key << ", " << unit.builtBy << ")";
462 	else
463 		out << unit.def->humanName << "(" << unit.key << ", " << unit.builtBy << ")";
464 	return out;
465 }
466