1 /*
2 * Copyright 2010-2014 OpenXcom Developers.
3 *
4 * This file is part of OpenXcom.
5 *
6 * OpenXcom is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * OpenXcom is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with OpenXcom. If not, see <http://www.gnu.org/licenses/>.
18 */
19 #define _USE_MATH_DEFINES
20 #include "BattleUnit.h"
21 #include "BattleItem.h"
22 #include <cmath>
23 #include <sstream>
24 #include <typeinfo>
25 #include "../Engine/Palette.h"
26 #include "../Engine/Surface.h"
27 #include "../Engine/Language.h"
28 #include "../Engine/Logger.h"
29 #include "../Engine/Options.h"
30 #include "../Battlescape/Pathfinding.h"
31 #include "../Battlescape/BattlescapeGame.h"
32 #include "../Battlescape/BattleAIState.h"
33 #include "Soldier.h"
34 #include "../Ruleset/Armor.h"
35 #include "../Ruleset/Unit.h"
36 #include "../Engine/RNG.h"
37 #include "../Ruleset/RuleInventory.h"
38 #include "../Ruleset/RuleSoldier.h"
39 #include "Tile.h"
40 #include "SavedGame.h"
41
42 namespace OpenXcom
43 {
44
45 /**
46 * Initializes a BattleUnit from a Soldier
47 * @param soldier Pointer to the Soldier.
48 * @param faction Which faction the units belongs to.
49 */
BattleUnit(Soldier * soldier,UnitFaction faction)50 BattleUnit::BattleUnit(Soldier *soldier, UnitFaction faction) : _faction(faction), _originalFaction(faction), _killedBy(faction), _id(0), _pos(Position()), _tile(0),
51 _lastPos(Position()), _direction(0), _toDirection(0), _directionTurret(0), _toDirectionTurret(0),
52 _verticalDirection(0), _status(STATUS_STANDING), _walkPhase(0), _fallPhase(0), _kneeled(false), _floating(false),
53 _dontReselect(false), _fire(0), _currentAIState(0), _visible(false), _cacheInvalid(true),
54 _expBravery(0), _expReactions(0), _expFiring(0), _expThrowing(0), _expPsiSkill(0), _expMelee(0),
55 _motionPoints(0), _kills(0), _hitByFire(false), _moraleRestored(0), _coverReserve(0), _charging(0),
56 _turnsSinceSpotted(255), _geoscapeSoldier(soldier), _unitRules(0), _rankInt(-1), _turretType(-1), _hidingForTurn(false)
57 {
58 _name = soldier->getName(true);
59 _id = soldier->getId();
60 _type = "SOLDIER";
61 _rank = soldier->getRankString();
62 _stats = *soldier->getCurrentStats();
63 _standHeight = soldier->getRules()->getStandHeight();
64 _kneelHeight = soldier->getRules()->getKneelHeight();
65 _floatHeight = soldier->getRules()->getFloatHeight();
66 _deathSound = 0; // this one is hardcoded
67 _aggroSound = -1;
68 _moveSound = -1; // this one is hardcoded
69 _intelligence = 2;
70 _aggression = 1;
71 _specab = SPECAB_NONE;
72 _armor = soldier->getArmor();
73 _stats += *_armor->getStats(); // armors may modify effective stats
74 _loftempsSet = _armor->getLoftempsSet();
75 _gender = soldier->getGender();
76 _faceDirection = -1;
77
78 int rankbonus = 0;
79
80 switch (soldier->getRank())
81 {
82 case RANK_SERGEANT: rankbonus = 1; break;
83 case RANK_CAPTAIN: rankbonus = 3; break;
84 case RANK_COLONEL: rankbonus = 6; break;
85 case RANK_COMMANDER: rankbonus = 10; break;
86 default: rankbonus = 0; break;
87 }
88
89 _value = 20 + soldier->getMissions() + rankbonus;
90
91 _tu = _stats.tu;
92 _energy = _stats.stamina;
93 _health = _stats.health;
94 _morale = 100;
95 _stunlevel = 0;
96 _currentArmor[SIDE_FRONT] = _armor->getFrontArmor();
97 _currentArmor[SIDE_LEFT] = _armor->getSideArmor();
98 _currentArmor[SIDE_RIGHT] = _armor->getSideArmor();
99 _currentArmor[SIDE_REAR] = _armor->getRearArmor();
100 _currentArmor[SIDE_UNDER] = _armor->getUnderArmor();
101 for (int i = 0; i < 6; ++i)
102 _fatalWounds[i] = 0;
103 for (int i = 0; i < 5; ++i)
104 _cache[i] = 0;
105
106 _activeHand = "STR_RIGHT_HAND";
107
108 lastCover = Position(-1, -1, -1);
109 }
110
111 /**
112 * Initializes a BattleUnit from a Unit (non-player) object.
113 * @param unit Pointer to Unit object.
114 * @param faction Which faction the units belongs to.
115 * @param id Unique unit ID.
116 * @param armor Pointer to unit Armor.
117 * @param diff difficulty level (for stat adjustement).
118 */
BattleUnit(Unit * unit,UnitFaction faction,int id,Armor * armor,int diff)119 BattleUnit::BattleUnit(Unit *unit, UnitFaction faction, int id, Armor *armor, int diff) : _faction(faction), _originalFaction(faction), _killedBy(faction), _id(id), _pos(Position()),
120 _tile(0), _lastPos(Position()), _direction(0), _toDirection(0), _directionTurret(0),
121 _toDirectionTurret(0), _verticalDirection(0), _status(STATUS_STANDING), _walkPhase(0),
122 _fallPhase(0), _kneeled(false), _floating(false), _dontReselect(false), _fire(0), _currentAIState(0),
123 _visible(false), _cacheInvalid(true), _expBravery(0), _expReactions(0), _expFiring(0),
124 _expThrowing(0), _expPsiSkill(0), _expMelee(0), _motionPoints(0), _kills(0), _hitByFire(false),
125 _moraleRestored(0), _coverReserve(0), _charging(0), _turnsSinceSpotted(255),
126 _armor(armor), _geoscapeSoldier(0), _unitRules(unit), _rankInt(-1),
127 _turretType(-1), _hidingForTurn(false)
128 {
129 _type = unit->getType();
130 _rank = unit->getRank();
131 _race = unit->getRace();
132 _stats = *unit->getStats();
133 _standHeight = unit->getStandHeight();
134 _kneelHeight = unit->getKneelHeight();
135 _floatHeight = unit->getFloatHeight();
136 _loftempsSet = _armor->getLoftempsSet();
137 _deathSound = unit->getDeathSound();
138 _aggroSound = unit->getAggroSound();
139 _moveSound = unit->getMoveSound();
140 _intelligence = unit->getIntelligence();
141 _aggression = unit->getAggression();
142 _specab = (SpecialAbility) unit->getSpecialAbility();
143 _spawnUnit = unit->getSpawnUnit();
144 _value = unit->getValue();
145 _gender = GENDER_MALE;
146 _faceDirection = -1;
147 _stats += *_armor->getStats(); // armors may modify effective stats
148 if (faction == FACTION_HOSTILE)
149 {
150 adjustStats(diff);
151 }
152
153 _tu = _stats.tu;
154 _energy = _stats.stamina;
155 _health = _stats.health;
156 _morale = 100;
157 _stunlevel = 0;
158 _currentArmor[SIDE_FRONT] = _armor->getFrontArmor();
159 _currentArmor[SIDE_LEFT] = _armor->getSideArmor();
160 _currentArmor[SIDE_RIGHT] = _armor->getSideArmor();
161 _currentArmor[SIDE_REAR] = _armor->getRearArmor();
162 _currentArmor[SIDE_UNDER] = _armor->getUnderArmor();
163 for (int i = 0; i < 6; ++i)
164 _fatalWounds[i] = 0;
165 for (int i = 0; i < 5; ++i)
166 _cache[i] = 0;
167
168 _activeHand = "STR_RIGHT_HAND";
169
170 lastCover = Position(-1, -1, -1);
171
172 }
173
174
175 /**
176 *
177 */
~BattleUnit()178 BattleUnit::~BattleUnit()
179 {
180 for (int i = 0; i < 5; ++i)
181 if (_cache[i]) delete _cache[i];
182 delete _currentAIState;
183 }
184
185 /**
186 * Loads the unit from a YAML file.
187 * @param node YAML node.
188 */
load(const YAML::Node & node)189 void BattleUnit::load(const YAML::Node &node)
190 {
191 _id = node["id"].as<int>(_id);
192 _faction = _originalFaction = (UnitFaction)node["faction"].as<int>(_faction);
193 _status = (UnitStatus)node["status"].as<int>(_status);
194 _pos = node["position"].as<Position>(_pos);
195 _direction = _toDirection = node["direction"].as<int>(_direction);
196 _directionTurret = _toDirectionTurret = node["directionTurret"].as<int>(_directionTurret);
197 _tu = node["tu"].as<int>(_tu);
198 _health = node["health"].as<int>(_health);
199 _stunlevel = node["stunlevel"].as<int>(_stunlevel);
200 _energy = node["energy"].as<int>(_energy);
201 _morale = node["morale"].as<int>(_morale);
202 _kneeled = node["kneeled"].as<bool>(_kneeled);
203 _floating = node["floating"].as<bool>(_floating);
204 for (int i=0; i < 5; i++)
205 _currentArmor[i] = node["armor"][i].as<int>(_currentArmor[i]);
206 for (int i=0; i < 6; i++)
207 _fatalWounds[i] = node["fatalWounds"][i].as<int>(_fatalWounds[i]);
208 _fire = node["fire"].as<int>(_fire);
209 _expBravery = node["expBravery"].as<int>(_expBravery);
210 _expReactions = node["expReactions"].as<int>(_expReactions);
211 _expFiring = node["expFiring"].as<int>(_expFiring);
212 _expThrowing = node["expThrowing"].as<int>(_expThrowing);
213 _expPsiSkill = node["expPsiSkill"].as<int>(_expPsiSkill);
214 _expMelee = node["expMelee"].as<int>(_expMelee);
215 _turretType = node["turretType"].as<int>(_turretType);
216 _visible = node["visible"].as<bool>(_visible);
217 _turnsSinceSpotted = node["turnsSinceSpotted"].as<int>(_turnsSinceSpotted);
218 _killedBy = (UnitFaction)node["killedBy"].as<int>(_killedBy);
219 _moraleRestored = node["moraleRestored"].as<int>(_moraleRestored);
220 _rankInt = node["rankInt"].as<int>(_rankInt);
221 _originalFaction = (UnitFaction)node["originalFaction"].as<int>(_originalFaction);
222 _kills = node["kills"].as<int>(_kills);
223 _dontReselect = node["dontReselect"].as<bool>(_dontReselect);
224 _charging = 0;
225 _specab = (SpecialAbility)node["specab"].as<int>(_specab);
226 _spawnUnit = node["spawnUnit"].as<std::string>(_spawnUnit);
227 _motionPoints = node["motionPoints"].as<int>(0);
228
229 }
230
231 /**
232 * Saves the soldier to a YAML file.
233 * @return YAML node.
234 */
save() const235 YAML::Node BattleUnit::save() const
236 {
237 YAML::Node node;
238
239 node["id"] = _id;
240 node["faction"] = (int)_faction;
241 node["soldierId"] = _id;
242 node["genUnitType"] = _type;
243 node["genUnitArmor"] = _armor->getType();
244 node["name"] = Language::wstrToUtf8(getName(0));
245 node["status"] = (int)_status;
246 node["position"] = _pos;
247 node["direction"] = _direction;
248 node["directionTurret"] = _directionTurret;
249 node["tu"] = _tu;
250 node["health"] = _health;
251 node["stunlevel"] = _stunlevel;
252 node["energy"] = _energy;
253 node["morale"] = _morale;
254 node["kneeled"] = _kneeled;
255 node["floating"] = _floating;
256 for (int i=0; i < 5; i++) node["armor"].push_back(_currentArmor[i]);
257 for (int i=0; i < 6; i++) node["fatalWounds"].push_back(_fatalWounds[i]);
258 node["fire"] = _fire;
259 node["expBravery"] = _expBravery;
260 node["expReactions"] = _expReactions;
261 node["expFiring"] = _expFiring;
262 node["expThrowing"] = _expThrowing;
263 node["expPsiSkill"] = _expPsiSkill;
264 node["expMelee"] = _expMelee;
265 node["turretType"] = _turretType;
266 node["visible"] = _visible;
267 node["turnsSinceSpotted"] = _turnsSinceSpotted;
268 node["rankInt"] = _rankInt;
269 node["moraleRestored"] = _moraleRestored;
270 if (getCurrentAIState())
271 {
272 node["AI"] = getCurrentAIState()->save();
273 }
274 node["killedBy"] = (int)_killedBy;
275 if (_originalFaction != _faction)
276 node["originalFaction"] = (int)_originalFaction;
277 if (_kills)
278 node["kills"] = _kills;
279 if (_faction == FACTION_PLAYER && _dontReselect)
280 node["dontReselect"] = _dontReselect;
281 node["specab"] = (int)_specab;
282 if (!_spawnUnit.empty())
283 node["spawnUnit"] = _spawnUnit;
284 node["motionPoints"] = _motionPoints;
285
286 return node;
287 }
288
289 /**
290 * Returns the BattleUnit's unique ID.
291 * @return Unique ID.
292 */
getId() const293 int BattleUnit::getId() const
294 {
295 return _id;
296 }
297
298 /**
299 * Changes the BattleUnit's position.
300 * @param pos position
301 * @param updateLastPos refresh last stored position
302 */
setPosition(const Position & pos,bool updateLastPos)303 void BattleUnit::setPosition(const Position& pos, bool updateLastPos)
304 {
305 if (updateLastPos) { _lastPos = _pos; }
306 _pos = pos;
307 }
308
309 /**
310 * Gets the BattleUnit's position.
311 * @return position
312 */
getPosition() const313 const Position& BattleUnit::getPosition() const
314 {
315 return _pos;
316 }
317
318 /**
319 * Gets the BattleUnit's position.
320 * @return position
321 */
getLastPosition() const322 const Position& BattleUnit::getLastPosition() const
323 {
324 return _lastPos;
325 }
326
327 /**
328 * Gets the BattleUnit's destination.
329 * @return destination
330 */
getDestination() const331 const Position& BattleUnit::getDestination() const
332 {
333 return _destination;
334 }
335
336 /**
337 * Changes the BattleUnit's (horizontal) direction.
338 * Only used for initial unit placement.
339 * @param direction new horizontal direction
340 */
setDirection(int direction)341 void BattleUnit::setDirection(int direction)
342 {
343 _direction = direction;
344 _toDirection = direction;
345 _directionTurret = direction;
346 }
347
348 /**
349 * Changes the BattleUnit's (horizontal) face direction.
350 * Only used for strafing moves.
351 * @param direction new face direction
352 */
setFaceDirection(int direction)353 void BattleUnit::setFaceDirection(int direction)
354 {
355 _faceDirection = direction;
356 }
357
358 /**
359 * Gets the BattleUnit's (horizontal) direction.
360 * @return horizontal direction
361 */
getDirection() const362 int BattleUnit::getDirection() const
363 {
364 return _direction;
365 }
366
367 /**
368 * Gets the BattleUnit's (horizontal) face direction.
369 * Used only during strafing moves.
370 * @return face direction
371 */
getFaceDirection() const372 int BattleUnit::getFaceDirection() const
373 {
374 return _faceDirection;
375 }
376
377 /**
378 * Gets the BattleUnit's turret direction.
379 * @return direction
380 */
getTurretDirection() const381 int BattleUnit::getTurretDirection() const
382 {
383 return _directionTurret;
384 }
385
386 /**
387 * Gets the BattleUnit's turret To direction.
388 * @return toDirectionTurret
389 */
getTurretToDirection() const390 int BattleUnit::getTurretToDirection() const
391 {
392 return _toDirectionTurret;
393 }
394
395 /**
396 * Gets the BattleUnit's vertical direction. This is when going up or down.
397 * @return direction
398 */
getVerticalDirection() const399 int BattleUnit::getVerticalDirection() const
400 {
401 return _verticalDirection;
402 }
403
404 /**
405 * Gets the unit's status.
406 * @return the unit's status
407 */
getStatus() const408 UnitStatus BattleUnit::getStatus() const
409 {
410 return _status;
411 }
412
413 /**
414 * Initialises variables to start walking.
415 * @param direction Which way to walk.
416 * @param destination The position we should end up on.
417 * @param tileBelowMe Which tile is currently below the unit.
418 * @param cache Update cache?
419 */
startWalking(int direction,const Position & destination,Tile * tileBelowMe,bool cache)420 void BattleUnit::startWalking(int direction, const Position &destination, Tile *tileBelowMe, bool cache)
421 {
422 if (direction >= Pathfinding::DIR_UP)
423 {
424 _verticalDirection = direction;
425 _status = STATUS_FLYING;
426 }
427 else
428 {
429 _direction = direction;
430 _status = STATUS_WALKING;
431 }
432 bool floorFound = false;
433 if (!_tile->hasNoFloor(tileBelowMe))
434 {
435 floorFound = true;
436 }
437 if (!floorFound || direction >= Pathfinding::DIR_UP)
438 {
439 _status = STATUS_FLYING;
440 _floating = true;
441 }
442 else
443 {
444 _floating = false;
445 }
446
447 _walkPhase = 0;
448 _destination = destination;
449 _lastPos = _pos;
450 _cacheInvalid = cache;
451 _kneeled = false;
452 }
453
454 /**
455 * This will increment the walking phase.
456 * @param tileBelowMe Pointer to tile currently below the unit.
457 * @param cache Refresh the unit cache.
458 */
keepWalking(Tile * tileBelowMe,bool cache)459 void BattleUnit::keepWalking(Tile *tileBelowMe, bool cache)
460 {
461 int middle, end;
462 if (_verticalDirection)
463 {
464 middle = 4;
465 end = 8;
466 }
467 else
468 {
469 // diagonal walking takes double the steps
470 middle = 4 + 4 * (_direction % 2);
471 end = 8 + 8 * (_direction % 2);
472 if (_armor->getSize() > 1)
473 {
474 if (_direction < 1 || _direction > 5)
475 middle = end;
476 else if (_direction == 5)
477 middle = 12;
478 else if (_direction == 1)
479 middle = 5;
480 else
481 middle = 1;
482 }
483 }
484 if (!cache)
485 {
486 _pos = _destination;
487 end = 2;
488 }
489
490 _walkPhase++;
491
492
493 if (_walkPhase == middle)
494 {
495 // we assume we reached our destination tile
496 // this is actually a drawing hack, so soldiers are not overlapped by floortiles
497 _pos = _destination;
498 }
499
500 if (_walkPhase >= end)
501 {
502 if (_floating && !_tile->hasNoFloor(tileBelowMe))
503 {
504 _floating = false;
505 }
506 // we officially reached our destination tile
507 _status = STATUS_STANDING;
508 _walkPhase = 0;
509 _verticalDirection = 0;
510 if (_faceDirection >= 0) {
511 // Finish strafing move facing the correct way.
512 _direction = _faceDirection;
513 _faceDirection = -1;
514 }
515
516 // motion points calculation for the motion scanner blips
517 if (_armor->getSize() > 1)
518 {
519 _motionPoints += 30;
520 }
521 else
522 {
523 // sectoids actually have less motion points
524 // but instead of create yet another variable,
525 // I used the height of the unit instead (logical)
526 if (getStandHeight() > 16)
527 _motionPoints += 4;
528 else
529 _motionPoints += 3;
530 }
531 }
532
533 _cacheInvalid = cache;
534 }
535
536 /**
537 * Gets the walking phase for animation and sound.
538 * @return phase will always go from 0-7
539 */
getWalkingPhase() const540 int BattleUnit::getWalkingPhase() const
541 {
542 return _walkPhase % 8;
543 }
544
545 /**
546 * Gets the walking phase for diagonal walking.
547 * @return phase this will be 0 or 8
548 */
getDiagonalWalkingPhase() const549 int BattleUnit::getDiagonalWalkingPhase() const
550 {
551 return (_walkPhase / 8) * 8;
552 }
553
554 /**
555 * Look at a point.
556 * @param point Position to look at.
557 * @param turret True to turn the turret, false to turn the unit.
558 */
lookAt(const Position & point,bool turret)559 void BattleUnit::lookAt(const Position &point, bool turret)
560 {
561 int dir = directionTo (point);
562
563 if (turret)
564 {
565 _toDirectionTurret = dir;
566 if (_toDirectionTurret != _directionTurret)
567 {
568 _status = STATUS_TURNING;
569 }
570 }
571 else
572 {
573 _toDirection = dir;
574 if (_toDirection != _direction
575 && _toDirection < 8
576 && _toDirection > -1)
577 {
578 _status = STATUS_TURNING;
579 }
580 }
581 }
582
583 /**
584 * Look at a direction.
585 * @param direction Direction to look at.
586 * @param force True to reset the direction, false to animate to it.
587 */
lookAt(int direction,bool force)588 void BattleUnit::lookAt(int direction, bool force)
589 {
590 if (!force)
591 {
592 if (direction < 0 || direction >= 8) return;
593 _toDirection = direction;
594 if (_toDirection != _direction)
595 {
596 _status = STATUS_TURNING;
597 }
598 }
599 else
600 {
601 _toDirection = direction;
602 _direction = direction;
603 }
604 }
605
606 /**
607 * Advances the turning towards the target direction.
608 * @param turret True to turn the turret, false to turn the unit.
609 */
turn(bool turret)610 void BattleUnit::turn(bool turret)
611 {
612 int a = 0;
613
614 if (turret)
615 {
616 if (_directionTurret == _toDirectionTurret)
617 {
618 abortTurn();
619 return;
620 }
621 a = _toDirectionTurret - _directionTurret;
622 }
623 else
624 {
625 if (_direction == _toDirection)
626 {
627 abortTurn();
628 return;
629 }
630 a = _toDirection - _direction;
631 }
632
633 if (a != 0) {
634 if (a > 0) {
635 if (a <= 4) {
636 if (!turret) {
637 if (_turretType > -1)
638 _directionTurret++;
639 _direction++;
640 } else _directionTurret++;
641 } else {
642 if (!turret) {
643 if (_turretType > -1)
644 _directionTurret--;
645 _direction--;
646 } else _directionTurret--;
647 }
648 } else {
649 if (a > -4) {
650 if (!turret) {
651 if (_turretType > -1)
652 _directionTurret--;
653 _direction--;
654 } else _directionTurret--;
655 } else {
656 if (!turret) {
657 if (_turretType > -1)
658 _directionTurret++;
659 _direction++;
660 } else _directionTurret++;
661 }
662 }
663 if (_direction < 0) _direction = 7;
664 if (_direction > 7) _direction = 0;
665 if (_directionTurret < 0) _directionTurret = 7;
666 if (_directionTurret > 7) _directionTurret = 0;
667 if (_visible || _faction == FACTION_PLAYER)
668 _cacheInvalid = true;
669 }
670
671 if (turret)
672 {
673 if (_toDirectionTurret == _directionTurret)
674 {
675 // we officially reached our destination
676 _status = STATUS_STANDING;
677 }
678 }
679 else if (_toDirection == _direction || _status == STATUS_UNCONSCIOUS)
680 {
681 // we officially reached our destination
682 _status = STATUS_STANDING;
683 }
684 }
685
686 /**
687 * Stops the turning towards the target direction.
688 */
abortTurn()689 void BattleUnit::abortTurn()
690 {
691 _status = STATUS_STANDING;
692 }
693
694
695 /**
696 * Gets the soldier's gender.
697 */
getGender() const698 SoldierGender BattleUnit::getGender() const
699 {
700 return _gender;
701 }
702
703 /**
704 * Returns the unit's faction.
705 * @return Faction. (player, hostile or neutral)
706 */
getFaction() const707 UnitFaction BattleUnit::getFaction() const
708 {
709 return _faction;
710 }
711
712 /**
713 * Sets the unit's cache flag.
714 * @param cache Pointer to cache surface to use, NULL to redraw from scratch.
715 * @param part Unit part to cache.
716 */
setCache(Surface * cache,int part)717 void BattleUnit::setCache(Surface *cache, int part)
718 {
719 if (cache == 0)
720 {
721 _cacheInvalid = true;
722 }
723 else
724 {
725 _cache[part] = cache;
726 _cacheInvalid = false;
727 }
728 }
729
730 /**
731 * Check if the unit is still cached in the Map cache.
732 * When the unit changes it needs to be re-cached.
733 * @param invalid Get if the cache is invalid.
734 * @param part Unit part to check.
735 * @return Pointer to cache surface used.
736 */
getCache(bool * invalid,int part) const737 Surface *BattleUnit::getCache(bool *invalid, int part) const
738 {
739 if (part < 0) part = 0;
740 *invalid = _cacheInvalid;
741 return _cache[part];
742 }
743
744 /**
745 * Kneel down.
746 * @param kneeled to kneel or to stand up
747 */
kneel(bool kneeled)748 void BattleUnit::kneel(bool kneeled)
749 {
750 _kneeled = kneeled;
751 _cacheInvalid = true;
752 }
753
754 /**
755 * Is kneeled down?
756 * @return true/false
757 */
isKneeled() const758 bool BattleUnit::isKneeled() const
759 {
760 return _kneeled;
761 }
762
763 /**
764 * Is floating? A unit is floating when there is no ground under him/her.
765 * @return true/false
766 */
isFloating() const767 bool BattleUnit::isFloating() const
768 {
769 return _floating;
770 }
771
772 /**
773 * Aim. (shows the right hand sprite and weapon holding)
774 * @param aiming true/false
775 */
aim(bool aiming)776 void BattleUnit::aim(bool aiming)
777 {
778 if (aiming)
779 _status = STATUS_AIMING;
780 else
781 _status = STATUS_STANDING;
782
783 if (_visible || _faction == FACTION_PLAYER)
784 _cacheInvalid = true;
785 }
786
787 /**
788 * Returns the direction from this unit to a given point.
789 * @param point given position.
790 * @return direction.
791 */
directionTo(const Position & point) const792 int BattleUnit::directionTo(const Position &point) const
793 {
794 double ox = point.x - _pos.x;
795 double oy = point.y - _pos.y;
796 double angle = atan2(ox, -oy);
797 // divide the pie in 4 angles each at 1/8th before each quarter
798 double pie[4] = {(M_PI_4 * 4.0) - M_PI_4 / 2.0, (M_PI_4 * 3.0) - M_PI_4 / 2.0, (M_PI_4 * 2.0) - M_PI_4 / 2.0, (M_PI_4 * 1.0) - M_PI_4 / 2.0};
799 int dir = 0;
800
801 if (angle > pie[0] || angle < -pie[0])
802 {
803 dir = 4;
804 }
805 else if (angle > pie[1])
806 {
807 dir = 3;
808 }
809 else if (angle > pie[2])
810 {
811 dir = 2;
812 }
813 else if (angle > pie[3])
814 {
815 dir = 1;
816 }
817 else if (angle < -pie[1])
818 {
819 dir = 5;
820 }
821 else if (angle < -pie[2])
822 {
823 dir = 6;
824 }
825 else if (angle < -pie[3])
826 {
827 dir = 7;
828 }
829 else if (angle < pie[0])
830 {
831 dir = 0;
832 }
833 return dir;
834 }
835
836 /**
837 * Returns the soldier's amount of time units.
838 * @return Time units.
839 */
getTimeUnits() const840 int BattleUnit::getTimeUnits() const
841 {
842 return _tu;
843 }
844
845 /**
846 * Returns the soldier's amount of energy.
847 * @return Energy.
848 */
getEnergy() const849 int BattleUnit::getEnergy() const
850 {
851 return _energy;
852 }
853
854 /**
855 * Returns the soldier's amount of health.
856 * @return Health.
857 */
getHealth() const858 int BattleUnit::getHealth() const
859 {
860 return _health;
861 }
862
863 /**
864 * Returns the soldier's amount of morale.
865 * @return Morale.
866 */
getMorale() const867 int BattleUnit::getMorale() const
868 {
869 return _morale;
870 }
871
872 /**
873 * Do an amount of damage.
874 * @param relative The relative position of which part of armor and/or bodypart is hit.
875 * @param power The amount of damage to inflict.
876 * @param type The type of damage being inflicted.
877 * @param ignoreArmor Should the damage ignore armor resistance?
878 * @return damage done after adjustment
879 */
damage(const Position & relative,int power,ItemDamageType type,bool ignoreArmor)880 int BattleUnit::damage(const Position &relative, int power, ItemDamageType type, bool ignoreArmor)
881 {
882 UnitSide side = SIDE_FRONT;
883 UnitBodyPart bodypart = BODYPART_TORSO;
884
885 if (power <= 0)
886 {
887 return 0;
888 }
889
890 power = (int)floor(power * _armor->getDamageModifier(type));
891
892 if (type == DT_SMOKE) type = DT_STUN; // smoke doesn't do real damage, but stun damage
893
894 if (!ignoreArmor)
895 {
896 if (relative == Position(0, 0, 0))
897 {
898 side = SIDE_UNDER;
899 }
900 else
901 {
902 int relativeDirection;
903 const int abs_x = abs(relative.x);
904 const int abs_y = abs(relative.y);
905 if (abs_y > abs_x * 2)
906 relativeDirection = 8 + 4 * (relative.y > 0);
907 else if (abs_x > abs_y * 2)
908 relativeDirection = 10 + 4 * (relative.x < 0);
909 else
910 {
911 if (relative.x < 0)
912 {
913 if (relative.y > 0)
914 relativeDirection = 13;
915 else
916 relativeDirection = 15;
917 }
918 else
919 {
920 if (relative.y > 0)
921 relativeDirection = 11;
922 else
923 relativeDirection = 9;
924 }
925 }
926
927 switch((relativeDirection - _direction) % 8)
928 {
929 case 0: side = SIDE_FRONT; break;
930 case 1: side = RNG::generate(0,2) < 2 ? SIDE_FRONT:SIDE_RIGHT; break;
931 case 2: side = SIDE_RIGHT; break;
932 case 3: side = RNG::generate(0,2) < 2 ? SIDE_REAR:SIDE_RIGHT; break;
933 case 4: side = SIDE_REAR; break;
934 case 5: side = RNG::generate(0,2) < 2 ? SIDE_REAR:SIDE_LEFT; break;
935 case 6: side = SIDE_LEFT; break;
936 case 7: side = RNG::generate(0,2) < 2 ? SIDE_FRONT:SIDE_LEFT; break;
937 }
938 if (relative.z > getHeight())
939 {
940 bodypart = BODYPART_HEAD;
941 }
942 else if (relative.z > 4)
943 {
944 switch(side)
945 {
946 case SIDE_LEFT: bodypart = BODYPART_LEFTARM; break;
947 case SIDE_RIGHT: bodypart = BODYPART_RIGHTARM; break;
948 default: bodypart = BODYPART_TORSO;
949 }
950 }
951 else
952 {
953 switch(side)
954 {
955 case SIDE_LEFT: bodypart = BODYPART_LEFTLEG; break;
956 case SIDE_RIGHT: bodypart = BODYPART_RIGHTLEG; break;
957 default:
958 bodypart = (UnitBodyPart) RNG::generate(BODYPART_RIGHTLEG,BODYPART_LEFTLEG);
959 }
960 }
961 }
962 power -= getArmor(side);
963 }
964
965 if (power > 0)
966 {
967 if (type == DT_STUN)
968 {
969 _stunlevel += power;
970 }
971 else
972 {
973 // health damage
974 _health -= power;
975 if (_health < 0)
976 {
977 _health = 0;
978 }
979
980 if (type != DT_IN)
981 {
982 if (_armor->getSize() == 1)
983 {
984 // conventional weapons can cause additional stun damage
985 _stunlevel += RNG::generate(0, power / 4);
986 }
987 // fatal wounds
988 if (isWoundable())
989 {
990 if (RNG::generate(0, 10) < power)
991 _fatalWounds[bodypart] += RNG::generate(1,3);
992
993 if (_fatalWounds[bodypart])
994 moraleChange(-_fatalWounds[bodypart]);
995 }
996 // armor damage
997 setArmor(getArmor(side) - (power/10) - 1, side);
998 }
999 }
1000 }
1001
1002 return power < 0 ? 0:power;
1003 }
1004
1005 /**
1006 * Do an amount of stun recovery.
1007 * @param power
1008 */
healStun(int power)1009 void BattleUnit::healStun(int power)
1010 {
1011 _stunlevel -= power;
1012 if (_stunlevel < 0) _stunlevel = 0;
1013 }
1014
getStunlevel() const1015 int BattleUnit::getStunlevel() const
1016 {
1017 return _stunlevel;
1018 }
1019
1020 /**
1021 * Raises a unit's stun level sufficiently so that the unit is ready to become unconscious.
1022 * Used when another unit falls on top of this unit.
1023 * Zombified units first convert to their spawn unit.
1024 * @param battle Pointer to the battlescape game.
1025 */
knockOut(BattlescapeGame * battle)1026 void BattleUnit::knockOut(BattlescapeGame *battle)
1027 {
1028 if (getArmor()->getSize() > 1) // large units die
1029 {
1030 _health = 0;
1031 }
1032 else if (_spawnUnit != "")
1033 {
1034 setSpecialAbility(SPECAB_NONE);
1035 BattleUnit *newUnit = battle->convertUnit(this, _spawnUnit);
1036 newUnit->knockOut(battle);
1037 }
1038 else
1039 {
1040 _stunlevel = _health;
1041 }
1042 }
1043
1044 /**
1045 * Intialises the falling sequence. Occurs after death or stunned.
1046 */
startFalling()1047 void BattleUnit::startFalling()
1048 {
1049 _status = STATUS_COLLAPSING;
1050 _fallPhase = 0;
1051 _cacheInvalid = true;
1052 }
1053
1054 /**
1055 * Advances the phase of falling sequence.
1056 */
keepFalling()1057 void BattleUnit::keepFalling()
1058 {
1059 _fallPhase++;
1060 int endFrame = 3;
1061 if (_spawnUnit != "" && _specab != SPECAB_RESPAWN)
1062 {
1063 endFrame = 18;
1064 }
1065 if (_fallPhase == endFrame)
1066 {
1067 _fallPhase--;
1068 if (_health == 0)
1069 _status = STATUS_DEAD;
1070 else
1071 _status = STATUS_UNCONSCIOUS;
1072 }
1073
1074 _cacheInvalid = true;
1075 }
1076
1077
1078 /**
1079 * Returns the phase of the falling sequence.
1080 * @return phase
1081 */
getFallingPhase() const1082 int BattleUnit::getFallingPhase() const
1083 {
1084 return _fallPhase;
1085 }
1086
1087 /**
1088 * Returns whether the soldier is out of combat, dead or unconscious.
1089 * A soldier that is out, cannot perform any actions, cannot be selected, but it's still a unit.
1090 * @return flag if out or not.
1091 */
isOut() const1092 bool BattleUnit::isOut() const
1093 {
1094 return _status == STATUS_DEAD || _status == STATUS_UNCONSCIOUS;
1095 }
1096
1097 /**
1098 * Get the number of time units a certain action takes.
1099 * @param actionType
1100 * @param item
1101 * @return TUs
1102 */
getActionTUs(BattleActionType actionType,BattleItem * item)1103 int BattleUnit::getActionTUs(BattleActionType actionType, BattleItem *item)
1104 {
1105 if (item == 0)
1106 {
1107 return 0;
1108 }
1109
1110 int cost = 0;
1111 switch (actionType)
1112 {
1113 case BA_PRIME:
1114 cost = 50; // maybe this should go in the ruleset
1115 break;
1116 case BA_THROW:
1117 cost = 25;
1118 break;
1119 case BA_AUTOSHOT:
1120 cost = item->getRules()->getTUAuto();
1121 break;
1122 case BA_SNAPSHOT:
1123 cost = item->getRules()->getTUSnap();
1124 break;
1125 case BA_STUN:
1126 case BA_HIT:
1127 cost = item->getRules()->getTUMelee();
1128 break;
1129 case BA_LAUNCH:
1130 case BA_AIMEDSHOT:
1131 cost = item->getRules()->getTUAimed();
1132 break;
1133 case BA_USE:
1134 case BA_MINDCONTROL:
1135 case BA_PANIC:
1136 cost = item->getRules()->getTUUse();
1137 break;
1138 default:
1139 cost = 0;
1140 }
1141
1142 // if it's a percentage, apply it to unit TUs
1143 if (!item->getRules()->getFlatRate() || actionType == BA_THROW || actionType == BA_PRIME)
1144 {
1145 cost = (int)floor(getStats()->tu * cost / 100.0f);
1146 }
1147
1148 return cost;
1149 }
1150
1151
1152 /**
1153 * Spend time units if it can. Return false if it can't.
1154 * @param tu
1155 * @return flag if it could spend the time units or not.
1156 */
spendTimeUnits(int tu)1157 bool BattleUnit::spendTimeUnits(int tu)
1158 {
1159 if (tu <= _tu)
1160 {
1161 _tu -= tu;
1162 return true;
1163 }
1164 else
1165 {
1166 return false;
1167 }
1168 }
1169
1170 /**
1171 * Spend energy if it can. Return false if it can't.
1172 * @param tu
1173 * @return flag if it could spend the time units or not.
1174 */
spendEnergy(int tu)1175 bool BattleUnit::spendEnergy(int tu)
1176 {
1177 int eu = tu / 2;
1178
1179 if (eu <= _energy)
1180 {
1181 _energy -= eu;
1182 return true;
1183 }
1184 else
1185 {
1186 return false;
1187 }
1188 }
1189
1190 /**
1191 * Set a specific number of timeunits.
1192 * @param tu
1193 */
setTimeUnits(int tu)1194 void BattleUnit::setTimeUnits(int tu)
1195 {
1196 _tu = tu;
1197 }
1198
1199 /**
1200 * Add this unit to the list of visible units. Returns true if this is a new one.
1201 * @param unit
1202 * @return
1203 */
addToVisibleUnits(BattleUnit * unit)1204 bool BattleUnit::addToVisibleUnits(BattleUnit *unit)
1205 {
1206 bool add = true;
1207 for (std::vector<BattleUnit*>::iterator i = _unitsSpottedThisTurn.begin(); i != _unitsSpottedThisTurn.end();++i)
1208 {
1209 if ((BattleUnit*)(*i) == unit)
1210 {
1211 add = false;
1212 break;
1213 }
1214 }
1215 if (add)
1216 {
1217 _unitsSpottedThisTurn.push_back(unit);
1218 }
1219 for (std::vector<BattleUnit*>::iterator i = _visibleUnits.begin(); i != _visibleUnits.end(); ++i)
1220 {
1221 if ((BattleUnit*)(*i) == unit)
1222 {
1223 return false;
1224 }
1225 }
1226 _visibleUnits.push_back(unit);
1227 return true;
1228 }
1229
1230 /**
1231 * Get the pointer to the vector of visible units.
1232 * @return pointer to vector.
1233 */
getVisibleUnits()1234 std::vector<BattleUnit*> *BattleUnit::getVisibleUnits()
1235 {
1236 return &_visibleUnits;
1237 }
1238
1239 /**
1240 * Clear visible units.
1241 */
clearVisibleUnits()1242 void BattleUnit::clearVisibleUnits()
1243 {
1244 _visibleUnits.clear();
1245 }
1246
1247 /**
1248 * Add this unit to the list of visible tiles. Returns true if this is a new one.
1249 * @param tile
1250 * @return
1251 */
addToVisibleTiles(Tile * tile)1252 bool BattleUnit::addToVisibleTiles(Tile *tile)
1253 {
1254 _visibleTiles.push_back(tile);
1255 return true;
1256 }
1257
1258 /**
1259 * Get the pointer to the vector of visible tiles.
1260 * @return pointer to vector.
1261 */
getVisibleTiles()1262 std::vector<Tile*> *BattleUnit::getVisibleTiles()
1263 {
1264 return &_visibleTiles;
1265 }
1266
1267 /**
1268 * Clear visible tiles.
1269 */
clearVisibleTiles()1270 void BattleUnit::clearVisibleTiles()
1271 {
1272 for (std::vector<Tile*>::iterator j = _visibleTiles.begin(); j != _visibleTiles.end(); ++j)
1273 {
1274 (*j)->setVisible(-1);
1275 }
1276
1277 _visibleTiles.clear();
1278 }
1279
1280 /**
1281 * Calculate firing accuracy.
1282 * Formula = accuracyStat * weaponAccuracy * kneelingbonus(1.15) * one-handPenalty(0.8) * woundsPenalty(% health) * critWoundsPenalty (-10%/wound)
1283 * @param actionType
1284 * @param item
1285 * @return firing Accuracy
1286 */
getFiringAccuracy(BattleActionType actionType,BattleItem * item)1287 int BattleUnit::getFiringAccuracy(BattleActionType actionType, BattleItem *item)
1288 {
1289
1290 int weaponAcc = item->getRules()->getAccuracySnap();
1291 if (actionType == BA_AIMEDSHOT || actionType == BA_LAUNCH)
1292 weaponAcc = item->getRules()->getAccuracyAimed();
1293 else if (actionType == BA_AUTOSHOT)
1294 weaponAcc = item->getRules()->getAccuracyAuto();
1295 else if (actionType == BA_HIT || actionType == BA_STUN)
1296 {
1297 if (item->getRules()->isSkillApplied())
1298 {
1299 return (getStats()->melee * item->getRules()->getAccuracyMelee() / 100) * getAccuracyModifier(item) / 100;
1300 }
1301 return item->getRules()->getAccuracyMelee() * getAccuracyModifier(item) / 100;
1302 }
1303
1304 int result = getStats()->firing * weaponAcc / 100;
1305
1306 if (_kneeled)
1307 {
1308 result = result * 115 / 100;
1309 }
1310
1311 if (item->getRules()->isTwoHanded())
1312 {
1313 // two handed weapon, means one hand should be empty
1314 if (getItem("STR_RIGHT_HAND") != 0 && getItem("STR_LEFT_HAND") != 0)
1315 {
1316 result = result * 80 / 100;
1317 }
1318 }
1319
1320 return result * getAccuracyModifier(item) / 100;
1321 }
1322
1323 /**
1324 * To calculate firing accuracy. Takes health and fatal wounds into account.
1325 * Formula = accuracyStat * woundsPenalty(% health) * critWoundsPenalty (-10%/wound)
1326 * @param item the item we are shooting right now.
1327 * @return modifier
1328 */
getAccuracyModifier(BattleItem * item)1329 int BattleUnit::getAccuracyModifier(BattleItem *item)
1330 {
1331 int wounds = _fatalWounds[BODYPART_HEAD];
1332
1333 if (item)
1334 {
1335 if (item->getRules()->isTwoHanded())
1336 {
1337 wounds += _fatalWounds[BODYPART_RIGHTARM] + _fatalWounds[BODYPART_LEFTARM];
1338 }
1339 else
1340 {
1341 if (getItem("STR_RIGHT_HAND") == item)
1342 {
1343 wounds += _fatalWounds[BODYPART_RIGHTARM];
1344 }
1345 else
1346 {
1347 wounds += _fatalWounds[BODYPART_LEFTARM];
1348 }
1349 }
1350 }
1351 return std::max(10, 25 * _health / getStats()->health + 75 + -10 * wounds);
1352 }
1353
1354 /**
1355 * Calculate throwing accuracy.
1356 * @return throwing Accuracy
1357 */
getThrowingAccuracy()1358 double BattleUnit::getThrowingAccuracy()
1359 {
1360 return (double)(getStats()->throwing * getAccuracyModifier()) / 100.0;
1361 }
1362
1363 /**
1364 * Set the armor value of a certain armor side.
1365 * @param armor Amount of armor.
1366 * @param side The side of the armor.
1367 */
setArmor(int armor,UnitSide side)1368 void BattleUnit::setArmor(int armor, UnitSide side)
1369 {
1370 if (armor < 0)
1371 {
1372 armor = 0;
1373 }
1374 _currentArmor[side] = armor;
1375 }
1376
1377 /**
1378 * Get the armor value of a certain armor side.
1379 * @param side The side of the armor.
1380 * @return Amount of armor.
1381 */
getArmor(UnitSide side) const1382 int BattleUnit::getArmor(UnitSide side) const
1383 {
1384 return _currentArmor[side];
1385 }
1386
1387 /**
1388 * Get total amount of fatal wounds this unit has.
1389 * @return Number of fatal wounds.
1390 */
getFatalWounds() const1391 int BattleUnit::getFatalWounds() const
1392 {
1393 int sum = 0;
1394 for (int i = 0; i < 6; ++i)
1395 sum += _fatalWounds[i];
1396 return sum;
1397 }
1398
1399
1400 /**
1401 * Little formula to calculate reaction score.
1402 * @return Reaction score.
1403 */
getReactionScore()1404 double BattleUnit::getReactionScore()
1405 {
1406 //(Reactions Stat) x (Current Time Units / Max TUs)
1407 double score = ((double)getStats()->reactions * (double)getTimeUnits()) / (double)getStats()->tu;
1408 return score;
1409 }
1410
1411
1412 /**
1413 * Prepare for a new turn.
1414 */
prepareNewTurn()1415 void BattleUnit::prepareNewTurn()
1416 {
1417 // revert to original faction
1418 _faction = _originalFaction;
1419
1420 _unitsSpottedThisTurn.clear();
1421
1422 // recover TUs
1423 int TURecovery = getStats()->tu;
1424 float encumbrance = (float)getStats()->strength / (float)getCarriedWeight();
1425 if (encumbrance < 1)
1426 {
1427 TURecovery = int(encumbrance * TURecovery);
1428 }
1429 // Each fatal wound to the left or right leg reduces the soldier's TUs by 10%.
1430 TURecovery -= (TURecovery * (_fatalWounds[BODYPART_LEFTLEG]+_fatalWounds[BODYPART_RIGHTLEG] * 10))/100;
1431 setTimeUnits(TURecovery);
1432
1433 // recover energy
1434 if (!isOut())
1435 {
1436 int ENRecovery;
1437 if (_geoscapeSoldier != 0)
1438 {
1439 ENRecovery = _geoscapeSoldier->getInitStats()->tu / 3;
1440 }
1441 else
1442 {
1443 ENRecovery = _unitRules->getEnergyRecovery();
1444 }
1445 // Each fatal wound to the body reduces the soldier's energy recovery by 10%.
1446 ENRecovery -= (_energy * (_fatalWounds[BODYPART_TORSO] * 10))/100;
1447 _energy += ENRecovery;
1448 if (_energy > getStats()->stamina)
1449 _energy = getStats()->stamina;
1450 }
1451
1452 // suffer from fatal wounds
1453 _health -= getFatalWounds();
1454
1455 // suffer from fire
1456 if (!_hitByFire && _fire > 0)
1457 {
1458 _health -= _armor->getDamageModifier(DT_IN) * RNG::generate(5, 10);
1459 _fire--;
1460 }
1461
1462 if (_health < 0)
1463 _health = 0;
1464
1465 // if unit is dead, AI state should be gone
1466 if (_health == 0 && _currentAIState)
1467 {
1468 _currentAIState->exit();
1469 delete _currentAIState;
1470 _currentAIState = 0;
1471 }
1472
1473 // recover stun 1pt/turn
1474 if (_stunlevel > 0)
1475 healStun(1);
1476
1477 if (!isOut())
1478 {
1479 int chance = 100 - (2 * getMorale());
1480 if (RNG::generate(1,100) <= chance)
1481 {
1482 int type = RNG::generate(0,100);
1483 _status = (type<=33?STATUS_BERSERK:STATUS_PANICKING); // 33% chance of berserk, panic can mean freeze or flee, but that is determined later
1484 }
1485 else
1486 {
1487 // successfully avoided panic
1488 // increase bravery experience counter
1489 if (chance > 1)
1490 _expBravery++;
1491 }
1492 }
1493 _hitByFire = false;
1494 _dontReselect = false;
1495 _motionPoints = 0;
1496 }
1497
1498
1499 /**
1500 * Morale change with bounds check.
1501 * @param change can be positive or negative
1502 */
moraleChange(int change)1503 void BattleUnit::moraleChange(int change)
1504 {
1505 if (!isFearable()) return;
1506
1507 _morale += change;
1508 if (_morale > 100)
1509 _morale = 100;
1510 if (_morale < 0)
1511 _morale = 0;
1512 }
1513
1514 /**
1515 * Mark this unit as not reselectable.
1516 */
dontReselect()1517 void BattleUnit::dontReselect()
1518 {
1519 _dontReselect = true;
1520 }
1521
1522 /**
1523 * Mark this unit as reselectable.
1524 */
allowReselect()1525 void BattleUnit::allowReselect()
1526 {
1527 _dontReselect = false;
1528 }
1529
1530
1531 /**
1532 * Check whether reselecting this unit is allowed.
1533 * @return bool
1534 */
reselectAllowed() const1535 bool BattleUnit::reselectAllowed() const
1536 {
1537 return !_dontReselect;
1538 }
1539
1540 /**
1541 * Set the amount of turns this unit is on fire. 0 = no fire.
1542 * @param fire : amount of turns this tile is on fire.
1543 */
setFire(int fire)1544 void BattleUnit::setFire(int fire)
1545 {
1546 if (_specab != SPECAB_BURNFLOOR)
1547 _fire = fire;
1548 }
1549
1550 /**
1551 * Get the amount of turns this unit is on fire. 0 = no fire.
1552 * @return fire : amount of turns this tile is on fire.
1553 */
getFire() const1554 int BattleUnit::getFire() const
1555 {
1556 return _fire;
1557 }
1558
1559 /**
1560 * Get the pointer to the vector of inventory items.
1561 * @return pointer to vector.
1562 */
getInventory()1563 std::vector<BattleItem*> *BattleUnit::getInventory()
1564 {
1565 return &_inventory;
1566 }
1567
1568 /**
1569 * Let AI do their thing.
1570 * @param action AI action.
1571 */
think(BattleAction * action)1572 void BattleUnit::think(BattleAction *action)
1573 {
1574 checkAmmo();
1575 _currentAIState->think(action);
1576 }
1577
1578 /**
1579 * Changes the current AI state.
1580 * @param aiState Pointer to AI state.
1581 */
setAIState(BattleAIState * aiState)1582 void BattleUnit::setAIState(BattleAIState *aiState)
1583 {
1584 if (_currentAIState)
1585 {
1586 _currentAIState->exit();
1587 delete _currentAIState;
1588 }
1589 _currentAIState = aiState;
1590 _currentAIState->enter();
1591 }
1592
1593 /**
1594 * Returns the current AI state.
1595 * @return Pointer to AI state.
1596 */
getCurrentAIState() const1597 BattleAIState *BattleUnit::getCurrentAIState() const
1598 {
1599 return _currentAIState;
1600 }
1601
1602 /**
1603 * Set whether this unit is visible.
1604 * @param flag
1605 */
setVisible(bool flag)1606 void BattleUnit::setVisible(bool flag)
1607 {
1608 _visible = flag;
1609 }
1610
1611
1612 /**
1613 * Get whether this unit is visible.
1614 * @return flag
1615 */
getVisible() const1616 bool BattleUnit::getVisible() const
1617 {
1618 if (getFaction() == FACTION_PLAYER)
1619 {
1620 return true;
1621 }
1622 else
1623 {
1624 return _visible;
1625 }
1626 }
1627
1628 /**
1629 * Sets the unit's tile it's standing on
1630 * @param tile Pointer to tile.
1631 * @param tileBelow Pointer to tile below.
1632 */
setTile(Tile * tile,Tile * tileBelow)1633 void BattleUnit::setTile(Tile *tile, Tile *tileBelow)
1634 {
1635 _tile = tile;
1636 if (!_tile)
1637 {
1638 _floating = false;
1639 return;
1640 }
1641 // unit could have changed from flying to walking or vice versa
1642 if (_status == STATUS_WALKING && _tile->hasNoFloor(tileBelow) && _armor->getMovementType() == MT_FLY)
1643 {
1644 _status = STATUS_FLYING;
1645 _floating = true;
1646 }
1647 else if (_status == STATUS_FLYING && !_tile->hasNoFloor(tileBelow) && _verticalDirection == 0)
1648 {
1649 _status = STATUS_WALKING;
1650 _floating = false;
1651 }
1652 else if (_status == STATUS_UNCONSCIOUS)
1653 {
1654 _floating = _armor->getMovementType() == MT_FLY && _tile->hasNoFloor(tileBelow);
1655 }
1656 }
1657
1658 /**
1659 * Gets the unit's tile.
1660 * @return Tile
1661 */
getTile() const1662 Tile *BattleUnit::getTile() const
1663 {
1664 return _tile;
1665 }
1666
1667 /**
1668 * Checks if there's an inventory item in
1669 * the specified inventory position.
1670 * @param slot Inventory slot.
1671 * @param x X position in slot.
1672 * @param y Y position in slot.
1673 * @return Item in the slot, or NULL if none.
1674 */
getItem(RuleInventory * slot,int x,int y) const1675 BattleItem *BattleUnit::getItem(RuleInventory *slot, int x, int y) const
1676 {
1677 // Soldier items
1678 if (slot->getType() != INV_GROUND)
1679 {
1680 for (std::vector<BattleItem*>::const_iterator i = _inventory.begin(); i != _inventory.end(); ++i)
1681 {
1682 if ((*i)->getSlot() == slot && (*i)->occupiesSlot(x, y))
1683 {
1684 return *i;
1685 }
1686 }
1687 }
1688 // Ground items
1689 else if (_tile != 0)
1690 {
1691 for (std::vector<BattleItem*>::const_iterator i = _tile->getInventory()->begin(); i != _tile->getInventory()->end(); ++i)
1692 {
1693 if ((*i)->occupiesSlot(x, y))
1694 {
1695 return *i;
1696 }
1697 }
1698 }
1699 return 0;
1700 }
1701
1702 /**
1703 * Checks if there's an inventory item in
1704 * the specified inventory position.
1705 * @param slot Inventory slot.
1706 * @param x X position in slot.
1707 * @param y Y position in slot.
1708 * @return Item in the slot, or NULL if none.
1709 */
getItem(const std::string & slot,int x,int y) const1710 BattleItem *BattleUnit::getItem(const std::string &slot, int x, int y) const
1711 {
1712 // Soldier items
1713 if (slot != "STR_GROUND")
1714 {
1715 for (std::vector<BattleItem*>::const_iterator i = _inventory.begin(); i != _inventory.end(); ++i)
1716 {
1717 if ((*i)->getSlot() != 0 && (*i)->getSlot()->getId() == slot && (*i)->occupiesSlot(x, y))
1718 {
1719 return *i;
1720 }
1721 }
1722 }
1723 // Ground items
1724 else if (_tile != 0)
1725 {
1726 for (std::vector<BattleItem*>::const_iterator i = _tile->getInventory()->begin(); i != _tile->getInventory()->end(); ++i)
1727 {
1728 if ((*i)->getSlot() != 0 && (*i)->occupiesSlot(x, y))
1729 {
1730 return *i;
1731 }
1732 }
1733 }
1734 return 0;
1735 }
1736
1737 /**
1738 * Get the "main hand weapon" from the unit.
1739 * @param quickest Whether to get the quickest weapon, default true
1740 * @return Pointer to item.
1741 */
getMainHandWeapon(bool quickest) const1742 BattleItem *BattleUnit::getMainHandWeapon(bool quickest) const
1743 {
1744 BattleItem *weaponRightHand = getItem("STR_RIGHT_HAND");
1745 BattleItem *weaponLeftHand = getItem("STR_LEFT_HAND");
1746
1747 // ignore weapons without ammo (rules out grenades)
1748 if (!weaponRightHand || !weaponRightHand->getAmmoItem() || !weaponRightHand->getAmmoItem()->getAmmoQuantity())
1749 weaponRightHand = 0;
1750 if (!weaponLeftHand || !weaponLeftHand->getAmmoItem() || !weaponLeftHand->getAmmoItem()->getAmmoQuantity())
1751 weaponLeftHand = 0;
1752
1753 // if there is only one weapon, it's easy:
1754 if (weaponRightHand && !weaponLeftHand)
1755 return weaponRightHand;
1756 else if (!weaponRightHand && weaponLeftHand)
1757 return weaponLeftHand;
1758 else if (!weaponRightHand && !weaponLeftHand)
1759 return 0;
1760
1761 // otherwise pick the one with the least snapshot TUs
1762 int tuRightHand = weaponRightHand->getRules()->getTUSnap();
1763 int tuLeftHand = weaponLeftHand->getRules()->getTUSnap();
1764 if (tuLeftHand >= tuRightHand)
1765 {
1766 return quickest?weaponRightHand:weaponLeftHand;
1767 }
1768 else
1769 {
1770 return quickest?weaponLeftHand:weaponRightHand;
1771 }
1772 }
1773
1774 /**
1775 * Get a grenade from the belt (used for AI)
1776 * @return Pointer to item.
1777 */
getGrenadeFromBelt() const1778 BattleItem *BattleUnit::getGrenadeFromBelt() const
1779 {
1780 for (std::vector<BattleItem*>::const_iterator i = _inventory.begin(); i != _inventory.end(); ++i)
1781 {
1782 if ((*i)->getRules()->getBattleType() == BT_GRENADE)
1783 {
1784 return *i;
1785 }
1786 }
1787 return 0;
1788 }
1789
1790 /**
1791 * Check if we have ammo and reload if needed (used for AI).
1792 * @return Do we have ammo?
1793 */
checkAmmo()1794 bool BattleUnit::checkAmmo()
1795 {
1796 BattleItem *weapon = getItem("STR_RIGHT_HAND");
1797 if (!weapon || weapon->getAmmoItem() != 0 || weapon->getRules()->getBattleType() == BT_MELEE || getTimeUnits() < 15)
1798 {
1799 weapon = getItem("STR_LEFT_HAND");
1800 if (!weapon || weapon->getAmmoItem() != 0 || weapon->getRules()->getBattleType() == BT_MELEE || getTimeUnits() < 15)
1801 {
1802 return false;
1803 }
1804 }
1805 // we have a non-melee weapon with no ammo and 15 or more TUs - we might need to look for ammo then
1806 BattleItem *ammo = 0;
1807 bool wrong = true;
1808 for (std::vector<BattleItem*>::iterator i = getInventory()->begin(); i != getInventory()->end(); ++i)
1809 {
1810 ammo = (*i);
1811 for (std::vector<std::string>::iterator c = weapon->getRules()->getCompatibleAmmo()->begin(); c != weapon->getRules()->getCompatibleAmmo()->end(); ++c)
1812 {
1813 if ((*c) == ammo->getRules()->getType())
1814 {
1815 wrong = false;
1816 break;
1817 }
1818 }
1819 if (!wrong) break;
1820 }
1821
1822 if (wrong) return false; // didn't find any compatible ammo in inventory
1823
1824 spendTimeUnits(15);
1825 weapon->setAmmoItem(ammo);
1826 ammo->moveToOwner(0);
1827
1828 return true;
1829 }
1830
1831 /**
1832 * Check if this unit is in the exit area.
1833 * @param stt Type of exit tile to check for.
1834 * @return Is in the exit area?
1835 */
isInExitArea(SpecialTileType stt) const1836 bool BattleUnit::isInExitArea(SpecialTileType stt) const
1837 {
1838 return _tile && _tile->getMapData(MapData::O_FLOOR) && (_tile->getMapData(MapData::O_FLOOR)->getSpecialType() == stt);
1839 }
1840
1841 /**
1842 * Gets the unit height taking into account kneeling/standing.
1843 * @return Unit's height.
1844 */
getHeight() const1845 int BattleUnit::getHeight() const
1846 {
1847 return isKneeled()?getKneelHeight():getStandHeight();
1848 }
1849
1850 /**
1851 * Adds one to the reaction exp counter.
1852 */
addReactionExp()1853 void BattleUnit::addReactionExp()
1854 {
1855 _expReactions++;
1856 }
1857
1858 /**
1859 * Adds one to the firing exp counter.
1860 */
addFiringExp()1861 void BattleUnit::addFiringExp()
1862 {
1863 _expFiring++;
1864 }
1865
1866 /**
1867 * Adds one to the firing exp counter.
1868 */
addThrowingExp()1869 void BattleUnit::addThrowingExp()
1870 {
1871 _expThrowing++;
1872 }
1873
1874 /**
1875 * Adds one to the firing exp counter.
1876 */
addPsiExp()1877 void BattleUnit::addPsiExp()
1878 {
1879 _expPsiSkill++;
1880 }
1881
1882 /**
1883 * Adds one to the firing exp counter.
1884 */
addMeleeExp()1885 void BattleUnit::addMeleeExp()
1886 {
1887 _expMelee++;
1888 }
1889
updateGeoscapeStats(Soldier * soldier)1890 void BattleUnit::updateGeoscapeStats(Soldier *soldier)
1891 {
1892 soldier->addMissionCount();
1893 soldier->addKillCount(_kills);
1894 }
1895
1896 /**
1897 * Check if unit eligible for squaddie promotion. If yes, promote the unit.
1898 * Increase the mission counter. Calculate the experience increases.
1899 * @param geoscape Pointer to geoscape save.
1900 * @return True if the soldier was eligible for squaddie promotion.
1901 */
postMissionProcedures(SavedGame * geoscape)1902 bool BattleUnit::postMissionProcedures(SavedGame *geoscape)
1903 {
1904 Soldier *s = geoscape->getSoldier(_id);
1905 if (s == 0)
1906 {
1907 return false;
1908 }
1909
1910 updateGeoscapeStats(s);
1911
1912 UnitStats *stats = s->getCurrentStats();
1913 const UnitStats caps = s->getRules()->getStatCaps();
1914 int healthLoss = stats->health - _health;
1915
1916 s->setWoundRecovery(RNG::generate((healthLoss*0.5),(healthLoss*1.5)));
1917
1918 if (_expBravery && stats->bravery < caps.bravery)
1919 {
1920 if (_expBravery > RNG::generate(0,10)) stats->bravery += 10;
1921 }
1922 if (_expReactions && stats->reactions < caps.reactions)
1923 {
1924 stats->reactions += improveStat(_expReactions);
1925 }
1926 if (_expFiring && stats->firing < caps.firing)
1927 {
1928 stats->firing += improveStat(_expFiring);
1929 }
1930 if (_expMelee && stats->melee < caps.melee)
1931 {
1932 stats->melee += improveStat(_expMelee);
1933 }
1934 if (_expThrowing && stats->throwing < caps.throwing)
1935 {
1936 stats->throwing += improveStat(_expThrowing);
1937 }
1938 if (_expPsiSkill && stats->psiSkill < caps.psiSkill)
1939 {
1940 stats->psiSkill += improveStat(_expPsiSkill);
1941 }
1942
1943 if (_expBravery || _expReactions || _expFiring || _expPsiSkill || _expMelee)
1944 {
1945 if (s->getRank() == RANK_ROOKIE)
1946 s->promoteRank();
1947 int v;
1948 v = caps.tu - stats->tu;
1949 if (v > 0) stats->tu += RNG::generate(0, v/10 + 2);
1950 v = caps.health - stats->health;
1951 if (v > 0) stats->health += RNG::generate(0, v/10 + 2);
1952 v = caps.strength - stats->strength;
1953 if (v > 0) stats->strength += RNG::generate(0, v/10 + 2);
1954 v = caps.stamina - stats->stamina;
1955 if (v > 0) stats->stamina += RNG::generate(0, v/10 + 2);
1956 return true;
1957 }
1958 else
1959 {
1960 return false;
1961 }
1962 }
1963
1964 /**
1965 * Converts the number of experience to the stat increase.
1966 * @param Experience counter.
1967 * @return Stat increase.
1968 */
improveStat(int exp)1969 int BattleUnit::improveStat(int exp)
1970 {
1971 double tier = 4.0;
1972 if (exp <= 10)
1973 {
1974 tier = 3.0;
1975 if (exp <= 5)
1976 {
1977 tier = exp > 2 ? 2.0 : 1.0;
1978 }
1979 }
1980 return (int)(tier/2.0 + RNG::generate(0.0, tier));
1981 }
1982
1983 /**
1984 * Get the unit's minimap sprite index. Used to display the unit on the minimap
1985 * @return the unit minimap index
1986 */
getMiniMapSpriteIndex() const1987 int BattleUnit::getMiniMapSpriteIndex () const
1988 {
1989 //minimap sprite index:
1990 // * 0-2 : Xcom soldier
1991 // * 3-5 : Alien
1992 // * 6-8 : Civilian
1993 // * 9-11 : Item
1994 // * 12-23 : Xcom HWP
1995 // * 24-35 : Alien big terror unit(cyberdisk, ...)
1996 if (isOut())
1997 {
1998 return 9;
1999 }
2000 switch (getFaction())
2001 {
2002 case FACTION_HOSTILE:
2003 if (_armor->getSize() == 1)
2004 return 3;
2005 else
2006 return 24;
2007 case FACTION_NEUTRAL:
2008 return 6;
2009 default:
2010 if (_armor->getSize() == 1)
2011 return 0;
2012 else
2013 return 12;
2014 }
2015 }
2016
2017 /**
2018 * Set the turret type. -1 is no turret.
2019 * @param turretType
2020 */
setTurretType(int turretType)2021 void BattleUnit::setTurretType(int turretType)
2022 {
2023 _turretType = turretType;
2024 }
2025
2026 /**
2027 * Get the turret type. -1 is no turret.
2028 * @return type
2029 */
getTurretType() const2030 int BattleUnit::getTurretType() const
2031 {
2032 return _turretType;
2033 }
2034
2035 /**
2036 * Get the amount of fatal wound for a body part
2037 * @param part The body part (in the range 0-5)
2038 * @return The amount of fatal wound of a body part
2039 */
getFatalWound(int part) const2040 int BattleUnit::getFatalWound(int part) const
2041 {
2042 if (part < 0 || part > 5)
2043 return 0;
2044 return _fatalWounds[part];
2045 }
2046
2047 /**
2048 * Heal a fatal wound of the soldier
2049 * @param part the body part to heal
2050 * @param woundAmount the amount of fatal wound healed
2051 * @param healthAmount The amount of health to add to soldier health
2052 */
heal(int part,int woundAmount,int healthAmount)2053 void BattleUnit::heal(int part, int woundAmount, int healthAmount)
2054 {
2055 if (part < 0 || part > 5)
2056 return;
2057 if(!_fatalWounds[part])
2058 return;
2059 _fatalWounds[part] -= woundAmount;
2060 _health += healthAmount;
2061 if (_health > getStats()->health)
2062 _health = getStats()->health;
2063 }
2064
2065 /**
2066 * Restore soldier morale
2067 */
painKillers()2068 void BattleUnit::painKillers ()
2069 {
2070 int lostHealth = getStats()->health - _health;
2071 if (lostHealth > _moraleRestored)
2072 {
2073 _morale = std::min(100, (lostHealth - _moraleRestored + _morale));
2074 _moraleRestored = lostHealth;
2075 }
2076 }
2077
2078 /**
2079 * Restore soldier energy and reduce stun level
2080 * @param energy The amount of energy to add
2081 * @param s The amount of stun level to reduce
2082 */
stimulant(int energy,int s)2083 void BattleUnit::stimulant (int energy, int s)
2084 {
2085 _energy += energy;
2086 if (_energy > getStats()->stamina)
2087 _energy = getStats()->stamina;
2088 healStun (s);
2089 }
2090
2091
2092 /**
2093 * Get motion points for the motion scanner. More points
2094 * is a larger blip on the scanner.
2095 * @return points.
2096 */
getMotionPoints() const2097 int BattleUnit::getMotionPoints() const
2098 {
2099 return _motionPoints;
2100 }
2101
2102
2103 /**
2104 * Gets the unit's armor.
2105 * @return Pointer to armor.
2106 */
getArmor() const2107 Armor *BattleUnit::getArmor() const
2108 {
2109 return _armor;
2110 }
2111 /**
2112 * Get unit's name.
2113 * An aliens name is the translation of it's race and rank.
2114 * hence the language pointer needed.
2115 * @param lang Pointer to language.
2116 * @param debugAppendId Append unit ID to name for debug purposes.
2117 * @return name Widecharstring of the unit's name.
2118 */
getName(Language * lang,bool debugAppendId) const2119 std::wstring BattleUnit::getName(Language *lang, bool debugAppendId) const
2120 {
2121 if (_type != "SOLDIER" && lang != 0)
2122 {
2123 std::wstring ret;
2124
2125 if (_type.find("STR_") != std::string::npos)
2126 ret = lang->getString(_type);
2127 else
2128 ret = lang->getString(_race);
2129
2130 if (debugAppendId)
2131 {
2132 std::wostringstream ss;
2133 ss << ret << L" " << _id;
2134 ret = ss.str();
2135 }
2136 return ret;
2137 }
2138
2139 return _name;
2140 }
2141 /**
2142 * Gets pointer to the unit's stats.
2143 * @return stats Pointer to the unit's stats.
2144 */
getStats()2145 UnitStats *BattleUnit::getStats()
2146 {
2147 return &_stats;
2148 }
2149
2150 /**
2151 * Get the unit's stand height.
2152 * @return The unit's height in voxels, when standing up.
2153 */
getStandHeight() const2154 int BattleUnit::getStandHeight() const
2155 {
2156 return _standHeight;
2157 }
2158
2159 /**
2160 * Get the unit's kneel height.
2161 * @return The unit's height in voxels, when kneeling.
2162 */
getKneelHeight() const2163 int BattleUnit::getKneelHeight() const
2164 {
2165 return _kneelHeight;
2166 }
2167
2168 /**
2169 * Get the unit's floating elevation.
2170 * @return The unit's elevation over the ground in voxels, when flying.
2171 */
getFloatHeight() const2172 int BattleUnit::getFloatHeight() const
2173 {
2174 return _floatHeight;
2175 }
2176
2177 /**
2178 * Get the unit's loft ID, one per unit tile.
2179 * Each tile only has one loft, as it is repeated over the entire height of the unit.
2180 * @param entry Unit tile
2181 * @return The unit's line of fire template ID.
2182 */
getLoftemps(int entry) const2183 int BattleUnit::getLoftemps(int entry) const
2184 {
2185 return _loftempsSet.at(entry);
2186 }
2187
2188 /**
2189 * Get the unit's value. Used for score at debriefing.
2190 * @return value score
2191 */
getValue() const2192 int BattleUnit::getValue() const
2193 {
2194 return _value;
2195 }
2196
2197 /**
2198 * Get the unit's death sound.
2199 * @return id.
2200 */
getDeathSound() const2201 int BattleUnit::getDeathSound() const
2202 {
2203 return _deathSound;
2204 }
2205
2206 /**
2207 * Get the unit's move sound.
2208 * @return id.
2209 */
getMoveSound() const2210 int BattleUnit::getMoveSound() const
2211 {
2212 return _moveSound;
2213 }
2214
2215
2216 /**
2217 * Get whether the unit is affected by fatal wounds.
2218 * Normally only soldiers are affected by fatal wounds.
2219 * @return Is the unit affected by wounds?
2220 */
isWoundable() const2221 bool BattleUnit::isWoundable() const
2222 {
2223 return (_type=="SOLDIER" || (Options::alienBleeding && _faction != FACTION_PLAYER && _armor->getSize() == 1));
2224 }
2225 /**
2226 * Get whether the unit is affected by morale loss.
2227 * Normally only small units are affected by morale loss.
2228 * @return Is the unit affected by morale?
2229 */
isFearable() const2230 bool BattleUnit::isFearable() const
2231 {
2232 return (_armor->getSize() == 1);
2233 }
2234
2235 /**
2236 * Get the number of turns an AI unit remembers a soldier's position.
2237 * @return intelligence.
2238 */
getIntelligence() const2239 int BattleUnit::getIntelligence() const
2240 {
2241 return _intelligence;
2242 }
2243
2244 /**
2245 * Get the unit's aggression.
2246 * @return aggression.
2247 */
getAggression() const2248 int BattleUnit::getAggression() const
2249 {
2250 return _aggression;
2251 }
2252
2253 /**
2254 * Returns the unit's special ability.
2255 * @return special ability.
2256 */
getSpecialAbility() const2257 int BattleUnit::getSpecialAbility() const
2258 {
2259 return _specab;
2260 }
2261
2262 /**
2263 * Changes the unit's special ability.
2264 * @param specab special ability.
2265 */
setSpecialAbility(SpecialAbility specab)2266 void BattleUnit::setSpecialAbility(SpecialAbility specab)
2267 {
2268 _specab = specab;
2269 }
2270
2271 /**
2272 * Get the unit that is spawned when this one dies.
2273 * @return unit.
2274 */
getSpawnUnit() const2275 std::string BattleUnit::getSpawnUnit() const
2276 {
2277 return _spawnUnit;
2278 }
2279
2280 /**
2281 * Set the unit that is spawned when this one dies.
2282 * @param spawnUnit unit.
2283 */
setSpawnUnit(std::string spawnUnit)2284 void BattleUnit::setSpawnUnit(std::string spawnUnit)
2285 {
2286 _spawnUnit = spawnUnit;
2287 }
2288
2289 /**
2290 * Get the units's rank string.
2291 * @return rank.
2292 */
getRankString() const2293 std::string BattleUnit::getRankString() const
2294 {
2295 return _rank;
2296 }
2297
2298 /**
2299 * Get the geoscape-soldier object.
2300 * @return soldier.
2301 */
getGeoscapeSoldier() const2302 Soldier *BattleUnit::getGeoscapeSoldier() const
2303 {
2304 return _geoscapeSoldier;
2305 }
2306
2307 /**
2308 * Add a kill to the counter.
2309 */
addKillCount()2310 void BattleUnit::addKillCount()
2311 {
2312 _kills++;
2313 }
2314
2315 /**
2316 * Get unit type.
2317 * @return unit type.
2318 */
getType() const2319 std::string BattleUnit::getType() const
2320 {
2321 return _type;
2322 }
2323
2324 /**
2325 * Set unit's active hand.
2326 * @param hand active hand.
2327 */
setActiveHand(const std::string & hand)2328 void BattleUnit::setActiveHand(const std::string &hand)
2329 {
2330 if (_activeHand != hand) _cacheInvalid = true;
2331 _activeHand = hand;
2332 }
2333 /**
2334 * Get unit's active hand.
2335 * @return active hand.
2336 */
getActiveHand() const2337 std::string BattleUnit::getActiveHand() const
2338 {
2339 if (getItem(_activeHand)) return _activeHand;
2340 if (getItem("STR_LEFT_HAND")) return "STR_LEFT_HAND";
2341 return "STR_RIGHT_HAND";
2342 }
2343
2344 /**
2345 * Converts unit to another faction (original faction is still stored).
2346 * @param f faction.
2347 */
convertToFaction(UnitFaction f)2348 void BattleUnit::convertToFaction(UnitFaction f)
2349 {
2350 _faction = f;
2351 }
2352
2353 /**
2354 * Set health to 0 and set status dead - used when getting zombified.
2355 */
instaKill()2356 void BattleUnit::instaKill()
2357 {
2358 _health = 0;
2359 _status = STATUS_DEAD;
2360 }
2361
2362 /**
2363 * Get sound to play when unit aggros.
2364 * @return sound
2365 */
getAggroSound() const2366 int BattleUnit::getAggroSound() const
2367 {
2368 return _aggroSound;
2369 }
2370
2371 /**
2372 * Set a specific number of energy.
2373 * @param energy energy.
2374 */
setEnergy(int energy)2375 void BattleUnit::setEnergy(int energy)
2376 {
2377 _energy = energy;
2378 }
2379
2380 /**
2381 * Halve this unit's armor values (for beginner mode)
2382 */
halveArmor()2383 void BattleUnit::halveArmor()
2384 {
2385 _currentArmor[0] /= 2;
2386 _currentArmor[1] /= 2;
2387 _currentArmor[2] /= 2;
2388 _currentArmor[3] /= 2;
2389 _currentArmor[4] /= 2;
2390 }
2391
2392 /**
2393 * Get the faction the unit was killed by.
2394 * @return faction
2395 */
killedBy() const2396 UnitFaction BattleUnit::killedBy() const
2397 {
2398 return _killedBy;
2399 }
2400
2401 /**
2402 * Set the faction the unit was killed by.
2403 * @param f faction
2404 */
killedBy(UnitFaction f)2405 void BattleUnit::killedBy(UnitFaction f)
2406 {
2407 _killedBy = f;
2408 }
2409
2410 /**
2411 * Set the units we are charging towards.
2412 * @param chargeTarget Charge Target
2413 */
setCharging(BattleUnit * chargeTarget)2414 void BattleUnit::setCharging(BattleUnit *chargeTarget)
2415 {
2416 _charging = chargeTarget;
2417 }
2418
2419 /**
2420 * Get the units we are charging towards.
2421 * @return Charge Target
2422 */
getCharging()2423 BattleUnit *BattleUnit::getCharging()
2424 {
2425 return _charging;
2426 }
2427
2428 /**
2429 * Get the units carried weight in strength units.
2430 * @param draggingItem item to ignore
2431 * @return weight
2432 */
getCarriedWeight(BattleItem * draggingItem) const2433 int BattleUnit::getCarriedWeight(BattleItem *draggingItem) const
2434 {
2435 int weight = _armor->getWeight();
2436 for (std::vector<BattleItem*>::const_iterator i = _inventory.begin(); i != _inventory.end(); ++i)
2437 {
2438 if ((*i) == draggingItem) continue;
2439 weight += (*i)->getRules()->getWeight();
2440 if ((*i)->getAmmoItem() != (*i) && (*i)->getAmmoItem()) weight += (*i)->getAmmoItem()->getRules()->getWeight();
2441 }
2442 return std::max(0,weight);
2443 }
2444
2445 /**
2446 * Set how long since this unit was last exposed.
2447 * @param turns number of turns
2448 */
setTurnsSinceSpotted(int turns)2449 void BattleUnit::setTurnsSinceSpotted (int turns)
2450 {
2451 _turnsSinceSpotted = turns;
2452 }
2453
2454 /**
2455 * Get how long since this unit was exposed.
2456 * @return number of turns
2457 */
getTurnsSinceSpotted() const2458 int BattleUnit::getTurnsSinceSpotted () const
2459 {
2460 return _turnsSinceSpotted;
2461 }
2462
2463 /**
2464 * Get this unit's original Faction.
2465 * @return original faction
2466 */
getOriginalFaction() const2467 UnitFaction BattleUnit::getOriginalFaction() const
2468 {
2469 return _originalFaction;
2470 }
2471
2472 /**
2473 * invalidate cache; call after copying object :(
2474 */
invalidateCache()2475 void BattleUnit::invalidateCache()
2476 {
2477 for (int i = 0; i < 5; ++i) { _cache[i] = 0; }
2478 _cacheInvalid = true;
2479 }
2480
2481 /**
2482 * Get the list of units spotted this turn.
2483 * @return List of units.
2484 */
getUnitsSpottedThisTurn()2485 std::vector<BattleUnit *> &BattleUnit::getUnitsSpottedThisTurn()
2486 {
2487 return _unitsSpottedThisTurn;
2488 }
2489
2490 /**
2491 * Change the numeric version of the unit's rank.
2492 * @param rank unit rank, 0 = lowest
2493 */
setRankInt(int rank)2494 void BattleUnit::setRankInt(int rank)
2495 {
2496 _rankInt = rank;
2497 }
2498
2499 /**
2500 * Return the numeric version of the unit's rank.
2501 * @return unit rank, 0 = lowest
2502 */
getRankInt() const2503 int BattleUnit::getRankInt() const
2504 {
2505 return _rankInt;
2506 }
2507
2508 /**
2509 * Derive the numeric unit rank from the string rank
2510 * (for soldier units).
2511 */
deriveRank()2512 void BattleUnit::deriveRank()
2513 {
2514 if (_faction == FACTION_PLAYER)
2515 {
2516 if (_rank == "STR_COMMANDER")
2517 _rankInt = 5;
2518 else if (_rank == "STR_COLONEL")
2519 _rankInt = 4;
2520 else if (_rank == "STR_CAPTAIN")
2521 _rankInt = 3;
2522 else if (_rank == "STR_SERGEANT")
2523 _rankInt = 2;
2524 else if (_rank == "STR_SQUADDIE")
2525 _rankInt = 1;
2526 else if (_rank == "STR_ROOKIE")
2527 _rankInt = 0;
2528 }
2529 }
2530
2531 /**
2532 * this function checks if a tile is visible, using maths.
2533 * @param pos the position to check against
2534 * @return what the maths decide
2535 */
checkViewSector(Position pos) const2536 bool BattleUnit::checkViewSector (Position pos) const
2537 {
2538 int deltaX = pos.x - _pos.x;
2539 int deltaY = _pos.y - pos.y;
2540
2541 switch (_direction)
2542 {
2543 case 0:
2544 if ( (deltaX + deltaY >= 0) && (deltaY - deltaX >= 0) )
2545 return true;
2546 break;
2547 case 1:
2548 if ( (deltaX >= 0) && (deltaY >= 0) )
2549 return true;
2550 break;
2551 case 2:
2552 if ( (deltaX + deltaY >= 0) && (deltaY - deltaX <= 0) )
2553 return true;
2554 break;
2555 case 3:
2556 if ( (deltaY <= 0) && (deltaX >= 0) )
2557 return true;
2558 break;
2559 case 4:
2560 if ( (deltaX + deltaY <= 0) && (deltaY - deltaX <= 0) )
2561 return true;
2562 break;
2563 case 5:
2564 if ( (deltaX <= 0) && (deltaY <= 0) )
2565 return true;
2566 break;
2567 case 6:
2568 if ( (deltaX + deltaY <= 0) && (deltaY - deltaX >= 0) )
2569 return true;
2570 break;
2571 case 7:
2572 if ( (deltaY >= 0) && (deltaX <= 0) )
2573 return true;
2574 break;
2575 default:
2576 return false;
2577 }
2578 return false;
2579 }
2580
2581 /**
2582 * common function to adjust a unit's stats according to difficulty setting.
2583 * @param diff difficulty level (for stat adjustement).
2584 */
adjustStats(const int diff)2585 void BattleUnit::adjustStats(const int diff)
2586 {
2587 // adjust the unit's stats according to the difficulty level.
2588 _stats.tu += 4 * diff * _stats.tu / 100;
2589 _stats.stamina += 4 * diff * _stats.stamina / 100;
2590 _stats.reactions += 6 * diff * _stats.reactions / 100;
2591 _stats.firing = (_stats.firing + 6 * diff * _stats.firing / 100) / (diff > 0 ? 1 : 2);
2592 _stats.strength += 2 * diff * _stats.strength / 100;
2593 _stats.melee += 4 * diff * _stats.melee / 100;
2594 _stats.psiSkill += 4 * diff * _stats.psiSkill / 100;
2595 _stats.psiStrength += 4 * diff * _stats.psiStrength / 100;
2596 }
2597
2598 /**
2599 * did this unit already take fire damage this turn?
2600 * (used to avoid damaging large units multiple times.)
2601 * @return ow it burns
2602 */
tookFireDamage() const2603 bool BattleUnit::tookFireDamage() const
2604 {
2605 return _hitByFire;
2606 }
2607
2608 /**
2609 * toggle the state of the fire damage tracking boolean.
2610 */
toggleFireDamage()2611 void BattleUnit::toggleFireDamage()
2612 {
2613 _hitByFire = !_hitByFire;
2614 }
2615
2616 /**
2617 * Changes the amount of TUs reserved for cover.
2618 * @param reserve time units.
2619 */
setCoverReserve(int reserve)2620 void BattleUnit::setCoverReserve(int reserve)
2621 {
2622 _coverReserve = reserve;
2623 }
2624
2625 /**
2626 * Returns the amount of TUs reserved for cover.
2627 * @return time units.
2628 */
getCoverReserve() const2629 int BattleUnit::getCoverReserve() const
2630 {
2631 return _coverReserve;
2632 }
2633
2634 /**
2635 * Checks if this unit can be selected. Only alive units
2636 * belonging to the faction can be selected.
2637 * @param faction The faction to compare with.
2638 * @param checkReselect Check if the unit is reselectable.
2639 * @param checkInventory Check if the unit has an inventory.
2640 * @return True if the unit can be selected, false otherwise.
2641 */
isSelectable(UnitFaction faction,bool checkReselect,bool checkInventory) const2642 bool BattleUnit::isSelectable(UnitFaction faction, bool checkReselect, bool checkInventory) const
2643 {
2644 return (_faction == faction && !isOut() && (!checkReselect || reselectAllowed()) && (!checkInventory || hasInventory()));
2645 }
2646
2647 /**
2648 * Checks if this unit has an inventory. Large units and/or
2649 * terror units don't have inventories.
2650 * @return True if an inventory is available, false otherwise.
2651 */
hasInventory() const2652 bool BattleUnit::hasInventory() const
2653 {
2654 return (_armor->getSize() == 1 && _rank != "STR_LIVE_TERRORIST");
2655 }
2656
2657 }
2658