diff --git a/Core/GameEngine/Include/Common/GameDefines.h b/Core/GameEngine/Include/Common/GameDefines.h index 1bf9f096fcf..f98ef9e3f21 100644 --- a/Core/GameEngine/Include/Common/GameDefines.h +++ b/Core/GameEngine/Include/Common/GameDefines.h @@ -79,6 +79,10 @@ #define PRESERVE_UNRELIABLE_FIRESTORMS (0) // The fix for this unfavorable behavior was approved by the Game Design Committee. #endif +#ifndef PRESERVE_SNIPING_EMPTY_STINGER_SITES +#define PRESERVE_SNIPING_EMPTY_STINGER_SITES (0) // The fix for this unfavorable behavior was approved by the Game Design Committee. +#endif + #ifndef PRESERVE_RETAIL_SCRIPTED_CAMERA #define PRESERVE_RETAIL_SCRIPTED_CAMERA (1) // Retain scripted camera behavior present in retail Generals 1.08 and Zero Hour 1.04 #endif diff --git a/Generals/Code/GameEngine/Include/GameLogic/Module/SpawnBehavior.h b/Generals/Code/GameEngine/Include/GameLogic/Module/SpawnBehavior.h index 95ec3c8cd7c..9345c4be5e2 100644 --- a/Generals/Code/GameEngine/Include/GameLogic/Module/SpawnBehavior.h +++ b/Generals/Code/GameEngine/Include/GameLogic/Module/SpawnBehavior.h @@ -111,6 +111,7 @@ class SpawnBehaviorInterface virtual CanAttackResult getCanAnySlavesUseWeaponAgainstTarget( AbleToAttackType attackType, const Object *victim, const Coord3D *pos, CommandSourceType cmdSource ) = 0; virtual Bool canAnySlavesAttack() = 0; virtual void orderSlavesToGoIdle( CommandSourceType cmdSource ) = 0; + virtual Int getSlaveCount() const = 0; }; // ------------------------------------------------------------------------------------------------ @@ -160,6 +161,7 @@ class SpawnBehavior : public UpdateModule, virtual CanAttackResult getCanAnySlavesUseWeaponAgainstTarget( AbleToAttackType attackType, const Object *victim, const Coord3D *pos, CommandSourceType cmdSource ) override; virtual Bool canAnySlavesAttack() override; virtual void orderSlavesToGoIdle( CommandSourceType cmdSource ) override; + virtual Int getSlaveCount() const override { return m_spawnCount; } // ********************************************************************************************** // our own methods diff --git a/Generals/Code/GameEngine/Source/GameLogic/Object/Weapon.cpp b/Generals/Code/GameEngine/Source/GameLogic/Object/Weapon.cpp index c9651c4fd1b..060e195a129 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/Object/Weapon.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/Object/Weapon.cpp @@ -74,6 +74,7 @@ #include "GameLogic/Module/AssistedTargetingUpdate.h" #include "GameLogic/Module/ProjectileStreamUpdate.h" #include "GameLogic/Module/PhysicsUpdate.h" +#include "GameLogic/Module/SpawnBehavior.h" #include "GameLogic/TerrainLogic.h" #define RATIONALIZE_ATTACK_RANGE @@ -581,6 +582,27 @@ Real WeaponTemplate::estimateWeaponTemplateDamage( } } +#if !RETAIL_COMPATIBLE_CRC + // hmm.. must be shooting a firebase or such, if there is noone home to take the bullet, return 0! + if ( victimObj->isKindOf( KINDOF_STRUCTURE) && damageType == DAMAGE_SNIPER ) + { +#if PRESERVE_SNIPING_EMPTY_STINGER_SITES + if (victimObj->getContain()) + { + if (victimObj->getContain()->getContainCount() == 0) + return 0.0f; + } +#else + // TheSuperHackers @bugfix Stubbjax 22/06/2026 Only allow targeting Stinger Sites when they contain Soldiers. + const Bool hasOccupants = victimObj->getContain() && victimObj->getContain()->getContainCount() > 0; + const Bool hasSlaves = victimObj->getSpawnBehaviorInterface() && victimObj->getSpawnBehaviorInterface()->getSlaveCount() > 0; + + if (!hasOccupants && !hasSlaves) + return 0.0f; +#endif + } +#endif + // this stays, even if ALLOW_SURRENDER is not defed, since flashbangs still use 'em if ( damageType == DAMAGE_SURRENDER || m_allowAttackGarrisonedBldgs ) { diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/SpawnBehavior.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/SpawnBehavior.h index eb043d35121..4ec859c18a5 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/SpawnBehavior.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/SpawnBehavior.h @@ -122,6 +122,7 @@ class SpawnBehaviorInterface virtual Bool areAllSlavesStealthed() const = 0; virtual void revealSlaves() = 0; virtual Bool doSlavesHaveFreedom() const = 0; + virtual Int getSlaveCount() const = 0; }; // ------------------------------------------------------------------------------------------------ @@ -177,6 +178,7 @@ class SpawnBehavior : public UpdateModule, virtual Bool areAllSlavesStealthed() const override; virtual void revealSlaves() override; virtual Bool doSlavesHaveFreedom() const override { return getSpawnBehaviorModuleData()->m_slavesHaveFreeWill; } + virtual Int getSlaveCount() const override { return m_spawnCount; } // ********************************************************************************************** // our own methods diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Weapon.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Weapon.cpp index deef23cd706..453c8db8764 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Weapon.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Weapon.cpp @@ -74,6 +74,7 @@ #include "GameLogic/Module/AssistedTargetingUpdate.h" #include "GameLogic/Module/ProjectileStreamUpdate.h" #include "GameLogic/Module/PhysicsUpdate.h" +#include "GameLogic/Module/SpawnBehavior.h" #include "GameLogic/TerrainLogic.h" #define RATIONALIZE_ATTACK_RANGE @@ -599,11 +600,21 @@ Real WeaponTemplate::estimateWeaponTemplateDamage( // hmm.. must be shooting a firebase or such, if there is noone home to take the bullet, return 0! if ( victimObj->isKindOf( KINDOF_STRUCTURE) && damageType == DAMAGE_SNIPER ) { + +#if RETAIL_COMPATIBLE_CRC || PRESERVE_SNIPING_EMPTY_STINGER_SITES if ( victimObj->getContain() ) { if ( victimObj->getContain()->getContainCount() == 0 ) return 0.0f; } +#else + // TheSuperHackers @bugfix Stubbjax 22/06/2026 Only allow targeting Stinger Sites when they contain Soldiers. + const Bool hasOccupants = victimObj->getContain() && victimObj->getContain()->getContainCount() > 0; + const Bool hasSlaves = victimObj->getSpawnBehaviorInterface() && victimObj->getSpawnBehaviorInterface()->getSlaveCount() > 0; + + if (!hasOccupants && !hasSlaves) + return 0.0f; +#endif }