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