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 <cmath>
21 #include <sstream>
22 #include <typeinfo>
23 #include "BattlescapeGame.h"
24 #include "BattlescapeState.h"
25 #include "Map.h"
26 #include "Camera.h"
27 #include "NextTurnState.h"
28 #include "AbortMissionState.h"
29 #include "BattleState.h"
30 #include "UnitTurnBState.h"
31 #include "UnitWalkBState.h"
32 #include "ProjectileFlyBState.h"
33 #include "ExplosionBState.h"
34 #include "TileEngine.h"
35 #include "UnitInfoState.h"
36 #include "UnitDieBState.h"
37 #include "UnitPanicBState.h"
38 #include "AlienBAIState.h"
39 #include "CivilianBAIState.h"
40 #include "Pathfinding.h"
41 #include "../Engine/Game.h"
42 #include "../Engine/Language.h"
43 #include "../Engine/Sound.h"
44 #include "../Resource/ResourcePack.h"
45 #include "../Interface/Cursor.h"
46 #include "../Savegame/SavedGame.h"
47 #include "../Savegame/SavedBattleGame.h"
48 #include "../Savegame/Tile.h"
49 #include "../Savegame/BattleUnit.h"
50 #include "../Savegame/BattleItem.h"
51 #include "../Ruleset/Ruleset.h"
52 #include "../Ruleset/RuleItem.h"
53 #include "../Ruleset/RuleInventory.h"
54 #include "../Ruleset/Armor.h"
55 #include "../Engine/Options.h"
56 #include "../Engine/RNG.h"
57 #include "InfoboxState.h"
58 #include "InfoboxOKState.h"
59 #include "UnitFallBState.h"
60 #include "../Engine/Logger.h"
61 
62 namespace OpenXcom
63 {
64 
65 bool BattlescapeGame::_debugPlay = false;
66 
67 /**
68  * Initializes all the elements in the Battlescape screen.
69  * @param save Pointer to the save game.
70  * @param parentState Pointer to the parent battlescape state.
71  */
BattlescapeGame(SavedBattleGame * save,BattlescapeState * parentState)72 BattlescapeGame::BattlescapeGame(SavedBattleGame *save, BattlescapeState *parentState) : _save(save), _parentState(parentState), _playedAggroSound(false), _endTurnRequested(false), _kneelReserved(false)
73 {
74 	_tuReserved = BA_NONE;
75 	_playerTUReserved = BA_NONE;
76 	_debugPlay = false;
77 	_playerPanicHandled = true;
78 	_AIActionCounter = 0;
79 	_AISecondMove = false;
80 	_currentAction.actor = 0;
81 
82 	checkForCasualties(0, 0, true);
83 	cancelCurrentAction();
84 	_currentAction.targeting = false;
85 	_currentAction.type = BA_NONE;
86 }
87 
88 
89 /**
90  * Delete BattlescapeGame.
91  */
~BattlescapeGame()92 BattlescapeGame::~BattlescapeGame()
93 {
94 	for (std::list<BattleState*>::iterator i = _states.begin(); i != _states.end(); ++i)
95 	{
96 		delete *i;
97 	}
98 	cleanupDeleted();
99 }
100 
101 /**
102  * Checks for units panicking or falling and so on.
103  */
think()104 void BattlescapeGame::think()
105 {
106 	// nothing is happening - see if we need some alien AI or units panicking or what have you
107 	if (_states.empty())
108 	{
109 		// it's a non player side (ALIENS or CIVILIANS)
110 		if (_save->getSide() != FACTION_PLAYER)
111 		{
112 			if (!_debugPlay)
113 			{
114 				if (_save->getSelectedUnit())
115 				{
116 					if (!handlePanickingUnit(_save->getSelectedUnit()))
117 						handleAI(_save->getSelectedUnit());
118 				}
119 				else
120 				{
121 					if (_save->selectNextPlayerUnit(true, _AISecondMove) == 0)
122 					{
123 						if (!_save->getDebugMode())
124 						{
125 							_endTurnRequested = true;
126 							statePushBack(0); // end AI turn
127 						}
128 						else
129 						{
130 							_save->selectNextPlayerUnit();
131 							_debugPlay = true;
132 						}
133 					}
134 				}
135 			}
136 		}
137 		else
138 		{
139 			// it's a player side && we have not handled all panicking units
140 			if (!_playerPanicHandled)
141 			{
142 				_playerPanicHandled = handlePanickingPlayer();
143 				_save->getBattleState()->updateSoldierInfo();
144 			}
145 		}
146 		if (_save->getUnitsFalling())
147 		{
148 			statePushFront(new UnitFallBState(this));
149 			_save->setUnitsFalling(false);
150 		}
151 	}
152 }
153 
154 /**
155  * Initializes the Battlescape game.
156  */
init()157 void BattlescapeGame::init()
158 {
159 	if (_save->getSide() == FACTION_PLAYER && _save->getTurn() > 1)
160 	{
161 		_playerPanicHandled = false;
162 	}
163 }
164 
165 
166 /**
167  * Handles the processing of the AI states of a unit.
168  * @param unit Pointer to a unit.
169  */
handleAI(BattleUnit * unit)170 void BattlescapeGame::handleAI(BattleUnit *unit)
171 {
172 	std::wostringstream ss;
173 
174 	_tuReserved = BA_NONE;
175 	if (unit->getTimeUnits() <= 5)
176 	{
177 		unit->dontReselect();
178 	}
179 	if (unit->getTimeUnits() <= 5 || _AIActionCounter >= 2 || !unit->reselectAllowed())
180 	{
181 		if (_save->selectNextPlayerUnit(true, _AISecondMove) == 0)
182 		{
183 			if (!_save->getDebugMode())
184 			{
185 				_endTurnRequested = true;
186 				statePushBack(0); // end AI turn
187 			}
188 			else
189 			{
190 				_save->selectNextPlayerUnit();
191 				_debugPlay = true;
192 			}
193 		}
194 		if (_save->getSelectedUnit())
195 		{
196 			_parentState->updateSoldierInfo();
197 			getMap()->getCamera()->centerOnPosition(_save->getSelectedUnit()->getPosition());
198 			if (_save->getSelectedUnit()->getId() <= unit->getId())
199 			{
200 				_AISecondMove = true;
201 			}
202 		}
203 		_AIActionCounter = 0;
204 		return;
205 	}
206 
207 	unit->setVisible(false);
208 
209 	_save->getTileEngine()->calculateFOV(unit->getPosition()); // might need this populate _visibleUnit for a newly-created alien
210         // it might also help chryssalids realize they've zombified someone and need to move on
211 		// it should also hide units when they've killed the guy spotting them
212         // it's also for good luck
213 
214     BattleAIState *ai = unit->getCurrentAIState();
215 	if (!ai)
216 	{
217 		// for some reason the unit had no AI routine assigned..
218 		if (unit->getFaction() == FACTION_HOSTILE)
219 			unit->setAIState(new AlienBAIState(_save, unit, 0));
220 		else
221 			unit->setAIState(new CivilianBAIState(_save, unit, 0));
222 		ai = unit->getCurrentAIState();
223 	}
224 	_AIActionCounter++;
225 	if(_AIActionCounter == 1)
226 	{
227 		_playedAggroSound = false;
228 		unit->_hidingForTurn = false;
229 		if (Options::traceAI) { Log(LOG_INFO) << "#" << unit->getId() << "--" << unit->getType(); }
230 	}
231 	//AlienBAIState *aggro = dynamic_cast<AlienBAIState*>(ai); // this cast only works when ai was already AlienBAIState at heart
232 
233 	BattleAction action;
234 	action.actor = unit;
235     action.number = _AIActionCounter;
236 	unit->think(&action);
237 
238 	if (action.type == BA_RETHINK)
239 	{
240 		_parentState->debug(L"Rethink");
241 		unit->think(&action);
242 	}
243 
244     _AIActionCounter = action.number;
245 
246 	if (!unit->getMainHandWeapon() || !unit->getMainHandWeapon()->getAmmoItem())
247 	{
248 		if (unit->getOriginalFaction() == FACTION_HOSTILE && unit->getVisibleUnits()->empty())
249 		{
250 			findItem(&action);
251 		}
252 	}
253 
254 	if (unit->getCharging() != 0)
255 	{
256 		if (unit->getAggroSound() != -1 && !_playedAggroSound)
257 		{
258 			getResourcePack()->getSound("BATTLE.CAT", unit->getAggroSound())->play();
259 			_playedAggroSound = true;
260 		}
261 	}
262 	if (action.type == BA_WALK)
263 	{
264 		ss << L"Walking to " << action.target;
265 		_parentState->debug(ss.str());
266 
267 		if (_save->getTile(action.target))
268 		{
269 			_save->getPathfinding()->calculate(action.actor, action.target);//, _save->getTile(action.target)->getUnit());
270 		}
271 		if (_save->getPathfinding()->getStartDirection() != -1)
272 		{
273 			statePushBack(new UnitWalkBState(this, action));
274 		}
275 	}
276 
277 	if (action.type == BA_SNAPSHOT || action.type == BA_AUTOSHOT || action.type == BA_AIMEDSHOT || action.type == BA_THROW || action.type == BA_HIT || action.type == BA_MINDCONTROL || action.type == BA_PANIC || action.type == BA_LAUNCH)
278 	{
279 		if (action.type == BA_MINDCONTROL || action.type == BA_PANIC)
280 		{
281 			action.weapon = new BattleItem(_parentState->getGame()->getRuleset()->getItem("ALIEN_PSI_WEAPON"), _save->getCurrentItemId());
282 			action.TU = action.weapon->getRules()->getTUUse();
283 		}
284 		else
285 		{
286 			statePushBack(new UnitTurnBState(this, action));
287 		}
288 
289 		ss.clear();
290 		ss << L"Attack type=" << action.type << " target="<< action.target << " weapon=" << action.weapon->getRules()->getName().c_str();
291 		_parentState->debug(ss.str());
292 
293 		statePushBack(new ProjectileFlyBState(this, action));
294 		if (action.type == BA_MINDCONTROL || action.type == BA_PANIC)
295 		{
296 			bool success = _save->getTileEngine()->psiAttack(&action);
297 			if (success && action.type == BA_MINDCONTROL)
298 			{
299 				// show a little infobox with the name of the unit and "... is under alien control"
300 				Game *game = _parentState->getGame();
301 				BattleUnit *unit = _save->getTile(action.target)->getUnit();
302 				game->pushState(new InfoboxState(game, game->getLanguage()->getString("STR_IS_UNDER_ALIEN_CONTROL", unit->getGender()).arg(unit->getName(game->getLanguage()))));
303 			}
304 			_save->removeItem(action.weapon);
305 		}
306 	}
307 
308 	if (action.type == BA_NONE)
309 	{
310 		_parentState->debug(L"Idle");
311 		_AIActionCounter = 0;
312 		if (_save->selectNextPlayerUnit(true, _AISecondMove) == 0)
313 		{
314 			if (!_save->getDebugMode())
315 			{
316 				_endTurnRequested = true;
317 				statePushBack(0); // end AI turn
318 			}
319 			else
320 			{
321 				_save->selectNextPlayerUnit();
322 				_debugPlay = true;
323 			}
324 		}
325 		if (_save->getSelectedUnit())
326 		{
327 			_parentState->updateSoldierInfo();
328 			getMap()->getCamera()->centerOnPosition(_save->getSelectedUnit()->getPosition());
329 			if (_save->getSelectedUnit()->getId() <= unit->getId())
330 			{
331 				_AISecondMove = true;
332 			}
333 		}
334 	}
335 }
336 
337 /**
338  * Toggles the Kneel/Standup status of the unit.
339  * @param bu Pointer to a unit.
340  * @return If the action succeeded.
341  */
kneel(BattleUnit * bu)342 bool BattlescapeGame::kneel(BattleUnit *bu)
343 {
344 	int tu = bu->isKneeled()?8:4;
345 	if (bu->getType() == "SOLDIER" && !bu->isFloating() && ((!bu->isKneeled() && _kneelReserved) || checkReservedTU(bu, tu)))
346 	{
347 		if (bu->spendTimeUnits(tu))
348 		{
349 			bu->kneel(!bu->isKneeled());
350 			// kneeling or standing up can reveal new terrain or units. I guess.
351 			getTileEngine()->calculateFOV(bu);
352 			getMap()->cacheUnits();
353 			_parentState->updateSoldierInfo();
354 			getTileEngine()->checkReactionFire(bu);
355 			return true;
356 		}
357 		else
358 		{
359 			_parentState->warning("STR_NOT_ENOUGH_TIME_UNITS");
360 		}
361 	}
362 	return false;
363 }
364 
365 /**
366  * Ends the turn.
367  */
endTurn()368 void BattlescapeGame::endTurn()
369 {
370 
371 	Position p;
372 
373 	_tuReserved = _playerTUReserved;
374 	_debugPlay = false;
375 	_currentAction.type = BA_NONE;
376 	getMap()->getWaypoints()->clear();
377 	_currentAction.waypoints.clear();
378 	_parentState->showLaunchButton(false);
379 	_currentAction.targeting = false;
380 	_AISecondMove = false;
381 
382 	if (_save->getTileEngine()->closeUfoDoors())
383 	{
384 		getResourcePack()->getSound("BATTLE.CAT", 21)->play(); // ufo door closed
385 	}
386 
387 	// check for hot grenades on the ground
388 	for (int i = 0; i < _save->getMapSizeXYZ(); ++i)
389 	{
390 		for (std::vector<BattleItem*>::iterator it = _save->getTiles()[i]->getInventory()->begin(); it != _save->getTiles()[i]->getInventory()->end(); )
391 		{
392 			if ((*it)->getRules()->getBattleType() == BT_GRENADE && (*it)->getFuseTimer() == 0)  // it's a grenade to explode now
393 			{
394 				p.x = _save->getTiles()[i]->getPosition().x*16 + 8;
395 				p.y = _save->getTiles()[i]->getPosition().y*16 + 8;
396 				p.z = _save->getTiles()[i]->getPosition().z*24 - _save->getTiles()[i]->getTerrainLevel();
397 				statePushNext(new ExplosionBState(this, p, (*it), (*it)->getPreviousOwner()));
398 				_save->removeItem((*it));
399 				statePushBack(0);
400 				return;
401 			}
402 			++it;
403 		}
404 	}
405 	// check for terrain explosions
406 	Tile *t = _save->getTileEngine()->checkForTerrainExplosions();
407 	if (t)
408 	{
409 		Position p = Position(t->getPosition().x * 16, t->getPosition().y * 16, t->getPosition().z * 24);
410 		statePushNext(new ExplosionBState(this, p, 0, 0, t));
411 		t = _save->getTileEngine()->checkForTerrainExplosions();
412 		statePushBack(0);
413 		return;
414 	}
415 
416 	if (_save->getSide() != FACTION_NEUTRAL)
417 	{
418 		for (std::vector<BattleItem*>::iterator it = _save->getItems()->begin(); it != _save->getItems()->end(); ++it)
419 		{
420 				if (((*it)->getRules()->getBattleType() == BT_GRENADE || (*it)->getRules()->getBattleType() == BT_PROXIMITYGRENADE) && (*it)->getFuseTimer() > 0)
421 				{
422 					(*it)->setFuseTimer((*it)->getFuseTimer() - 1);
423 				}
424 		}
425 	}
426 
427 	// if all units from either faction are killed - the mission is over.
428 	int liveAliens = 0;
429 	int liveSoldiers = 0;
430 	// we'll tally them NOW, so that any infected units will... change
431 	tallyUnits(liveAliens, liveSoldiers, true);
432 
433 	_save->endTurn();
434 
435 	if (_save->getSide() == FACTION_PLAYER)
436 	{
437 		setupCursor();
438 	}
439 	else
440 	{
441 		getMap()->setCursorType(CT_NONE);
442 	}
443 
444 	checkForCasualties(0, 0, false, false);
445 
446 	// turn off MCed alien lighting.
447 	_save->getTileEngine()->calculateUnitLighting();
448 
449 	if (_save->isObjectiveDestroyed())
450 	{
451 		_parentState->finishBattle(false,liveSoldiers);
452 		return;
453 	}
454 
455 
456 	if (liveAliens > 0 && liveSoldiers > 0)
457 	{
458 		showInfoBoxQueue();
459 
460 		_parentState->updateSoldierInfo();
461 
462 		if (playableUnitSelected())
463 		{
464 			getMap()->getCamera()->centerOnPosition(_save->getSelectedUnit()->getPosition());
465 			setupCursor();
466 		}
467 	}
468 
469 	bool battleComplete = liveAliens == 0 || liveSoldiers == 0;
470 
471 	if ((_save->getSide() != FACTION_NEUTRAL || battleComplete)
472 		&& _endTurnRequested)
473 	{
474 		_parentState->getGame()->pushState(new NextTurnState(_parentState->getGame(), _save, _parentState));
475 	}
476 	_endTurnRequested = false;
477 }
478 
479 
480 /**
481  * Checks for casualties and adjusts morale accordingly.
482  * @param murderweapon Need to know this, for a HE explosion there is an instant death.
483  * @param murderer This is needed for credits for the kill.
484  * @param hiddenExplosion Set to true for the explosions of UFO Power sources at start of battlescape.
485  * @param terrainExplosion Set to true for the explosions of terrain.
486  */
checkForCasualties(BattleItem * murderweapon,BattleUnit * murderer,bool hiddenExplosion,bool terrainExplosion)487 void BattlescapeGame::checkForCasualties(BattleItem *murderweapon, BattleUnit *murderer, bool hiddenExplosion, bool terrainExplosion)
488 {
489 	for (std::vector<BattleUnit*>::iterator j = _save->getUnits()->begin(); j != _save->getUnits()->end(); ++j)
490 	{
491 		if ((*j)->getHealth() == 0 && (*j)->getStatus() != STATUS_DEAD && (*j)->getStatus() != STATUS_COLLAPSING)
492 		{
493 			BattleUnit *victim = (*j);
494 
495 			if (murderer)
496 			{
497 				murderer->addKillCount();
498 				victim->killedBy(murderer->getFaction());
499 				int modifier = murderer->getFaction() == FACTION_PLAYER ? _save->getMoraleModifier() : 100;
500 
501 				// if there is a known murderer, he will get a morale bonus if he is of a different faction (what with neutral?)
502 				if ((victim->getOriginalFaction() == FACTION_PLAYER && murderer->getFaction() == FACTION_HOSTILE) ||
503 					(victim->getOriginalFaction() == FACTION_HOSTILE && murderer->getFaction() == FACTION_PLAYER))
504 				{
505 					murderer->moraleChange(20 * modifier / 100);
506 				}
507 				// murderer will get a penalty with friendly fire
508 				if (victim->getOriginalFaction() == murderer->getOriginalFaction())
509 				{
510 					murderer->moraleChange(-(2000 / modifier));
511 				}
512 				if (victim->getOriginalFaction() == FACTION_NEUTRAL)
513 				{
514 					if (murderer->getOriginalFaction() == FACTION_PLAYER)
515 					{
516 						murderer->moraleChange(-(1000 / modifier));
517 					}
518 					else
519 					{
520 						murderer->moraleChange(10);
521 					}
522 				}
523 			}
524 
525 			if (victim->getFaction() != FACTION_NEUTRAL)
526 			{
527 				int modifier = _save->getMoraleModifier(victim);
528 				int loserMod = victim->getFaction() == FACTION_HOSTILE ? 100 : _save->getMoraleModifier();
529 				int winnerMod = victim->getFaction() == FACTION_HOSTILE ? _save->getMoraleModifier() : 100;
530 				for (std::vector<BattleUnit*>::iterator i = _save->getUnits()->begin(); i != _save->getUnits()->end(); ++i)
531 				{
532 					if (!(*i)->isOut() && (*i)->getArmor()->getSize() == 1)
533 					{
534 						// the losing squad all get a morale loss
535 						if ((*i)->getOriginalFaction() == victim->getOriginalFaction())
536 						{
537 							int bravery = (110 - (*i)->getStats()->bravery) / 10;
538 							(*i)->moraleChange(-(modifier * 200 * bravery / loserMod / 100));
539 
540 							if (victim->getFaction() == FACTION_HOSTILE && murderer)
541 							{
542 								murderer->setTurnsSinceSpotted(0);
543 							}
544 						}
545 						// the winning squad all get a morale increase
546 						else
547 						{
548 							(*i)->moraleChange(10 * winnerMod / 100);
549 						}
550 					}
551 				}
552 			}
553 			if (murderweapon)
554 			{
555 				statePushNext(new UnitDieBState(this, (*j), murderweapon->getRules()->getDamageType(), false));
556 			}
557 			else
558 			{
559 				if (hiddenExplosion)
560 				{
561 					// this is instant death from UFO powersources, without screaming sounds
562 					statePushNext(new UnitDieBState(this, (*j), DT_HE, true));
563 				}
564 				else
565 				{
566 					if (terrainExplosion)
567 					{
568 						// terrain explosion
569 						statePushNext(new UnitDieBState(this, (*j), DT_HE, false));
570 					}
571 					else
572 					{
573 						// no murderer, and no terrain explosion, must be fatal wounds
574 						statePushNext(new UnitDieBState(this, (*j), DT_NONE, false));  // DT_NONE = STR_HAS_DIED_FROM_A_FATAL_WOUND
575 					}
576 				}
577 			}
578 		}
579 		else if ((*j)->getStunlevel() >= (*j)->getHealth() && (*j)->getStatus() != STATUS_DEAD && (*j)->getStatus() != STATUS_UNCONSCIOUS && (*j)->getStatus() != STATUS_COLLAPSING && (*j)->getStatus() != STATUS_TURNING)
580 		{
581 			statePushNext(new UnitDieBState(this, (*j), DT_STUN, true));
582 		}
583 	}
584 	BattleUnit *bu = _save->getSelectedUnit();
585 	if (_save->getSide() == FACTION_PLAYER)
586 	{
587 		_parentState->showPsiButton(bu && bu->getOriginalFaction() == FACTION_HOSTILE && bu->getStats()->psiSkill > 0 && !bu->isOut());
588 	}
589 }
590 
591 /**
592  * Shows the infoboxes in the queue (if any).
593  */
showInfoBoxQueue()594 void BattlescapeGame::showInfoBoxQueue()
595 {
596 	for (std::vector<InfoboxOKState*>::iterator i = _infoboxQueue.begin(); i != _infoboxQueue.end(); ++i)
597 	{
598 		_parentState->getGame()->pushState(*i);
599 	}
600 
601 	_infoboxQueue.clear();
602 }
603 
604 /**
605  * Handles the result of non target actions, like priming a grenade.
606  */
handleNonTargetAction()607 void BattlescapeGame::handleNonTargetAction()
608 {
609 	if (!_currentAction.targeting)
610 	{
611 		_currentAction.cameraPosition = Position(0,0,-1);
612 		if (_currentAction.type == BA_PRIME && _currentAction.value > -1)
613 		{
614 			if (_currentAction.actor->spendTimeUnits(_currentAction.TU))
615 			{
616 				_parentState->warning("STR_GRENADE_IS_ACTIVATED");
617 				_currentAction.weapon->setFuseTimer(_currentAction.value);
618 			}
619 			else
620 			{
621 				_parentState->warning("STR_NOT_ENOUGH_TIME_UNITS");
622 			}
623 		}
624 		if (_currentAction.type == BA_USE || _currentAction.type == BA_LAUNCH)
625 		{
626 			if (_currentAction.result.length() > 0)
627 			{
628 				_parentState->warning(_currentAction.result);
629 				_currentAction.result = "";
630 			}
631 			_save->reviveUnconsciousUnits();
632 		}
633 		if (_currentAction.type == BA_HIT)
634 		{
635 			if (_currentAction.result.length() > 0)
636 			{
637 				_parentState->warning(_currentAction.result);
638 				_currentAction.result = "";
639 			}
640 			else
641 			{
642 				if (_currentAction.actor->spendTimeUnits(_currentAction.TU))
643 				{
644 					statePushBack(new ProjectileFlyBState(this, _currentAction));
645 					return;
646 				}
647 				else
648 				{
649 					_parentState->warning("STR_NOT_ENOUGH_TIME_UNITS");
650 				}
651 			}
652 		}
653 		_currentAction.type = BA_NONE;
654 		_parentState->updateSoldierInfo();
655 	}
656 
657 	setupCursor();
658 }
659 
660 /**
661  * Sets the cursor according to the selected action.
662  */
setupCursor()663 void BattlescapeGame::setupCursor()
664 {
665 	if (_currentAction.targeting)
666 	{
667 		if (_currentAction.type == BA_THROW)
668 		{
669 			getMap()->setCursorType(CT_THROW);
670 		}
671 		else if (_currentAction.type == BA_MINDCONTROL || _currentAction.type == BA_PANIC || _currentAction.type == BA_USE)
672 		{
673 			getMap()->setCursorType(CT_PSI);
674 		}
675 		else if (_currentAction.type == BA_LAUNCH)
676 		{
677 			getMap()->setCursorType(CT_WAYPOINT);
678 		}
679 		else
680 		{
681 			getMap()->setCursorType(CT_AIM);
682 		}
683 	}
684 	else
685 	{
686 		_currentAction.actor = _save->getSelectedUnit();
687 		if (_currentAction.actor)
688 		{
689 			getMap()->setCursorType(CT_NORMAL, _currentAction.actor->getArmor()->getSize());
690 		}
691 	}
692 }
693 
694 /**
695  * Determines whether a playable unit is selected. Normally only player side units can be selected, but in debug mode one can play with aliens too :)
696  * Is used to see if stats can be displayed and action buttons will work.
697  * @return Whether a playable unit is selected.
698  */
playableUnitSelected()699 bool BattlescapeGame::playableUnitSelected()
700 {
701 	return _save->getSelectedUnit() != 0 && (_save->getSide() == FACTION_PLAYER || _save->getDebugMode());
702 }
703 
704 /**
705  * Gives time slice to the front state.
706  */
handleState()707 void BattlescapeGame::handleState()
708 {
709 	if (!_states.empty())
710 	{
711 		// end turn request?
712 		if (_states.front() == 0)
713 		{
714 			_states.pop_front();
715 			endTurn();
716 			return;
717 		}
718 		else
719 		{
720 			_states.front()->think();
721 		}
722 		getMap()->invalidate(); // redraw map
723 	}
724 }
725 
726 /**
727  * Pushes a state to the front of the queue and starts it.
728  * @param bs Battlestate.
729  */
statePushFront(BattleState * bs)730 void BattlescapeGame::statePushFront(BattleState *bs)
731 {
732 	_states.push_front(bs);
733 	bs->init();
734 }
735 
736 /**
737  * Pushes a state as the next state after the current one.
738  * @param bs Battlestate.
739  */
statePushNext(BattleState * bs)740 void BattlescapeGame::statePushNext(BattleState *bs)
741 {
742 	if (_states.empty())
743 	{
744 		_states.push_front(bs);
745 		bs->init();
746 	}
747 	else
748 	{
749 		_states.insert(++_states.begin(), bs);
750 	}
751 
752 }
753 
754 /**
755  * Pushes a state to the back.
756  * @param bs Battlestate.
757  */
statePushBack(BattleState * bs)758 void BattlescapeGame::statePushBack(BattleState *bs)
759 {
760 	if (_states.empty())
761 	{
762 		_states.push_front(bs);
763 		// end turn request?
764 		if (_states.front() == 0)
765 		{
766 			_states.pop_front();
767 			endTurn();
768 			return;
769 		}
770 		else
771 		{
772 			bs->init();
773 		}
774 	}
775 	else
776 	{
777 		_states.push_back(bs);
778 	}
779 }
780 
781 /**
782  * Removes the current state.
783  *
784  * This is a very important function. It is called by a BattleState (walking, projectile is flying, explosions,...) at the moment this state has finished its action.
785  * Here we check the result of that action and do all the aftermath.
786  * The state is popped off the list.
787  */
popState()788 void BattlescapeGame::popState()
789 {
790 	if (Options::traceAI)
791 	{
792 		Log(LOG_INFO) << "BattlescapeGame::popState() #" << _AIActionCounter << " with " << (_save->getSelectedUnit() ? _save->getSelectedUnit()->getTimeUnits() : -9999) << " TU";
793 	}
794 	bool actionFailed = false;
795 
796 	if (_states.empty()) return;
797 
798 	BattleAction action = _states.front()->getAction();
799 
800 	if (action.actor && action.result.length() > 0 && action.actor->getFaction() == FACTION_PLAYER
801     && _playerPanicHandled && (_save->getSide() == FACTION_PLAYER || _debugPlay))
802 	{
803 		_parentState->warning(action.result);
804 		actionFailed = true;
805 	}
806 	_deleted.push_back(_states.front());
807 	_states.pop_front();
808 
809 	// handle the end of this unit's actions
810 	if (action.actor && noActionsPending(action.actor))
811 	{
812 		if (action.actor->getFaction() == FACTION_PLAYER)
813 		{
814 			// spend TUs of "target triggered actions" (shooting, throwing) only
815 			// the other actions' TUs (healing,scanning,..) are already take care of
816 			if (action.targeting && _save->getSelectedUnit() && !actionFailed)
817 			{
818 				action.actor->spendTimeUnits(action.TU);
819 			}
820 			if (_save->getSide() == FACTION_PLAYER)
821 			{
822 				// after throwing the cursor returns to default cursor, after shooting it stays in targeting mode and the player can shoot again in the same mode (autoshot,snap,aimed)
823 				if ((action.type == BA_THROW || action.type == BA_LAUNCH) && !actionFailed)
824 				{
825 					// clean up the waypoints
826 					if (action.type == BA_LAUNCH)
827 					{
828 						_currentAction.waypoints.clear();
829 					}
830 
831 					cancelCurrentAction(true);
832 				}
833 				_parentState->getGame()->getCursor()->setVisible(true);
834 				setupCursor();
835 			}
836 		}
837 		else
838 		{
839 			// spend TUs
840 			action.actor->spendTimeUnits(action.TU);
841 			if (_save->getSide() != FACTION_PLAYER && !_debugPlay)
842 			{
843 				 // AI does three things per unit, before switching to the next, or it got killed before doing the second thing
844 				if (_AIActionCounter > 2 || _save->getSelectedUnit() == 0 || _save->getSelectedUnit()->isOut())
845 				{
846 					if (_save->getSelectedUnit())
847 					{
848 						_save->getSelectedUnit()->setCache(0);
849 						getMap()->cacheUnit(_save->getSelectedUnit());
850 					}
851 					_AIActionCounter = 0;
852 					if (_states.empty() && _save->selectNextPlayerUnit(true) == 0)
853 					{
854 						if (!_save->getDebugMode())
855 						{
856 							_endTurnRequested = true;
857 							statePushBack(0); // end AI turn
858 						}
859 						else
860 						{
861 							_save->selectNextPlayerUnit();
862 							_debugPlay = true;
863 						}
864 					}
865 					if (_save->getSelectedUnit())
866 					{
867 						getMap()->getCamera()->centerOnPosition(_save->getSelectedUnit()->getPosition());
868 					}
869 				}
870 			}
871 			else if (_debugPlay)
872 			{
873 				_parentState->getGame()->getCursor()->setVisible(true);
874 				setupCursor();
875 			}
876 		}
877 	}
878 
879 	if (!_states.empty())
880 	{
881 		// end turn request?
882 		if (_states.front() == 0)
883 		{
884 			while (!_states.empty())
885 			{
886 				if (_states.front() == 0)
887 					_states.pop_front();
888 				else
889 					break;
890 			}
891 			if (_states.empty())
892 			{
893 				endTurn();
894 				return;
895 			}
896 			else
897 			{
898 				_states.push_back(0);
899 			}
900 		}
901 		// init the next state in queue
902 		_states.front()->init();
903 	}
904 
905 	// the currently selected unit died or became unconscious or disappeared inexplicably
906 	if (_save->getSelectedUnit() == 0 || _save->getSelectedUnit()->isOut())
907 	{
908 		cancelCurrentAction();
909 		getMap()->setCursorType(CT_NORMAL, 1);
910 		_parentState->getGame()->getCursor()->setVisible(true);
911 		_save->setSelectedUnit(0);
912 	}
913 	_parentState->updateSoldierInfo();
914 }
915 
916 /**
917  * Determines whether there are any actions pending for the given unit.
918  * @param bu BattleUnit.
919  * @return True if there are no actions pending.
920  */
noActionsPending(BattleUnit * bu)921 bool BattlescapeGame::noActionsPending(BattleUnit *bu)
922 {
923 	if (_states.empty()) return true;
924 
925 	for (std::list<BattleState*>::iterator i = _states.begin(); i != _states.end(); ++i)
926 	{
927 		if ((*i) != 0 && (*i)->getAction().actor == bu)
928 			return false;
929 	}
930 
931 	return true;
932 }
933 /**
934  * Sets the timer interval for think() calls of the state.
935  * @param interval An interval in ms.
936  */
setStateInterval(Uint32 interval)937 void BattlescapeGame::setStateInterval(Uint32 interval)
938 {
939 	_parentState->setStateInterval(interval);
940 }
941 
942 
943 /**
944  * Checks against reserved time units.
945  * @param bu Pointer to the unit.
946  * @param tu Number of time units to check.
947  * @param justChecking True to suppress error messages, false otherwise.
948  * @return bool Whether or not we got enough time units.
949  */
checkReservedTU(BattleUnit * bu,int tu,bool justChecking)950 bool BattlescapeGame::checkReservedTU(BattleUnit *bu, int tu, bool justChecking)
951 {
952     BattleActionType effectiveTuReserved = _tuReserved; // avoid changing _tuReserved in this method
953 
954 	if (_save->getSide() != FACTION_PLAYER) // aliens reserve TUs as a percentage rather than just enough for a single action.
955 	{
956 		if (_save->getSide() == FACTION_NEUTRAL)
957 		{
958 			return tu <= bu->getTimeUnits();
959 		}
960 		switch (effectiveTuReserved)
961 		{
962 		case BA_SNAPSHOT: return tu + (bu->getStats()->tu / 3) <= bu->getTimeUnits(); break; // 33%
963 		case BA_AUTOSHOT: return tu + ((bu->getStats()->tu / 5)*2) <= bu->getTimeUnits(); break; // 40%
964 		case BA_AIMEDSHOT: return tu + (bu->getStats()->tu / 2) <= bu->getTimeUnits(); break; // 50%
965 		default: return tu <= bu->getTimeUnits(); break;
966 		}
967 	}
968 
969 	// check TUs against slowest weapon if we have two weapons
970 	BattleItem *slowestWeapon = bu->getMainHandWeapon(false);
971 	// if the weapon has no autoshot, reserve TUs for snapshot
972 	if (bu->getActionTUs(_tuReserved, slowestWeapon) == 0 && _tuReserved == BA_AUTOSHOT)
973 	{
974 		effectiveTuReserved = BA_SNAPSHOT;
975 	}
976 	// likewise, if we don't have a snap shot available, try aimed.
977 	if (bu->getActionTUs(effectiveTuReserved, slowestWeapon) == 0 && effectiveTuReserved == BA_SNAPSHOT)
978 	{
979 		effectiveTuReserved = BA_AIMEDSHOT;
980 	}
981 	const int tuKneel = (_kneelReserved && bu->getType() == "SOLDIER") ? 4 : 0;
982 	if ((effectiveTuReserved != BA_NONE || _kneelReserved) &&
983 		tu + tuKneel + bu->getActionTUs(effectiveTuReserved, slowestWeapon) > bu->getTimeUnits() &&
984 		(tuKneel + bu->getActionTUs(effectiveTuReserved, slowestWeapon) <= bu->getTimeUnits() || justChecking))
985 	{
986 		if (!justChecking)
987 		{
988 			if (tuKneel)
989 			{
990 				switch (effectiveTuReserved)
991 				{
992 				case BA_NONE: _parentState->warning("STR_TIME_UNITS_RESERVED_FOR_KNEELING"); break;
993 				default: _parentState->warning("STR_TIME_UNITS_RESERVED_FOR_KNEELING_AND_FIRING");
994 				}
995 			}
996 			else
997 			{
998 				switch (effectiveTuReserved)
999 				{
1000 				case BA_SNAPSHOT: _parentState->warning("STR_TIME_UNITS_RESERVED_FOR_SNAP_SHOT"); break;
1001 				case BA_AUTOSHOT: _parentState->warning("STR_TIME_UNITS_RESERVED_FOR_AUTO_SHOT"); break;
1002 				case BA_AIMEDSHOT: _parentState->warning("STR_TIME_UNITS_RESERVED_FOR_AIMED_SHOT"); break;
1003 				default: ;
1004 				}
1005 			}
1006 		}
1007 		return false;
1008 	}
1009 
1010 	return true;
1011 }
1012 
1013 
1014 
1015 /**
1016  * Picks the first soldier that is panicking.
1017  * @return True when all panicking is over.
1018  */
handlePanickingPlayer()1019 bool BattlescapeGame::handlePanickingPlayer()
1020 {
1021 	for (std::vector<BattleUnit*>::iterator j = _save->getUnits()->begin(); j != _save->getUnits()->end(); ++j)
1022 	{
1023 		if ((*j)->getFaction() == FACTION_PLAYER && (*j)->getOriginalFaction() == FACTION_PLAYER && handlePanickingUnit(*j))
1024 			return false;
1025 	}
1026 	return true;
1027 }
1028 
1029 /**
1030  * Common function for hanlding panicking units.
1031  * @return False when unit not in panicking mode.
1032  */
handlePanickingUnit(BattleUnit * unit)1033 bool BattlescapeGame::handlePanickingUnit(BattleUnit *unit)
1034 {
1035 	UnitStatus status = unit->getStatus();
1036 	if (status != STATUS_PANICKING && status != STATUS_BERSERK) return false;
1037 	_save->setSelectedUnit(unit);
1038 	_parentState->getMap()->setCursorType(CT_NONE);
1039 
1040 	// show a little infobox with the name of the unit and "... is panicking"
1041 	Game *game = _parentState->getGame();
1042 	if (unit->getVisible() || !Options::noAlienPanicMessages)
1043 	{
1044 		getMap()->getCamera()->centerOnPosition(unit->getPosition());
1045 		if (status == STATUS_PANICKING)
1046 		{
1047 			game->pushState(new InfoboxState(game, game->getLanguage()->getString("STR_HAS_PANICKED", unit->getGender()).arg(unit->getName(game->getLanguage()))));
1048 		}
1049 		else
1050 		{
1051 			game->pushState(new InfoboxState(game, game->getLanguage()->getString("STR_HAS_GONE_BERSERK", unit->getGender()).arg(unit->getName(game->getLanguage()))));
1052 		}
1053 	}
1054 
1055 	unit->abortTurn(); //makes the unit go to status STANDING :p
1056 
1057 	int flee = RNG::generate(0,100);
1058 	BattleAction ba;
1059 	ba.actor = unit;
1060 	switch (status)
1061 	{
1062 	case STATUS_PANICKING: // 1/2 chance to freeze and 1/2 chance try to flee
1063 		if (flee <= 50)
1064 		{
1065 			BattleItem *item = unit->getItem("STR_RIGHT_HAND");
1066 			if (item)
1067 			{
1068 				dropItem(unit->getPosition(), item, false, true);
1069 			}
1070 			item = unit->getItem("STR_LEFT_HAND");
1071 			if (item)
1072 			{
1073 				dropItem(unit->getPosition(), item, false, true);
1074 			}
1075 			unit->setCache(0);
1076 			ba.target = Position(unit->getPosition().x + RNG::generate(-5,5), unit->getPosition().y + RNG::generate(-5,5), unit->getPosition().z);
1077 			if (_save->getTile(ba.target)) // only walk towards it when the place exists
1078 			{
1079 				_save->getPathfinding()->calculate(ba.actor, ba.target);
1080 				statePushBack(new UnitWalkBState(this, ba));
1081 			}
1082 		}
1083 		break;
1084 	case STATUS_BERSERK: // berserk - do some weird turning around and then aggro towards an enemy unit or shoot towards random place
1085 		ba.type = BA_TURN;
1086 		for (int i= 0; i < 4; i++)
1087 		{
1088 			ba.target = Position(unit->getPosition().x + RNG::generate(-5,5), unit->getPosition().y + RNG::generate(-5,5), unit->getPosition().z);
1089 			statePushBack(new UnitTurnBState(this, ba));
1090 		}
1091 		for (std::vector<BattleUnit*>::iterator j = unit->getVisibleUnits()->begin(); j != unit->getVisibleUnits()->end(); ++j)
1092 		{
1093 			ba.target = (*j)->getPosition();
1094 			statePushBack(new UnitTurnBState(this, ba));
1095 		}
1096 		if (_save->getTile(ba.target) != 0)
1097 		{
1098 			ba.weapon = unit->getMainHandWeapon();
1099 			if(ba.weapon)
1100 			{
1101 				if (ba.weapon->getRules()->getBattleType() == BT_FIREARM)
1102 				{
1103 					ba.type = BA_SNAPSHOT;
1104 					int tu = ba.actor->getActionTUs(ba.type, ba.weapon);
1105 					for (int i= 0; i < 10; i++)
1106 					{
1107 						// fire shots until unit runs out of TUs
1108 						if (!ba.actor->spendTimeUnits(tu))
1109 							break;
1110 						statePushBack(new ProjectileFlyBState(this, ba));
1111 					}
1112 				}
1113 				else if (ba.weapon->getRules()->getBattleType() == BT_GRENADE)
1114 				{
1115 					if (ba.weapon->getFuseTimer() == -1)
1116 					{
1117 						ba.weapon->setFuseTimer(0);
1118 					}
1119 					ba.type = BA_THROW;
1120 					statePushBack(new ProjectileFlyBState(this, ba));
1121 				}
1122 			}
1123 		}
1124 		// replace the TUs from shooting
1125 		unit->setTimeUnits(unit->getStats()->tu);
1126 		ba.type = BA_NONE;
1127 		break;
1128 	default: break;
1129 	}
1130 	// Time units can only be reset after everything else occurs
1131 	statePushBack(new UnitPanicBState(this, ba.actor));
1132 	unit->moraleChange(+15);
1133 
1134 	return true;
1135 }
1136 
1137 /**
1138   * Cancels the current action the user had selected (firing, throwing,..)
1139   * @param bForce Force the action to be cancelled.
1140   * @return Whether an action was cancelled or not.
1141   */
cancelCurrentAction(bool bForce)1142 bool BattlescapeGame::cancelCurrentAction(bool bForce)
1143 {
1144 	bool bPreviewed = Options::battleNewPreviewPath != PATH_NONE;
1145 
1146 	if (_save->getPathfinding()->removePreview() && bPreviewed) return true;
1147 
1148 	if (_states.empty() || bForce)
1149 	{
1150 		if (_currentAction.targeting)
1151 		{
1152 			if (_currentAction.type == BA_LAUNCH && !_currentAction.waypoints.empty())
1153 			{
1154 				_currentAction.waypoints.pop_back();
1155 				if (!getMap()->getWaypoints()->empty())
1156 				{
1157 					getMap()->getWaypoints()->pop_back();
1158 				}
1159 				if (_currentAction.waypoints.empty())
1160 				{
1161 					_parentState->showLaunchButton(false);
1162 				}
1163 				return true;
1164 			}
1165 			else
1166 			{
1167 				if (Options::battleConfirmFireMode && !_currentAction.waypoints.empty())
1168 				{
1169 					_currentAction.waypoints.pop_back();
1170 					getMap()->getWaypoints()->pop_back();
1171 					return true;
1172 				}
1173 				_currentAction.targeting = false;
1174 				_currentAction.type = BA_NONE;
1175 				setupCursor();
1176 				_parentState->getGame()->getCursor()->setVisible(true);
1177 				return true;
1178 			}
1179 		}
1180 	}
1181 	else if (!_states.empty() && _states.front() != 0)
1182 	{
1183 		_states.front()->cancel();
1184 		return true;
1185 	}
1186 
1187 	return false;
1188 }
1189 /**
1190  * Gets a pointer to access action members directly.
1191  * @return Pointer to action.
1192  */
getCurrentAction()1193 BattleAction *BattlescapeGame::getCurrentAction()
1194 {
1195 	return &_currentAction;
1196 }
1197 
1198 /**
1199  * Determines whether an action is currently going on?
1200  * @return true or false.
1201  */
isBusy()1202 bool BattlescapeGame::isBusy()
1203 {
1204 	return !_states.empty();
1205 }
1206 
1207 /**
1208  * Activates primary action (left click).
1209  * @param pos Position on the map.
1210  */
primaryAction(const Position & pos)1211 void BattlescapeGame::primaryAction(const Position &pos)
1212 {
1213 	bool bPreviewed = Options::battleNewPreviewPath != PATH_NONE;
1214 
1215 	if (_currentAction.targeting && _save->getSelectedUnit())
1216 	{
1217 		if (_currentAction.type == BA_LAUNCH)
1218 		{
1219 			_parentState->showLaunchButton(true);
1220 			_currentAction.waypoints.push_back(pos);
1221 			getMap()->getWaypoints()->push_back(pos);
1222 		}
1223 		else if (_currentAction.type == BA_USE && _currentAction.weapon->getRules()->getBattleType() == BT_MINDPROBE)
1224 		{
1225 			if (_save->selectUnit(pos) && _save->selectUnit(pos)->getFaction() != _save->getSelectedUnit()->getFaction() && _save->selectUnit(pos)->getVisible())
1226 			{
1227 				if (!_currentAction.weapon->getRules()->isLOSRequired() ||
1228 					std::find(_currentAction.actor->getVisibleUnits()->begin(), _currentAction.actor->getVisibleUnits()->end(), _save->selectUnit(pos)) != _currentAction.actor->getVisibleUnits()->end())
1229 				{
1230 					if (_currentAction.actor->spendTimeUnits(_currentAction.TU))
1231 					{
1232 						_parentState->getGame()->getResourcePack()->getSound("BATTLE.CAT", _currentAction.weapon->getRules()->getHitSound())->play();
1233 						_parentState->getGame()->pushState (new UnitInfoState(_parentState->getGame(), _save->selectUnit(pos), _parentState, false, true));
1234 						cancelCurrentAction();
1235 					}
1236 					else
1237 					{
1238 						_parentState->warning("STR_NOT_ENOUGH_TIME_UNITS");
1239 					}
1240 				}
1241 				else
1242 				{
1243 						_parentState->warning("STR_NO_LINE_OF_FIRE");
1244 				}
1245 			}
1246 		}
1247 		else if (_currentAction.type == BA_PANIC || _currentAction.type == BA_MINDCONTROL)
1248 		{
1249 			if (_save->selectUnit(pos) && _save->selectUnit(pos)->getFaction() != _save->getSelectedUnit()->getFaction() && _save->selectUnit(pos)->getVisible())
1250 			{
1251 				bool builtinpsi = !_currentAction.weapon;
1252 				if (builtinpsi)
1253 				{
1254 					_currentAction.weapon = new BattleItem(_parentState->getGame()->getRuleset()->getItem("ALIEN_PSI_WEAPON"), _save->getCurrentItemId());
1255 				}
1256 				_currentAction.TU = _currentAction.actor->getActionTUs(_currentAction.type, _currentAction.weapon);
1257 				_currentAction.target = pos;
1258 				if (!_currentAction.weapon->getRules()->isLOSRequired() ||
1259 					std::find(_currentAction.actor->getVisibleUnits()->begin(), _currentAction.actor->getVisibleUnits()->end(), _save->selectUnit(pos)) != _currentAction.actor->getVisibleUnits()->end())
1260 				{
1261 					// get the sound/animation started
1262 					getMap()->setCursorType(CT_NONE);
1263 					_parentState->getGame()->getCursor()->setVisible(false);
1264 					_currentAction.cameraPosition = getMap()->getCamera()->getMapOffset();
1265 					statePushBack(new ProjectileFlyBState(this, _currentAction));
1266 					if (_currentAction.TU <= _currentAction.actor->getTimeUnits())
1267 					{
1268 						if (getTileEngine()->psiAttack(&_currentAction))
1269 						{
1270 							// show a little infobox if it's successful
1271 							Game *game = _parentState->getGame();
1272 							if (_currentAction.type == BA_PANIC)
1273 								game->pushState(new InfoboxState(game, game->getLanguage()->getString("STR_MORALE_ATTACK_SUCCESSFUL")));
1274 							else if (_currentAction.type == BA_MINDCONTROL)
1275 								game->pushState(new InfoboxState(game, game->getLanguage()->getString("STR_MIND_CONTROL_SUCCESSFUL")));
1276 							_parentState->updateSoldierInfo();
1277 						}
1278 					}
1279 				}
1280 				else
1281 				{
1282 						_parentState->warning("STR_NO_LINE_OF_FIRE");
1283 				}
1284 				if (builtinpsi)
1285 				{
1286 					_save->removeItem(_currentAction.weapon);
1287 					_currentAction.weapon = 0;
1288 				}
1289 			}
1290 		}
1291 		else if (Options::battleConfirmFireMode && (_currentAction.waypoints.empty() || pos != _currentAction.waypoints.front()))
1292 		{
1293 			_currentAction.waypoints.clear();
1294 			_currentAction.waypoints.push_back(pos);
1295 			getMap()->getWaypoints()->clear();
1296 			getMap()->getWaypoints()->push_back(pos);
1297 		}
1298 		else
1299 		{
1300 			_currentAction.target = pos;
1301 			getMap()->setCursorType(CT_NONE);
1302 
1303 			if (Options::battleConfirmFireMode)
1304 			{
1305 				_currentAction.waypoints.clear();
1306 				getMap()->getWaypoints()->clear();
1307 			}
1308 
1309 			_parentState->getGame()->getCursor()->setVisible(false);
1310 			_currentAction.cameraPosition = getMap()->getCamera()->getMapOffset();
1311 			_states.push_back(new ProjectileFlyBState(this, _currentAction));
1312 			statePushFront(new UnitTurnBState(this, _currentAction)); // first of all turn towards the target
1313 		}
1314 	}
1315 	else
1316 	{
1317 		_currentAction.actor = _save->getSelectedUnit();
1318 		BattleUnit *unit = _save->selectUnit(pos);
1319 		if (unit && unit != _save->getSelectedUnit() && (unit->getVisible() || _debugPlay))
1320 		{
1321 		//  -= select unit =-
1322 			if (unit->getFaction() == _save->getSide())
1323 			{
1324 				_save->setSelectedUnit(unit);
1325 				_parentState->updateSoldierInfo();
1326 				cancelCurrentAction();
1327 				setupCursor();
1328 				_currentAction.actor = unit;
1329 			}
1330 		}
1331 		else if (playableUnitSelected())
1332 		{
1333 			bool modifierPressed = (SDL_GetModState() & KMOD_CTRL) != 0;
1334 			if (bPreviewed &&
1335 				(_currentAction.target != pos || (_save->getPathfinding()->isModifierUsed() != modifierPressed)))
1336 			{
1337 				_save->getPathfinding()->removePreview();
1338 			}
1339 			_currentAction.run = false;
1340 			_currentAction.strafe = Options::strafe && modifierPressed && _save->getSelectedUnit()->getTurretType() == -1;
1341 			if (_currentAction.strafe && _save->getTileEngine()->distance(_currentAction.actor->getPosition(), pos) > 1)
1342 			{
1343 				_currentAction.run = true;
1344 				_currentAction.strafe = false;
1345 			}
1346 			_currentAction.target = pos;
1347 			_save->getPathfinding()->calculate(_currentAction.actor, _currentAction.target);
1348 			if (bPreviewed && !_save->getPathfinding()->previewPath() && _save->getPathfinding()->getStartDirection() != -1)
1349 			{
1350 				_save->getPathfinding()->removePreview();
1351 				bPreviewed = false;
1352 			}
1353 
1354 			if (!bPreviewed && _save->getPathfinding()->getStartDirection() != -1)
1355 			{
1356 				//  -= start walking =-
1357 				getMap()->setCursorType(CT_NONE);
1358 				_parentState->getGame()->getCursor()->setVisible(false);
1359 				statePushBack(new UnitWalkBState(this, _currentAction));
1360 			}
1361 		}
1362 	}
1363 }
1364 
1365 /**
1366  * Activates secondary action (right click).
1367  * @param pos Position on the map.
1368  */
secondaryAction(const Position & pos)1369 void BattlescapeGame::secondaryAction(const Position &pos)
1370 {
1371 	//  -= turn to or open door =-
1372 	_currentAction.target = pos;
1373 	_currentAction.actor = _save->getSelectedUnit();
1374 	_currentAction.strafe = Options::strafe && (SDL_GetModState() & KMOD_CTRL) != 0 && _save->getSelectedUnit()->getTurretType() > -1;
1375 	statePushBack(new UnitTurnBState(this, _currentAction));
1376 }
1377 
1378 /**
1379  * Handler for the blaster launcher button.
1380  */
launchAction()1381 void BattlescapeGame::launchAction()
1382 {
1383 	_parentState->showLaunchButton(false);
1384 	getMap()->getWaypoints()->clear();
1385 	_currentAction.target = _currentAction.waypoints.front();
1386 	getMap()->setCursorType(CT_NONE);
1387 	_parentState->getGame()->getCursor()->setVisible(false);
1388 	_currentAction.cameraPosition = getMap()->getCamera()->getMapOffset();
1389 	_states.push_back(new ProjectileFlyBState(this, _currentAction));
1390 	statePushFront(new UnitTurnBState(this, _currentAction)); // first of all turn towards the target
1391 }
1392 
1393 /**
1394  * Handler for the psi button.
1395  */
psiButtonAction()1396 void BattlescapeGame::psiButtonAction()
1397 {
1398 	_currentAction.weapon = 0;
1399 	_currentAction.targeting = true;
1400 	_currentAction.type = BA_PANIC;
1401 	_currentAction.TU = 25;
1402 	setupCursor();
1403 }
1404 
1405 
1406 /**
1407  * Moves a unit up or down.
1408  * @param unit The unit.
1409  * @param dir Direction DIR_UP or DIR_DOWN.
1410  */
moveUpDown(BattleUnit * unit,int dir)1411 void BattlescapeGame::moveUpDown(BattleUnit *unit, int dir)
1412 {
1413 	_currentAction.target = unit->getPosition();
1414 	if (dir == Pathfinding::DIR_UP)
1415 	{
1416 		_currentAction.target.z++;
1417 	}
1418 	else
1419 	{
1420 		_currentAction.target.z--;
1421 	}
1422 	getMap()->setCursorType(CT_NONE);
1423 	_parentState->getGame()->getCursor()->setVisible(false);
1424 	if (_save->getSelectedUnit()->isKneeled())
1425 	{
1426 		kneel(_save->getSelectedUnit());
1427 	}
1428 	_save->getPathfinding()->calculate(_currentAction.actor, _currentAction.target);
1429 	statePushBack(new UnitWalkBState(this, _currentAction));
1430 }
1431 
1432 /**
1433  * Requests the end of the turn (waits for explosions etc to really end the turn).
1434  */
requestEndTurn()1435 void BattlescapeGame::requestEndTurn()
1436 {
1437 	cancelCurrentAction();
1438 	if (!_endTurnRequested)
1439 	{
1440 		_endTurnRequested = true;
1441 		statePushBack(0);
1442 	}
1443 }
1444 
1445 /**
1446  * Sets the TU reserved type.
1447  * @param tur A battleactiontype.
1448  * @param player is this requested by the player?
1449  */
setTUReserved(BattleActionType tur,bool player)1450 void BattlescapeGame::setTUReserved(BattleActionType tur, bool player)
1451 {
1452 	_tuReserved = tur;
1453 	if (player)
1454 	{
1455 		_playerTUReserved = tur;
1456 		_save->setTUReserved(tur);
1457 	}
1458 }
1459 
1460 /**
1461  * Drops an item to the floor and affects it with gravity.
1462  * @param position Position to spawn the item.
1463  * @param item Pointer to the item.
1464  * @param newItem Bool whether this is a new item.
1465  * @param removeItem Bool whether to remove the item from the owner.
1466  */
dropItem(const Position & position,BattleItem * item,bool newItem,bool removeItem)1467 void BattlescapeGame::dropItem(const Position &position, BattleItem *item, bool newItem, bool removeItem)
1468 {
1469 	Position p = position;
1470 
1471 	// don't spawn anything outside of bounds
1472 	if (_save->getTile(p) == 0)
1473 		return;
1474 
1475 	// don't ever drop fixed items
1476 	if (item->getRules()->isFixed())
1477 		return;
1478 
1479 	_save->getTile(p)->addItem(item, getRuleset()->getInventory("STR_GROUND"));
1480 
1481 	if (item->getUnit())
1482 	{
1483 		item->getUnit()->setPosition(p);
1484 	}
1485 
1486 	if(newItem)
1487 	{
1488 		_save->getItems()->push_back(item);
1489 	}
1490 	else if (_save->getSide() != FACTION_PLAYER)
1491 	{
1492 		item->setTurnFlag(true);
1493 	}
1494 
1495 	if (removeItem)
1496 	{
1497 		item->moveToOwner(0);
1498 	}
1499 	else if (item->getRules()->getBattleType() != BT_GRENADE && item->getRules()->getBattleType() != BT_PROXIMITYGRENADE)
1500 	{
1501 		item->setOwner(0);
1502 	}
1503 
1504 	getTileEngine()->applyGravity(_save->getTile(p));
1505 
1506 	if (item->getRules()->getBattleType() == BT_FLARE)
1507 	{
1508 		getTileEngine()->calculateTerrainLighting();
1509 		getTileEngine()->calculateFOV(position);
1510 	}
1511 
1512 }
1513 
1514 /**
1515  * Converts a unit into a unit of another type.
1516  * @param unit The unit to convert.
1517  * @param newType The type of unit to convert to.
1518  * @return Pointer to the new unit.
1519  */
convertUnit(BattleUnit * unit,std::string newType)1520 BattleUnit *BattlescapeGame::convertUnit(BattleUnit *unit, std::string newType)
1521 {
1522 	getSave()->getBattleState()->showPsiButton(false);
1523 	// in case the unit was unconscious
1524 	getSave()->removeUnconsciousBodyItem(unit);
1525 
1526 	unit->instaKill();
1527 
1528 	if (Options::battleNotifyDeath && unit->getFaction() == FACTION_PLAYER && unit->getOriginalFaction() == FACTION_PLAYER)
1529 	{
1530 		_parentState->getGame()->pushState(new InfoboxState(_parentState->getGame(), _parentState->getGame()->getLanguage()->getString("STR_HAS_BEEN_KILLED", unit->getGender()).arg(unit->getName(_parentState->getGame()->getLanguage()))));
1531 	}
1532 
1533 	for (std::vector<BattleItem*>::iterator i = unit->getInventory()->begin(); i != unit->getInventory()->end(); ++i)
1534 	{
1535 		dropItem(unit->getPosition(), (*i));
1536 		(*i)->setOwner(0);
1537 	}
1538 
1539 	unit->getInventory()->clear();
1540 
1541 	// remove unit-tile link
1542 	unit->setTile(0);
1543 
1544 	getSave()->getTile(unit->getPosition())->setUnit(0);
1545 	std::ostringstream newArmor;
1546 	newArmor << getRuleset()->getUnit(newType)->getArmor();
1547 	std::string terroristWeapon = getRuleset()->getUnit(newType)->getRace().substr(4);
1548 	terroristWeapon += "_WEAPON";
1549 	RuleItem *newItem = getRuleset()->getItem(terroristWeapon);
1550 	int difficulty = (int)(_parentState->getGame()->getSavedGame()->getDifficulty());
1551 
1552 	BattleUnit *newUnit = new BattleUnit(getRuleset()->getUnit(newType), FACTION_HOSTILE, _save->getUnits()->back()->getId() + 1, getRuleset()->getArmor(newArmor.str()), difficulty);
1553 
1554 	if (!difficulty)
1555 	{
1556 		newUnit->halveArmor();
1557 	}
1558 
1559 	getSave()->getTile(unit->getPosition())->setUnit(newUnit, _save->getTile(unit->getPosition() + Position(0,0,-1)));
1560 	newUnit->setPosition(unit->getPosition());
1561 	newUnit->setDirection(3);
1562 	newUnit->setCache(0);
1563 	newUnit->setTimeUnits(0);
1564 	getSave()->getUnits()->push_back(newUnit);
1565 	getMap()->cacheUnit(newUnit);
1566 	newUnit->setAIState(new AlienBAIState(getSave(), newUnit, 0));
1567 	BattleItem *bi = new BattleItem(newItem, getSave()->getCurrentItemId());
1568 	bi->moveToOwner(newUnit);
1569 	bi->setSlot(getRuleset()->getInventory("STR_RIGHT_HAND"));
1570 	getSave()->getItems()->push_back(bi);
1571 	getTileEngine()->calculateFOV(newUnit->getPosition());
1572 	getTileEngine()->applyGravity(newUnit->getTile());
1573 	//newUnit->getCurrentAIState()->think();
1574 	return newUnit;
1575 
1576 }
1577 
1578 /**
1579  * Gets the map.
1580  * @return map.
1581  */
getMap()1582 Map *BattlescapeGame::getMap()
1583 {
1584 	return _parentState->getMap();
1585 }
1586 /**
1587  * Gets the save.
1588  * @return save.
1589  */
getSave()1590 SavedBattleGame *BattlescapeGame::getSave()
1591 {
1592 	return _save;
1593 }
1594 /**
1595  * Gets the tilengine.
1596  * @return tilengine.
1597  */
getTileEngine()1598 TileEngine *BattlescapeGame::getTileEngine()
1599 {
1600 	return _save->getTileEngine();
1601 }
1602 /**
1603  * Gets the pathfinding.
1604  * @return pathfinding.
1605  */
getPathfinding()1606 Pathfinding *BattlescapeGame::getPathfinding()
1607 {
1608 	return _save->getPathfinding();
1609 }
1610 /**
1611  * Gets the resourcepack.
1612  * @return resourcepack.
1613  */
getResourcePack()1614 ResourcePack *BattlescapeGame::getResourcePack()
1615 {
1616 	return _parentState->getGame()->getResourcePack();
1617 }
1618 /**
1619  * Gets the ruleset.
1620  * @return ruleset.
1621  */
getRuleset() const1622 const Ruleset *BattlescapeGame::getRuleset() const
1623 {
1624 	return _parentState->getGame()->getRuleset();
1625 }
1626 
1627 
1628 /**
1629  * Tries to find an item and pick it up if possible.
1630  */
findItem(BattleAction * action)1631 void BattlescapeGame::findItem(BattleAction *action)
1632 {
1633 	// terrorists don't have hands.
1634 	if (action->actor->getRankString() != "STR_LIVE_TERRORIST")
1635 	{
1636 		// pick the best available item
1637 		BattleItem *targetItem = surveyItems(action);
1638 		// make sure it's worth taking
1639 		if (targetItem && worthTaking(targetItem, action))
1640 		{
1641 			// if we're already standing on it...
1642 			if (targetItem->getTile()->getPosition() == action->actor->getPosition())
1643 			{
1644 				// try to pick it up
1645 				if (takeItemFromGround(targetItem, action) == 0)
1646 				{
1647 					// if it isn't loaded or it is ammo
1648 					if (!targetItem->getAmmoItem())
1649 					{
1650 						// try to load our weapon
1651 						action->actor->checkAmmo();
1652 					}
1653 				}
1654 			}
1655 			else if (!targetItem->getTile()->getUnit() || targetItem->getTile()->getUnit()->isOut())
1656 			{
1657 				// if we're not standing on it, we should try to get to it.
1658 				action->target = targetItem->getTile()->getPosition();
1659 				action->type = BA_WALK;
1660 			}
1661 		}
1662 	}
1663 }
1664 
1665 
1666 /**
1667  * Searches through items on the map that were dropped on an alien turn, then picks the most "attractive" one.
1668  * @param action A pointer to the action being performed.
1669  * @return The item to attempt to take.
1670  */
surveyItems(BattleAction * action)1671 BattleItem *BattlescapeGame::surveyItems(BattleAction *action)
1672 {
1673 	std::vector<BattleItem*> droppedItems;
1674 
1675 	// first fill a vector with items on the ground that were dropped on the alien turn, and have an attraction value.
1676 	for (std::vector<BattleItem*>::iterator i = _save->getItems()->begin(); i != _save->getItems()->end(); ++i)
1677 	{
1678 		if ((*i)->getSlot() && (*i)->getSlot()->getId() == "STR_GROUND" && (*i)->getTile() && (*i)->getTurnFlag() && (*i)->getRules()->getAttraction())
1679 		{
1680 			droppedItems.push_back(*i);
1681 		}
1682 	}
1683 
1684 	BattleItem *targetItem = 0;
1685 	int maxWorth = 0;
1686 
1687 	// now select the most suitable candidate depending on attractiveness and distance
1688 	// (are we still talking about items?)
1689 	for (std::vector<BattleItem*>::iterator i = droppedItems.begin(); i != droppedItems.end(); ++i)
1690 	{
1691 		int currentWorth = (*i)->getRules()->getAttraction() / ((_save->getTileEngine()->distance(action->actor->getPosition(), (*i)->getTile()->getPosition()) * 2)+1);
1692 		if (currentWorth > maxWorth)
1693 		{
1694 			maxWorth = currentWorth;
1695 			targetItem = *i;
1696 		}
1697 	}
1698 
1699 	return targetItem;
1700 }
1701 
1702 
1703 /**
1704  * Assesses whether this item is worth trying to pick up, taking into account how many units we see,
1705  * whether or not the Weapon has ammo, and if we have ammo FOR it,
1706  * or, if it's ammo, checks if we have the weapon to go with it,
1707  * assesses the attraction value of the item and compares it with the distance to the object,
1708  * then returns false anyway.
1709  * @param item The item to attempt to take.
1710  * @param action A pointer to the action being performed.
1711  * @return false.
1712  */
worthTaking(BattleItem * item,BattleAction * action)1713 bool BattlescapeGame::worthTaking(BattleItem* item, BattleAction *action)
1714 {
1715 	int worthToTake = 0;
1716 
1717 	// don't even think about making a move for that gun if you can see a target, for some reason
1718 	// (maybe this should check for enemies spotting the tile the item is on?)
1719 	if (action->actor->getVisibleUnits()->empty())
1720 	{
1721 		// retrieve an insignificantly low value from the ruleset.
1722 		worthToTake = item->getRules()->getAttraction();
1723 
1724 		// it's always going to be worth while to try and take a blaster launcher, apparently
1725 		if (!item->getRules()->isWaypoint() && item->getRules()->getBattleType() != BT_AMMO)
1726 		{
1727 			// we only want weapons that HAVE ammo, or weapons that we have ammo FOR
1728 			bool ammoFound = true;
1729 			if (!item->getAmmoItem())
1730 			{
1731 				ammoFound = false;
1732 				for (std::vector<BattleItem*>::iterator i = action->actor->getInventory()->begin(); i != action->actor->getInventory()->end() && !ammoFound; ++i)
1733 				{
1734 					if ((*i)->getRules()->getBattleType() == BT_AMMO)
1735 					{
1736 						for (std::vector<std::string>::iterator j = item->getRules()->getCompatibleAmmo()->begin(); j != item->getRules()->getCompatibleAmmo()->end() && !ammoFound; ++j)
1737 						{
1738 							if ((*i)->getRules()->getName() == *j)
1739 							{
1740 								ammoFound = true;
1741 								break;
1742 							}
1743 						}
1744 					}
1745 				}
1746 			}
1747 			if (!ammoFound)
1748 			{
1749 				return false;
1750 			}
1751 		}
1752 
1753 		if ( item->getRules()->getBattleType() == BT_AMMO)
1754 		{
1755 			// similar to the above, but this time we're checking if the ammo is suitable for a weapon we have.
1756 			bool weaponFound = false;
1757 			for (std::vector<BattleItem*>::iterator i = action->actor->getInventory()->begin(); i != action->actor->getInventory()->end() && !weaponFound; ++i)
1758 			{
1759 				if ((*i)->getRules()->getBattleType() == BT_FIREARM)
1760 				{
1761 					for (std::vector<std::string>::iterator j = (*i)->getRules()->getCompatibleAmmo()->begin(); j != (*i)->getRules()->getCompatibleAmmo()->end() && !weaponFound; ++j)
1762 					{
1763 						if ((*i)->getRules()->getName() == *j)
1764 						{
1765 							weaponFound = true;
1766 							break;
1767 						}
1768 					}
1769 				}
1770 			}
1771 			if (!weaponFound)
1772 			{
1773 				return false;
1774 			}
1775 		}
1776 	}
1777 
1778     if (worthToTake)
1779     {
1780 		// use bad logic to determine if we'll have room for the item
1781 		int freeSlots = 25;
1782 		for (std::vector<BattleItem*>::iterator i = action->actor->getInventory()->begin(); i != action->actor->getInventory()->end(); ++i)
1783 		{
1784 			freeSlots -= (*i)->getRules()->getInventoryHeight() * (*i)->getRules()->getInventoryWidth();
1785 		}
1786 		int size = item->getRules()->getInventoryHeight() * item->getRules()->getInventoryWidth();
1787 		if (freeSlots < size)
1788 		{
1789 			return false;
1790 		}
1791 	}
1792 
1793 	// return false for any item that we aren't standing directly on top of with an attraction value less than 6 (aka always)
1794 	return (worthToTake - (_save->getTileEngine()->distance(action->actor->getPosition(), item->getTile()->getPosition())*2)) > 5;
1795 }
1796 
1797 
1798 /**
1799  * Picks the item up from the ground.
1800  *
1801  * At this point we've decided it's worth our while to grab this item, so we try to do just that.
1802  * First we check to make sure we have time units, then that we have space (using horrifying logic)
1803  * then we attempt to actually recover the item.
1804  * @param item The item to attempt to take.
1805  * @param action A pointer to the action being performed.
1806  * @return 0 if successful, 1 for no TUs, 2 for not enough room, 3 for "won't fit" and -1 for "something went horribly wrong".
1807  */
takeItemFromGround(BattleItem * item,BattleAction * action)1808 int BattlescapeGame::takeItemFromGround(BattleItem* item, BattleAction *action)
1809 {
1810 	const int unhandledError = -1;
1811 	const int success = 0;
1812 	const int notEnoughTimeUnits = 1;
1813 	const int notEnoughSpace = 2;
1814 	const int couldNotFit = 3;
1815 	int freeSlots = 25;
1816 
1817 	// make sure we have time units
1818 	if (action->actor->getTimeUnits() < 6)
1819 	{
1820 		return notEnoughTimeUnits;
1821 	}
1822 	else
1823 	{
1824 		// check to make sure we have enough space by checking all the sizes of items in our inventory
1825 		for (std::vector<BattleItem*>::iterator i = action->actor->getInventory()->begin(); i != action->actor->getInventory()->end(); ++i)
1826 		{
1827 			freeSlots -= (*i)->getRules()->getInventoryHeight() * (*i)->getRules()->getInventoryWidth();
1828 		}
1829 		if (freeSlots < item->getRules()->getInventoryHeight() * item->getRules()->getInventoryWidth())
1830 		{
1831 			return notEnoughSpace;
1832 		}
1833 		else
1834 		{
1835 			// check that the item will fit in our inventory, and if so, take it
1836 			if (takeItem(item, action))
1837 			{
1838 				action->actor->spendTimeUnits(6);
1839 				item->getTile()->removeItem(item);
1840 				return success;
1841 			}
1842 			else
1843 			{
1844 				return couldNotFit;
1845 			}
1846 		}
1847 	}
1848 	// shouldn't ever end up here
1849 	return unhandledError;
1850 }
1851 
1852 
1853 /**
1854  * Tries to fit an item into the unit's inventory, return false if you can't.
1855  * @param item The item to attempt to take.
1856  * @param action A pointer to the action being performed.
1857  * @return Whether or not the item was successfully retrieved.
1858  */
takeItem(BattleItem * item,BattleAction * action)1859 bool BattlescapeGame::takeItem(BattleItem* item, BattleAction *action)
1860 {
1861 	bool placed = false;
1862 	Ruleset* rules = _parentState->getGame()->getRuleset();
1863 	switch (item->getRules()->getBattleType())
1864 	{
1865 	case BT_AMMO:
1866 		// find equipped weapons that can be loaded with this ammo
1867 		if (action->actor->getItem("STR_RIGHT_HAND") && action->actor->getItem("STR_RIGHT_HAND")->getAmmoItem() == 0)
1868 		{
1869 			if (action->actor->getItem("STR_RIGHT_HAND")->setAmmoItem(item) == 0)
1870 			{
1871 				placed = true;
1872 			}
1873 		}
1874 		else
1875 		{
1876 			for (int i = 0; i != 4; ++i)
1877 			{
1878 				if (!action->actor->getItem("STR_BELT", i))
1879 				{
1880 					item->moveToOwner(action->actor);
1881 					item->setSlot(rules->getInventory("STR_BELT"));
1882 					item->setSlotX(i);
1883 					placed = true;
1884 					break;
1885 				}
1886 			}
1887 		}
1888 		break;
1889 	case BT_GRENADE:
1890 	case BT_PROXIMITYGRENADE:
1891 		for (int i = 0; i != 4; ++i)
1892 		{
1893 			if (!action->actor->getItem("STR_BELT", i))
1894 			{
1895 				item->moveToOwner(action->actor);
1896 				item->setSlot(rules->getInventory("STR_BELT"));
1897 				item->setSlotX(i);
1898 				placed = true;
1899 				break;
1900 			}
1901 		}
1902 		break;
1903 	case BT_FIREARM:
1904 	case BT_MELEE:
1905 		if (!action->actor->getItem("STR_RIGHT_HAND"))
1906 		{
1907 			item->moveToOwner(action->actor);
1908 			item->setSlot(rules->getInventory("STR_RIGHT_HAND"));
1909 			placed = true;
1910 		}
1911 		break;
1912 	case BT_MEDIKIT:
1913 	case BT_SCANNER:
1914 		if (!action->actor->getItem("STR_BACK_PACK"))
1915 		{
1916 			item->moveToOwner(action->actor);
1917 			item->setSlot(rules->getInventory("STR_BACK_PACK"));
1918 			placed = true;
1919 		}
1920 		break;
1921 	case BT_MINDPROBE:
1922 		if (!action->actor->getItem("STR_LEFT_HAND"))
1923 		{
1924 			item->moveToOwner(action->actor);
1925 			item->setSlot(rules->getInventory("STR_LEFT_HAND"));
1926 			placed = true;
1927 		}
1928 		break;
1929 	default: break;
1930 	}
1931 	return placed;
1932 }
1933 
1934 /**
1935  * Returns the action type that is reserved.
1936  * @return The type of action that is reserved.
1937  */
getReservedAction()1938 BattleActionType BattlescapeGame::getReservedAction()
1939 {
1940 	return _tuReserved;
1941 }
1942 
1943 /**
1944  * Tallies the living units in the game and, if required, converts units into their spawn unit.
1945  * @param &liveAliens The integer in which to store the live alien tally.
1946  * @param &liveSoldiers The integer in which to store the live XCom tally.
1947  * @param convert Should we convert infected units?
1948  */
tallyUnits(int & liveAliens,int & liveSoldiers,bool convert)1949 void BattlescapeGame::tallyUnits(int &liveAliens, int &liveSoldiers, bool convert)
1950 {
1951 	liveSoldiers = 0;
1952 	liveAliens = 0;
1953 
1954 	if (convert)
1955 	{
1956 		for (std::vector<BattleUnit*>::iterator j = _save->getUnits()->begin(); j != _save->getUnits()->end(); ++j)
1957 		{
1958 			if ((*j)->getHealth() > 0 && (*j)->getSpecialAbility() == SPECAB_RESPAWN)
1959 			{
1960 				(*j)->setSpecialAbility(SPECAB_NONE);
1961 				convertUnit((*j), (*j)->getSpawnUnit());
1962 				j = _save->getUnits()->begin();
1963 			}
1964 		}
1965 	}
1966 
1967 	for (std::vector<BattleUnit*>::iterator j = _save->getUnits()->begin(); j != _save->getUnits()->end(); ++j)
1968 	{
1969 		if (!(*j)->isOut())
1970 		{
1971 			if ((*j)->getOriginalFaction() == FACTION_HOSTILE)
1972 			{
1973 				if (!Options::allowPsionicCapture || (*j)->getFaction() != FACTION_PLAYER)
1974 				{
1975 					liveAliens++;
1976 				}
1977 			}
1978 			else if ((*j)->getOriginalFaction() == FACTION_PLAYER)
1979 			{
1980 				if ((*j)->getFaction() == FACTION_PLAYER)
1981 				{
1982 					liveSoldiers++;
1983 				}
1984 				else
1985 				{
1986 					liveAliens++;
1987 				}
1988 			}
1989 		}
1990 	}
1991 }
1992 
1993 /**
1994  * Sets the kneel reservation setting.
1995  * @param reserved Should we reserve an extra 4 TUs to kneel?
1996  */
setKneelReserved(bool reserved)1997 void BattlescapeGame::setKneelReserved(bool reserved)
1998 {
1999 	_kneelReserved = reserved;
2000 	_save->setKneelReserved(reserved);
2001 }
2002 
2003 /**
2004  * Gets the kneel reservation setting.
2005  * @return Kneel reservation setting.
2006  */
getKneelReserved()2007 bool BattlescapeGame::getKneelReserved()
2008 {
2009 	if (_save->getSelectedUnit() && _save->getSelectedUnit()->getGeoscapeSoldier())
2010 	{
2011 		return _kneelReserved;
2012 	}
2013 	return false;
2014 }
2015 
2016 /**
2017  * Checks if a unit has moved next to a proximity grenade.
2018  * Checks one tile around the unit in every direction.
2019  * For a large unit we check every tile it occupies.
2020  * @param unit Pointer to a unit.
2021  * @return True if a proximity grenade was triggered.
2022  */
checkForProximityGrenades(BattleUnit * unit)2023 bool BattlescapeGame::checkForProximityGrenades(BattleUnit *unit)
2024 {
2025 	int size = unit->getArmor()->getSize() - 1;
2026 	for (int x = size; x >= 0; x--)
2027 	{
2028 		for (int y = size; y >= 0; y--)
2029 		{
2030 			for (int tx = -1; tx < 2; tx++)
2031 			{
2032 				for (int ty = -1; ty < 2; ty++)
2033 				{
2034 					Tile *t = _save->getTile(unit->getPosition() + Position(x,y,0) + Position(tx,ty,0));
2035 					if (t)
2036 					{
2037 						for (std::vector<BattleItem*>::iterator i = t->getInventory()->begin(); i != t->getInventory()->end(); ++i)
2038 						{
2039 							if ((*i)->getRules()->getBattleType() == BT_PROXIMITYGRENADE && (*i)->getFuseTimer() == 0)
2040 							{
2041 								Position p;
2042 								p.x = t->getPosition().x*16 + 8;
2043 								p.y = t->getPosition().y*16 + 8;
2044 								p.z = t->getPosition().z*24 + t->getTerrainLevel();
2045 								statePushNext(new ExplosionBState(this, p, (*i), (*i)->getPreviousOwner()));
2046 								getSave()->removeItem(*i);
2047 								unit->setCache(0);
2048 								getMap()->cacheUnit(unit);
2049 								return true;
2050 							}
2051 						}
2052 					}
2053 				}
2054 			}
2055 		}
2056 	}
2057 	return false;
2058 }
2059 
cleanupDeleted()2060 void BattlescapeGame::cleanupDeleted()
2061 {
2062 	for (std::list<BattleState*>::iterator i = _deleted.begin(); i != _deleted.end(); ++i)
2063 	{
2064 		delete *i;
2065 	}
2066 	_deleted.clear();
2067 }
2068 
2069 }
2070