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 ®) {
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