Skip to content

Commit

Permalink
Experimental: weapon safety switch (for reactions)
Browse files Browse the repository at this point in the history
  • Loading branch information
MeridianOXC committed Feb 9, 2024
1 parent 1b18f38 commit fb5360a
Show file tree
Hide file tree
Showing 6 changed files with 126 additions and 21 deletions.
28 changes: 23 additions & 5 deletions src/Battlescape/BattlescapeState.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1497,7 +1497,8 @@ void BattlescapeState::btnLeftHandItemClick(Action *action)
bool rightClick = _game->isRightClick(action, true);
if (rightClick)
{
_save->getSelectedUnit()->toggleLeftHandForReactions();
bool isCtrl = _game->isCtrlPressed(true);
_save->getSelectedUnit()->toggleLeftHandForReactions(isCtrl);
return;
}

Expand Down Expand Up @@ -1545,7 +1546,8 @@ void BattlescapeState::btnRightHandItemClick(Action *action)
bool rightClick = _game->isRightClick(action, true);
if (rightClick)
{
_save->getSelectedUnit()->toggleRightHandForReactions();
bool isCtrl = _game->isCtrlPressed(true);
_save->getSelectedUnit()->toggleRightHandForReactions(isCtrl);
return;
}

Expand Down Expand Up @@ -1885,7 +1887,7 @@ bool BattlescapeState::playableUnitSelected()
/**
* Draw hand item with ammo number.
*/
void BattlescapeState::drawItem(BattleItem* item, Surface* hand, std::vector<NumberText*> &ammoText, std::vector<NumberText*> &medikitText, NumberText *twoHandedText, bool drawReactionIndicator)
void BattlescapeState::drawItem(BattleItem* item, Surface* hand, std::vector<NumberText*> &ammoText, std::vector<NumberText*> &medikitText, NumberText *twoHandedText, bool drawReactionIndicator, bool drawNoReactionIndicator)
{
hand->clear();
for (int slot = 0; slot < RuleItem::AmmoSlotMax; ++slot)
Expand Down Expand Up @@ -1962,6 +1964,18 @@ void BattlescapeState::drawItem(BattleItem* item, Surface* hand, std::vector<Num
tempSurface->blitNShade(hand, 28, 0);
}
}
if (drawNoReactionIndicator)
{
if (Surface* noReactionIndicator = _game->getMod()->getSurface("noReactionIndicator", false))
{
noReactionIndicator->blitNShade(hand, 0, 0);
}
else
{
Surface* tempSurface = _game->getMod()->getSurfaceSet("SCANG.DAT")->getFrame(6); // red dot
tempSurface->blitNShade(hand, 28, 0);
}
}
}

/**
Expand All @@ -1972,12 +1986,16 @@ void BattlescapeState::drawHandsItems()
BattleUnit *battleUnit = _battleGame->playableUnitSelected() ? _save->getSelectedUnit() : nullptr;
bool left = false;
bool right = false;
bool left2 = false;
bool right2 = false;
BattleItem* leftHandItem = nullptr;
BattleItem* rightHandItem = nullptr;
if (battleUnit)
{
left = battleUnit->isLeftHandPreferredForReactions();
right = battleUnit->isRightHandPreferredForReactions();
left2 = battleUnit->isLeftHandDisabledForReactions();
right2 = battleUnit->isRightHandDisabledForReactions();
leftHandItem = battleUnit->getLeftHandWeapon();
rightHandItem = battleUnit->getRightHandWeapon();
if (!leftHandItem || !rightHandItem)
Expand All @@ -1996,8 +2014,8 @@ void BattlescapeState::drawHandsItems()
}
}
}
drawItem(leftHandItem, _btnLeftHandItem, _numAmmoLeft, _numMedikitLeft, _numTwoHandedIndicatorLeft, left);
drawItem(rightHandItem, _btnRightHandItem, _numAmmoRight, _numMedikitRight, _numTwoHandedIndicatorRight, right);
drawItem(leftHandItem, _btnLeftHandItem, _numAmmoLeft, _numMedikitLeft, _numTwoHandedIndicatorLeft, left, left2);
drawItem(rightHandItem, _btnRightHandItem, _numAmmoRight, _numMedikitRight, _numTwoHandedIndicatorRight, right, right2);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/Battlescape/BattlescapeState.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ class BattlescapeState : public State
/// Shifts the red colors of the visible unit buttons backgrounds.
void blinkVisibleUnitButtons();
/// Draw hand item with ammo number.
void drawItem(BattleItem *item, Surface *hand, std::vector<NumberText*> &ammoText, std::vector<NumberText*> &medikitText, NumberText *twoHandedText, bool drawReactionIndicator);
void drawItem(BattleItem *item, Surface *hand, std::vector<NumberText*> &ammoText, std::vector<NumberText*> &medikitText, NumberText *twoHandedText, bool drawReactionIndicator, bool drawNoReactionIndicator);
/// Draw both hands sprites.
void drawHandsItems();
/// Shifts the colors of the health bar when unit has fatal wounds.
Expand Down
33 changes: 32 additions & 1 deletion src/Battlescape/TileEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2724,7 +2724,7 @@ TileEngine::ReactionScore TileEngine::determineReactionType(BattleUnit *unit, Ba
reactionWeapons.push_back(meleeWeapon);
}
// 3. then the rest (AI: quickest weapon, Player: last selected/main weapon)
if (BattleItem* otherWeapon = unit->getMainHandWeapon(!isPlayer))
if (BattleItem* otherWeapon = unit->getMainHandWeapon(!isPlayer, true))
{
reactionWeapons.push_back(otherWeapon);
}
Expand All @@ -2736,8 +2736,39 @@ TileEngine::ReactionScore TileEngine::determineReactionType(BattleUnit *unit, Ba
tempDirection = getDirectionTo(unit->getPosition(), target->getPosition());
}

BattleItem* disabledLeft = nullptr;
BattleItem* disabledRight = nullptr;
// player units only... to prevent abuse
if (isPlayer)
{
BattleItem* leftHandItem = unit->getLeftHandWeapon();
BattleItem* rightHandItem = unit->getRightHandWeapon();
BattleItem* emptyHandItem = nullptr;
if ((!leftHandItem && unit->isLeftHandDisabledForReactions()) || (!rightHandItem && unit->isRightHandDisabledForReactions()))
{
auto typesToCheck = { BT_MELEE, BT_PSIAMP, BT_FIREARM/*, BT_MEDIKIT, BT_SCANNER, BT_MINDPROBE*/ };
for (auto& type : typesToCheck)
{
emptyHandItem = unit->getSpecialWeapon(type);
if (emptyHandItem && emptyHandItem->getRules()->isSpecialUsingEmptyHand())
{
break;
}
emptyHandItem = nullptr;
}
}
disabledLeft = unit->isLeftHandDisabledForReactions() ? (leftHandItem ? leftHandItem : emptyHandItem) : nullptr;
disabledRight = unit->isRightHandDisabledForReactions() ? (rightHandItem ? rightHandItem : emptyHandItem) : nullptr;
}

for (auto* weapon : reactionWeapons)
{
if (weapon == disabledLeft || weapon == disabledRight)
{
// the player doesn't want to react with this weapon
continue;
}

if (_save->canUseWeapon(weapon, unit, false, BA_HIT))
{
// has a weapon capable of melee and is in melee range
Expand Down
69 changes: 59 additions & 10 deletions src/Savegame/BattleUnit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ BattleUnit::BattleUnit(const Mod *mod, Soldier *soldier, int depth, const RuleSt
_verticalDirection(0), _status(STATUS_STANDING), _wantsToSurrender(false), _isSurrendering(false), _walkPhase(0), _fallPhase(0), _kneeled(false), _floating(false),
_dontReselect(false), _fire(0), _currentAIState(0), _visible(false),
_exp{ }, _expTmp{ },
_motionPoints(0), _scannedTurn(-1), _kills(0), _hitByFire(false), _hitByAnything(false), _alreadyExploded(false), _fireMaxHit(0), _smokeMaxHit(0), _moraleRestored(0), _charging(0), _turnsSinceSpotted(255), _turnsLeftSpottedForSnipers(0),
_motionPoints(0), _scannedTurn(-1), _kills(0), _hitByFire(false), _hitByAnything(false), _alreadyExploded(false), _fireMaxHit(0), _smokeMaxHit(0),
_moraleRestored(0), _charging(0), _turnsSinceSpotted(255), _turnsLeftSpottedForSnipers(0),
_statistics(), _murdererId(0), _mindControllerID(0), _fatalShotSide(SIDE_FRONT), _fatalShotBodyPart(BODYPART_HEAD), _armor(0),
_geoscapeSoldier(soldier), _unitRules(0), _rankInt(0), _turretType(-1), _hidingForTurn(false), _floorAbove(false), _respawn(false), _alreadyRespawned(false),
_isLeeroyJenkins(false), _summonedPlayerUnit(false), _resummonedFakeCivilian(false), _pickUpWeaponsMoreActively(false), _disableIndicators(false),
Expand Down Expand Up @@ -641,6 +642,8 @@ void BattleUnit::load(const YAML::Node &node, const Mod *mod, const ScriptGlobal
_alreadyRespawned = node["alreadyRespawned"].as<bool>(_alreadyRespawned);
_activeHand = node["activeHand"].as<std::string>(_activeHand);
_preferredHandForReactions = node["preferredHandForReactions"].as<std::string>(_preferredHandForReactions);
_reactionsDisabledForLeftHand = node["reactionsDisabledForLeftHand"].as<bool>(_reactionsDisabledForLeftHand);
_reactionsDisabledForRightHand = node["reactionsDisabledForRightHand"].as<bool>(_reactionsDisabledForRightHand);
if (node["tempUnitStatistics"])
{
_statistics->load(node["tempUnitStatistics"]);
Expand Down Expand Up @@ -758,6 +761,10 @@ YAML::Node BattleUnit::save(const ScriptGlobal *shared) const
node["activeHand"] = _activeHand;
if (!_preferredHandForReactions.empty())
node["preferredHandForReactions"] = _preferredHandForReactions;
if (_reactionsDisabledForLeftHand)
node["reactionsDisabledForLeftHand"] = _reactionsDisabledForLeftHand;
if (_reactionsDisabledForRightHand)
node["reactionsDisabledForRightHand"] = _reactionsDisabledForRightHand;
node["tempUnitStatistics"] = _statistics->save();
if (_murdererId)
node["murdererId"] = _murdererId;
Expand Down Expand Up @@ -3371,7 +3378,7 @@ BattleItem *BattleUnit::getItem(RuleInventory *slot, int x, int y) const
* @param quickest Whether to get the quickest weapon, default true
* @return Pointer to item.
*/
BattleItem *BattleUnit::getMainHandWeapon(bool quickest) const
BattleItem *BattleUnit::getMainHandWeapon(bool quickest, bool reactions) const
{
BattleItem *weaponRightHand = getRightHandWeapon();
BattleItem *weaponLeftHand = getLeftHandWeapon();
Expand All @@ -3382,6 +3389,16 @@ BattleItem *BattleUnit::getMainHandWeapon(bool quickest) const
if (!weaponLeftHand || !weaponLeftHand->haveAnyAmmo())
weaponLeftHand = 0;

// ignore disabled hands/weapons (player units only... to prevent abuse)
// Note: there is another check later, but this one is still needed, so that also non-main weapons get a chance to be used in case the main weapon is disabled
if (reactions && _faction == FACTION_PLAYER)
{
if (_reactionsDisabledForRightHand)
weaponRightHand = nullptr;
if (_reactionsDisabledForLeftHand)
weaponLeftHand = nullptr;
}

// if there is only one weapon, it's easy:
if (weaponRightHand && !weaponLeftHand)
return weaponRightHand;
Expand Down Expand Up @@ -3599,23 +3616,55 @@ bool BattleUnit::reloadAmmo()
/**
* Toggle the right hand as main hand for reactions.
*/
void BattleUnit::toggleRightHandForReactions()
void BattleUnit::toggleRightHandForReactions(bool isCtrl)
{
if (isRightHandPreferredForReactions())
_preferredHandForReactions = "";
if (isCtrl)
{
if (isRightHandPreferredForReactions())
{
_preferredHandForReactions = "";
}
_reactionsDisabledForRightHand = !_reactionsDisabledForRightHand;
}
else
_preferredHandForReactions = "STR_RIGHT_HAND";
{
if (isRightHandPreferredForReactions())
{
_preferredHandForReactions = "";
}
else
{
_preferredHandForReactions = "STR_RIGHT_HAND";
}
_reactionsDisabledForRightHand = false;
}
}

/**
* Toggle the left hand as main hand for reactions.
*/
void BattleUnit::toggleLeftHandForReactions()
void BattleUnit::toggleLeftHandForReactions(bool isCtrl)
{
if (isLeftHandPreferredForReactions())
_preferredHandForReactions = "";
if (isCtrl)
{
if (isLeftHandPreferredForReactions())
{
_preferredHandForReactions = "";
}
_reactionsDisabledForLeftHand = !_reactionsDisabledForLeftHand;
}
else
_preferredHandForReactions = "STR_LEFT_HAND";
{
if (isLeftHandPreferredForReactions())
{
_preferredHandForReactions = "";
}
else
{
_preferredHandForReactions = "STR_LEFT_HAND";
}
_reactionsDisabledForLeftHand = false;
}
}

/**
Expand Down
13 changes: 10 additions & 3 deletions src/Savegame/BattleUnit.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ class BattleUnit
const Unit *_spawnUnit = nullptr;
std::string _activeHand;
std::string _preferredHandForReactions;
bool _reactionsDisabledForLeftHand = false;
bool _reactionsDisabledForRightHand = false;
BattleUnitStatistics* _statistics;
int _murdererId; // used to credit the murderer with the kills that this unit got by blowing up on death
int _mindControllerID; // used to credit the mind controller with the kills of the mind controllee
Expand Down Expand Up @@ -431,7 +433,7 @@ class BattleUnit
/// Gets the item in the specified slot.
BattleItem *getItem(RuleInventory *slot, int x = 0, int y = 0) const;
/// Gets the item in the main hand.
BattleItem *getMainHandWeapon(bool quickest = true) const;
BattleItem *getMainHandWeapon(bool quickest = true, bool reactions = false) const;
/// Gets a grenade from the belt, if any.
BattleItem *getGrenadeFromBelt() const;
/// Gets the item from right hand.
Expand All @@ -448,16 +450,21 @@ class BattleUnit
bool reloadAmmo();

/// Toggle the right hand as main hand for reactions.
void toggleRightHandForReactions();
void toggleRightHandForReactions(bool isCtrl);
/// Toggle the left hand as main hand for reactions.
void toggleLeftHandForReactions();
void toggleLeftHandForReactions(bool isCtrl);
/// Is right hand preferred for reactions?
bool isRightHandPreferredForReactions() const;
/// Is left hand preferred for reactions?
bool isLeftHandPreferredForReactions() const;
/// Get preferred weapon for reactions, if applicable.
BattleItem *getWeaponForReactions() const;

/// Is right hand disabled for reactions?
bool isRightHandDisabledForReactions() const { return _reactionsDisabledForRightHand; }
/// Is left hand disabled for reactions?
bool isLeftHandDisabledForReactions() const { return _reactionsDisabledForLeftHand; }

/// Check if this unit is in the exit area
bool isInExitArea(SpecialTileType stt) const;
bool liesInExitArea(Tile *tile, SpecialTileType stt) const;
Expand Down
2 changes: 1 addition & 1 deletion src/version.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,5 @@
#define OPENXCOM_VERSION_NUMBER 7,11,4,0

#ifndef OPENXCOM_VERSION_GIT
#define OPENXCOM_VERSION_GIT " (v2024-02-05)"
#define OPENXCOM_VERSION_GIT " (v2024-02-09)"
#endif

0 comments on commit fb5360a

Please sign in to comment.