6666#include " WarningMessage.h"
6767#include " BattlescapeOptionsState.h"
6868#include " DebriefingState.h"
69+ #include " ../Engine/RNG.h"
70+ #include " InfoboxState.h"
6971
7072namespace OpenXcom
7173{
@@ -344,6 +346,8 @@ void BattlescapeState::init()
344346 _map->cacheUnits ();
345347 _map->draw ();
346348 updateSoldierInfo ();
349+ if (_battleGame->getSide () == FACTION_PLAYER )
350+ _playerPanicHandled = false ;
347351}
348352
349353/* *
@@ -371,13 +375,33 @@ void BattlescapeState::think()
371375 _game->pushState (*_popups.begin ());
372376 _popups.erase (_popups.begin ());
373377 popped = true ;
378+ return ;
374379 }
375380
376- // it's the alien side, and nothing is happening
377- if (_battleGame-> getSide () != FACTION_PLAYER && _states.empty () && !_debugPlay )
381+ // nothing is happening - see if we need some alien AI or units panicking or what have you
382+ if (_states.empty ())
378383 {
379- if (_battleGame->getSelectedUnit ())
380- handleAI (_battleGame->getSelectedUnit ());
384+ // it's a non player side (ALIENS or CIVILIANS)
385+ if (_battleGame->getSide () != FACTION_PLAYER )
386+ {
387+ if (!_debugPlay)
388+ {
389+ if (_battleGame->getSelectedUnit ())
390+ {
391+ if (!handlePanickingUnit (_battleGame->getSelectedUnit ()))
392+ handleAI (_battleGame->getSelectedUnit ());
393+ }
394+ }
395+ }
396+ else
397+ {
398+ // it's a player side && we have not handled all panicking units
399+ if (!_playerPanicHandled)
400+ {
401+ _playerPanicHandled = handlePanickingPlayer ();
402+ }
403+ }
404+
381405 }
382406
383407}
@@ -395,7 +419,6 @@ void BattlescapeState::handleAI(BattleUnit *unit)
395419 unit->think (&_action);
396420 if (_action.type == BA_WALK )
397421 {
398- _tuReserved = BA_SNAPSHOT ; // aliens are not dumb :)
399422 statePushBack (new UnitWalkBState (this , _action));
400423 }
401424
@@ -424,7 +447,7 @@ void BattlescapeState::handleAI(BattleUnit *unit)
424447 _debugPlay = true ;
425448 }
426449 }
427- if (playableUnitSelected ())
450+ if (_battleGame-> getSelectedUnit ())
428451 {
429452 _map->centerOnPosition (_battleGame->getSelectedUnit ()->getPosition ());
430453 }
@@ -687,8 +710,6 @@ void BattlescapeState::endTurn()
687710 Position p;
688711
689712 _debugPlay = false ;
690- _tuReserved = BA_NONE ;
691- _reserve = _btnReserveNone;
692713 _action.type = BA_NONE ;
693714
694715 // check for hot grenades on the ground
@@ -760,13 +781,19 @@ void BattlescapeState::endTurn()
760781 statePushNext (new ExplosionBState (this , p, 0 , 0 , t));
761782 }
762783
763- checkForCasualties (0 , 0 );
784+ bool bBattleIsOver = checkForCasualties (0 , 0 );
785+ if (bBattleIsOver)
786+ {
787+ return ;
788+ }
764789
765790 updateSoldierInfo ();
791+
766792 if (playableUnitSelected ())
767793 {
768794 _map->centerOnPosition (_battleGame->getSelectedUnit ()->getPosition ());
769795 }
796+
770797 _game->pushState (new NextTurnState (_game, _battleGame));
771798}
772799
@@ -775,8 +802,9 @@ void BattlescapeState::endTurn()
775802 * Checks for casualties and adjusts morale accordingly.
776803 * @param murderweapon
777804 * @param murderer
805+ * @return Whether the battle is finished.
778806 */
779- void BattlescapeState::checkForCasualties (BattleItem *murderweapon, BattleUnit *murderer)
807+ bool BattlescapeState::checkForCasualties (BattleItem *murderweapon, BattleUnit *murderer)
780808{
781809 // TODO : include rank bonuses and penalties !!
782810 for (std::vector<BattleUnit*>::iterator j = _battleGame->getUnits ()->begin (); j != _battleGame->getUnits ()->end (); ++j)
@@ -842,7 +870,10 @@ void BattlescapeState::checkForCasualties(BattleItem *murderweapon, BattleUnit *
842870 if (liveAliens == 0 || liveSoldiers == 0 )
843871 {
844872 finishBattle (false );
873+ return true ;
845874 }
875+
876+ return false ;
846877}
847878
848879/* *
@@ -966,7 +997,7 @@ void BattlescapeState::handleNonTargetAction()
966997 {
967998 if (_action.type == BA_PRIME && _action.value > -1 )
968999 {
969- if (_action.actor ->spendTimeUnits (_action.TU , _game-> getSavedGame ()-> getBattleGame ()-> getDebugMode ()))
1000+ if (_action.actor ->spendTimeUnits (_action.TU , dontSpendTUs ()))
9701001 {
9711002 _action.weapon ->setExplodeTurn (_game->getSavedGame ()->getBattleGame ()->getTurn () + _action.value );
9721003 }
@@ -1268,7 +1299,7 @@ void BattlescapeState::popState()
12681299
12691300 if (_states.empty ()) return ;
12701301
1271- if (_states.front ()->getResult ().length () > 0 && _battleGame->getSide () == FACTION_PLAYER )
1302+ if (_states.front ()->getResult ().length () > 0 && _battleGame->getSide () == FACTION_PLAYER && ! dontSpendTUs () )
12721303 {
12731304 _warning->showMessage (_game->getLanguage ()->getString (_states.front ()->getResult ()));
12741305 actionFailed = true ;
@@ -1279,7 +1310,7 @@ void BattlescapeState::popState()
12791310 {
12801311 if (_action.targeting && _battleGame->getSelectedUnit () && !actionFailed)
12811312 {
1282- _action.actor ->spendTimeUnits (_action.TU , _battleGame-> getDebugMode ());
1313+ _action.actor ->spendTimeUnits (_action.TU , dontSpendTUs ());
12831314 }
12841315 if (_action.type == BA_THROW && !actionFailed)
12851316 {
@@ -1396,6 +1427,8 @@ void BattlescapeState::popup(State *state)
13961427 */
13971428bool BattlescapeState::checkReservedTU (BattleUnit *bu, int tu)
13981429{
1430+ if (dontSpendTUs () || _battleGame->getSide () != FACTION_PLAYER ) return true ; // aliens don't reserve TUs
1431+
13991432 if (_tuReserved != BA_NONE &&
14001433 tu + bu->getActionTUs (_tuReserved, bu->getMainHandWeapon ()) > bu->getTimeUnits ())
14011434 {
@@ -1457,6 +1490,116 @@ void BattlescapeState::dropItem(const Position &position, BattleItem *item, bool
14571490 }
14581491
14591492 item->setSlot (_game->getRuleset ()->getInventory (" STR_GROUND" ));
1493+ item->setOwner (0 );
1494+ }
1495+
1496+ /* *
1497+ * Pick the first soldier from the list in status panic.
1498+ * @return True when all panicking is over.
1499+ */
1500+ bool BattlescapeState::handlePanickingPlayer ()
1501+ {
1502+ for (std::vector<BattleUnit*>::iterator j = _battleGame->getUnits ()->begin (); j != _battleGame->getUnits ()->end (); ++j)
1503+ {
1504+ if (handlePanickingUnit (*j))
1505+ return false ;
1506+ }
1507+ return true ;
1508+ }
1509+
1510+ /* *
1511+ * Cmmon function for panicking units.
1512+ * @return False when unit not in panicking mode.
1513+ */
1514+ bool BattlescapeState::handlePanickingUnit (BattleUnit *unit)
1515+ {
1516+ UnitStatus status = unit->getStatus ();
1517+ if (status != STATUS_PANICKING && status != STATUS_BERSERK ) return false ;
1518+ unit->setVisible (true );
1519+
1520+ std::wstringstream ss;
1521+ ss << unit->getUnit ()->getName (_game->getLanguage ()) << L' \n ' << _game->getLanguage ()->getString (status==STATUS_PANICKING ?" STR_HAS_PANICKED" :" STR_HAS_GONE_BERSERK" );
1522+ _game->pushState (new InfoboxState (_game, ss.str ()));
1523+
1524+ unit->abortTurn (); // makes the unit go to status STANDING :p
1525+
1526+ int dropweapons = RNG::generate (0 ,100 );
1527+ int flee = RNG::generate (0 ,100 );
1528+ switch (status)
1529+ {
1530+ case STATUS_PANICKING : // 1/2 chance to drop weapons and 1/2 chance try to flee
1531+
1532+ if (dropweapons <= 50 )
1533+ {
1534+ BattleItem *item = unit->getItem (" STR_RIGHT_HAND" );
1535+ if (item)
1536+ {
1537+ dropItem (unit->getPosition (), item);
1538+ item->moveToOwner (0 );
1539+ }
1540+ item = unit->getItem (" STR_LEFT_HAND" );
1541+ if (item)
1542+ {
1543+ dropItem (unit->getPosition (), item);
1544+ item->moveToOwner (0 );
1545+ }
1546+ unit->setCache (0 );
1547+ }
1548+
1549+ if (flee <= 50 )
1550+ {
1551+ _action.actor = unit;
1552+ _action.target = Position (unit->getPosition ().x + RNG::generate (-5 ,5 ), unit->getPosition ().y + RNG::generate (-5 ,5 ), unit->getPosition ().z );
1553+ statePushBack (new UnitWalkBState (this , _action));
1554+ }
1555+ break ;
1556+ case STATUS_BERSERK : // berserk - do some weird turning around and then pick the closest unit to shoot towards
1557+ _action.actor = unit;
1558+ for (int i= 0 ; i < 4 ; i++)
1559+ {
1560+ _action.target = Position (unit->getPosition ().x + RNG::generate (-5 ,5 ), unit->getPosition ().y + RNG::generate (-5 ,5 ), unit->getPosition ().z );
1561+ statePushBack (new UnitTurnBState (this , _action));
1562+ }
1563+ int mindistance = 10000 ;
1564+ for (std::vector<BattleUnit*>::iterator i = _battleGame->getUnits ()->begin (); i != _battleGame->getUnits ()->end (); ++i)
1565+ {
1566+ int x = abs (unit->getPosition ().x - (*i)->getPosition ().x );
1567+ int y = abs (unit->getPosition ().y - (*i)->getPosition ().y );
1568+ int distance = int (floor (sqrt (float (x*x + y*y)) + 0.5 ));
1569+
1570+ if (distance < mindistance && (*i) != unit && !(*i)->isOut ())
1571+ {
1572+ _action.target = (*i)->getPosition ();
1573+ mindistance = distance;
1574+ }
1575+ }
1576+ statePushBack (new UnitTurnBState (this , _action));
1577+ _action.type = BA_SNAPSHOT ;
1578+ _action.weapon = unit->getMainHandWeapon ();
1579+ for (int i= 0 ; i < 6 ; i++)
1580+ {
1581+ statePushBack (new ProjectileFlyBState (this , _action));
1582+ }
1583+ break ;
1584+ }
1585+ unit->setTimeUnits (0 );
1586+ unit->moraleChange (+15 );
1587+
1588+ return true ;
1589+ }
1590+
1591+ /* *
1592+ * TUs are not spent when handling panicking mode or in debug mode.
1593+ * @return Whether TUs are spent or not.
1594+ */
1595+ bool BattlescapeState::dontSpendTUs ()
1596+ {
1597+ if (_battleGame->getDebugMode ())
1598+ return true ;
1599+ if (!_playerPanicHandled)
1600+ return true ;
1601+
1602+ return false ;
14601603}
14611604
14621605}
0 commit comments