From aaf8cf67f0cbfbcd33e1b4c1b61aa4346f36df03 Mon Sep 17 00:00:00 2001 From: warboy1982 Date: Thu, 9 Nov 2017 01:04:29 +1100 Subject: [PATCH] amend reaction fire behaviour allow xcom soldiers to spin and shoot at aggressors --- src/Battlescape/BattlescapeGame.cpp | 1 + src/Battlescape/TileEngine.cpp | 19 +++++++++++++++- src/Savegame/BattleUnit.cpp | 34 ++++++++++++++++++++++++++--- src/Savegame/BattleUnit.h | 10 +++++++-- src/Savegame/SavedBattleGame.cpp | 11 ++++++++++ src/Savegame/SavedBattleGame.h | 3 +++ 6 files changed, 72 insertions(+), 6 deletions(-) diff --git a/src/Battlescape/BattlescapeGame.cpp b/src/Battlescape/BattlescapeGame.cpp index 9b4cab9d3f..ff90c3a82f 100644 --- a/src/Battlescape/BattlescapeGame.cpp +++ b/src/Battlescape/BattlescapeGame.cpp @@ -105,6 +105,7 @@ void BattlescapeGame::think() // it's a non player side (ALIENS or CIVILIANS) if (_save->getSide() != FACTION_PLAYER) { + _save->resetUnitHitStates(); if (!_debugPlay) { if (_save->getSelectedUnit()) diff --git a/src/Battlescape/TileEngine.cpp b/src/Battlescape/TileEngine.cpp index dd8795dfa3..dcadf0a81f 100644 --- a/src/Battlescape/TileEngine.cpp +++ b/src/Battlescape/TileEngine.cpp @@ -857,7 +857,24 @@ std::vector > TileEngine::getSpottingUnits(BattleUn Position originVoxel = getOriginVoxel(falseAction, 0); Position targetVoxel; AIModule *ai = (*i)->getAIModule(); - bool gotHit = (ai != 0 && ai->getWasHitBy(unit->getId())); + + // Inquisitor's note regarding 'gotHit' variable + // in vanilla, the 'hitState' flag is the only part of this equation that comes into play. + // any time a unit takes damage, this flag is set, then it would be reset by a call to + // a function analogous to SavedBattleGame::resetUnitHitStates(), any time: + // 1: a unit was selected by being clicked on. + // 2: either "next unit" button was pressed. + // 3: the inventory screen was accessed. (i didn't look too far into this one, it's possible it's only called in the pre-mission equip screen) + // 4: the same place where we call it, immediately before every move the AI makes. + // this flag is responsible for units turning around to respond to hits, and is in keeping with the details listed on http://www.ufopaedia.org/index.php/Reaction_fire_triggers + // we've gone for a slightly different implementation: AI units keep a list of which units have hit them and don't forget until the end of the player's turn. + // this method is in keeping with the spirit of the original feature, but much less exploitable by players. + // the hitState flag in our implementation allows player units to turn and react as they did in the original, (which is far less cumbersome than giving them all an AI module) + // we don't extend the same "enhanced aggressor memory" courtesy to players, because in the original, they could only turn and react to damage immediately after it happened. + // this is because as much as we want the player's soldiers dead, we don't want them to feel like we're being unfair about it. + + bool gotHit = (ai != 0 && ai->getWasHitBy(unit->getId())) || (ai == 0 && (*i)->getHitState()); + // can actually see the target Tile, or we got hit if (((*i)->checkViewSector(unit->getPosition()) || gotHit) && // can actually target the unit diff --git a/src/Savegame/BattleUnit.cpp b/src/Savegame/BattleUnit.cpp index 459e988fa8..7235904b90 100644 --- a/src/Savegame/BattleUnit.cpp +++ b/src/Savegame/BattleUnit.cpp @@ -52,7 +52,7 @@ BattleUnit::BattleUnit(Soldier *soldier, int depth) : _verticalDirection(0), _status(STATUS_STANDING), _walkPhase(0), _fallPhase(0), _kneeled(false), _floating(false), _dontReselect(false), _fire(0), _currentAIState(0), _visible(false), _cacheInvalid(true), _expBravery(0), _expReactions(0), _expFiring(0), _expThrowing(0), _expPsiSkill(0), _expPsiStrength(0), _expMelee(0), - _motionPoints(0), _kills(0), _hitByFire(false), _moraleRestored(0), _coverReserve(0), _charging(0), _turnsSinceSpotted(255), + _motionPoints(0), _kills(0), _hitByFire(false), _hitByAnything(false), _moraleRestored(0), _coverReserve(0), _charging(0), _turnsSinceSpotted(255), _statistics(), _murdererId(0), _mindControllerID(0), _fatalShotSide(SIDE_FRONT), _fatalShotBodyPart(BODYPART_HEAD), _geoscapeSoldier(soldier), _unitRules(0), _rankInt(0), _turretType(-1), _hidingForTurn(false), _respawn(false) { @@ -164,7 +164,7 @@ BattleUnit::BattleUnit(Unit *unit, UnitFaction faction, int id, Armor *armor, St _toDirectionTurret(0), _verticalDirection(0), _status(STATUS_STANDING), _walkPhase(0), _fallPhase(0), _kneeled(false), _floating(false), _dontReselect(false), _fire(0), _currentAIState(0), _visible(false), _cacheInvalid(true), _expBravery(0), _expReactions(0), _expFiring(0), - _expThrowing(0), _expPsiSkill(0), _expPsiStrength(0), _expMelee(0), _motionPoints(0), _kills(0), _hitByFire(false), + _expThrowing(0), _expPsiSkill(0), _expPsiStrength(0), _expMelee(0), _motionPoints(0), _kills(0), _hitByFire(false), _hitByAnything(false), _moraleRestored(0), _coverReserve(0), _charging(0), _turnsSinceSpotted(255), _statistics(), _murdererId(0), _mindControllerID(0), _fatalShotSide(SIDE_FRONT), _fatalShotBodyPart(BODYPART_HEAD), _armor(armor), _geoscapeSoldier(0), _unitRules(unit), @@ -1079,7 +1079,7 @@ int BattleUnit::damage(Position relative, int power, ItemDamageType type, bool i { UnitSide side = SIDE_FRONT; UnitBodyPart bodypart = BODYPART_TORSO; - + _hitByAnything = true; if (power <= 0) { return 0; @@ -1639,6 +1639,11 @@ void BattleUnit::prepareNewTurn(bool fullProcess) if (_faction != _originalFaction) { _faction = _originalFaction; + if (_faction == FACTION_PLAYER && _currentAIState) + { + delete _currentAIState; + _currentAIState = 0; + } } else { @@ -3242,13 +3247,36 @@ int BattleUnit::getMindControllerId() const return _mindControllerID; } +/** + * Get the unit's total firing xp for this mission. + */ int BattleUnit::getFiringXP() const { return _expFiring; } + +/** + * Artificially alter a unit's firing xp. (used for shotguns) + */ void BattleUnit::nerfFiringXP(int newXP) { _expFiring = newXP; } +/** + * Was this unit just hit? + */ +bool BattleUnit::getHitState() +{ + return _hitByAnything; +} + +/** + * reset the unit hit state. + */ +void BattleUnit::resetHitState() +{ + _hitByAnything = false; +} + } diff --git a/src/Savegame/BattleUnit.h b/src/Savegame/BattleUnit.h index 667e4d3cf5..8fd4ea68fd 100644 --- a/src/Savegame/BattleUnit.h +++ b/src/Savegame/BattleUnit.h @@ -91,7 +91,7 @@ class BattleUnit int _motionPoints; int _kills; int _faceDirection; // used only during strafeing moves - bool _hitByFire; + bool _hitByFire, _hitByAnything; int _moraleRestored; int _coverReserve; BattleUnit *_charging; @@ -494,9 +494,15 @@ class BattleUnit void setMindControllerId(int id); /// Get the unit mind controller's id. int getMindControllerId() const; + /// Get the unit's total firing xp for this mission. int getFiringXP() const; + /// Artificially alter a unit's firing xp. (used for shotguns) void nerfFiringXP(int newXP); - + /// Was this unit just hit? + bool getHitState(); + /// reset the unit hit state. + void resetHitState(); + }; } diff --git a/src/Savegame/SavedBattleGame.cpp b/src/Savegame/SavedBattleGame.cpp index bf377de116..1c2be87367 100644 --- a/src/Savegame/SavedBattleGame.cpp +++ b/src/Savegame/SavedBattleGame.cpp @@ -2059,4 +2059,15 @@ bool SavedBattleGame::isItemUsable(RuleItem *item) const return true; } +/** + * Resets all unit hit state flags. + */ +void SavedBattleGame::resetUnitHitStates() +{ + for (std::vector::iterator i = _units.begin(); i != _units.end(); ++i) + { + (*i)->resetHitState(); + } +} + } diff --git a/src/Savegame/SavedBattleGame.h b/src/Savegame/SavedBattleGame.h index a11150959a..428f0cb495 100644 --- a/src/Savegame/SavedBattleGame.h +++ b/src/Savegame/SavedBattleGame.h @@ -289,9 +289,12 @@ class SavedBattleGame void setChronoTrigger(ChronoTrigger trigger); /// Sets the turn to start the aliens cheating. void setCheatTurn(int turn); + /// Check whether the battle has actually commenced or not. bool isBeforeGame() const; /// Checks if an item is usable on this map. bool isItemUsable(RuleItem *item) const; + /// Reset all the unit hit state flags. + void resetUnitHitStates(); }; }