Skip to content

Commit 5e041b4

Browse files
fix(actionmanager): Improve validation of Sneak Attack placement legality (#2533)
1 parent d31c6c5 commit 5e041b4

5 files changed

Lines changed: 40 additions & 12 deletions

File tree

Generals/Code/GameEngine/Include/Common/BuildAssistant.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,14 +148,14 @@ class BuildAssistant : public SubsystemInterface
148148
const ThingTemplate *build,
149149
Real angle, // angle to construct 'build' at
150150
UnsignedInt options, // use LocationLegalToBuildOptions
151-
Object *builderObject,
151+
const Object *builderObject,
152152
Player *player);
153153

154154
/// query if we can build at this location
155155
virtual Bool isLocationClearOfObjects( const Coord3D *worldPos,
156156
const ThingTemplate *build,
157157
Real angle, // angle to construct 'build' a
158-
Object *builderObject,
158+
const Object *builderObject,
159159
UnsignedInt options,
160160
Player *thePlayer);
161161

Generals/Code/GameEngine/Source/Common/System/BuildAssistant.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -656,7 +656,7 @@ void BuildAssistant::iterateFootprint( const ThingTemplate *build,
656656
Bool BuildAssistant::isLocationClearOfObjects( const Coord3D *worldPos,
657657
const ThingTemplate *build,
658658
Real angle,
659-
Object *builderObject,
659+
const Object *builderObject,
660660
UnsignedInt options,
661661
Player *thePlayer)
662662
{
@@ -842,7 +842,7 @@ LegalBuildCode BuildAssistant::isLocationLegalToBuild( const Coord3D *worldPos,
842842
const ThingTemplate *build,
843843
Real angle,
844844
UnsignedInt options,
845-
Object *builderObject,
845+
const Object *builderObject,
846846
Player *player)
847847
{
848848

@@ -928,7 +928,7 @@ LegalBuildCode BuildAssistant::isLocationLegalToBuild( const Coord3D *worldPos,
928928
// if clear path is requested check to see if the builder object can get there
929929
if( BitIsSet( options, CLEAR_PATH ) && builderObject )
930930
{
931-
AIUpdateInterface *ai = builderObject->getAIUpdateInterface();
931+
const AIUpdateInterface *ai = builderObject->getAIUpdateInterface();
932932

933933
//
934934
// if there is no AI interface for this object, it cannot possible pass a clear path

GeneralsMD/Code/GameEngine/Include/Common/BuildAssistant.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,14 +151,14 @@ class BuildAssistant : public SubsystemInterface
151151
const ThingTemplate *build,
152152
Real angle, // angle to construct 'build' at
153153
UnsignedInt options, // use LocationLegalToBuildOptions
154-
Object *builderObject,
154+
const Object *builderObject,
155155
Player *player);
156156

157157
/// query if we can build at this location
158158
virtual LegalBuildCode isLocationClearOfObjects( const Coord3D *worldPos,
159159
const ThingTemplate *build,
160160
Real angle, // angle to construct 'build' a
161-
Object *builderObject,
161+
const Object *builderObject,
162162
UnsignedInt options,
163163
Player *thePlayer);
164164

GeneralsMD/Code/GameEngine/Source/Common/RTS/ActionManager.cpp

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include "PreRTS.h" // This must go first in EVERY cpp file in the GameEngine
3737

3838
#include "Common/ActionManager.h"
39+
#include "Common/BuildAssistant.h"
3940
#include "Common/GlobalData.h"
4041
#include "Common/Player.h"
4142
#include "Common/PlayerList.h"
@@ -1506,7 +1507,7 @@ Bool ActionManager::canDoSpecialPowerAtLocation( const Object *obj, const Coord3
15061507
}
15071508
}
15081509

1509-
// Last check is shroudedness, if it is cared about
1510+
// Second check is shroudedness, if it is cared about
15101511
switch( spTemplate->getSpecialPowerType() )
15111512
{
15121513
case SPECIAL_DAISY_CUTTER:
@@ -1552,7 +1553,6 @@ Bool ActionManager::canDoSpecialPowerAtLocation( const Object *obj, const Coord3
15521553
case SUPW_SPECIAL_PARTICLE_UPLINK_CANNON:
15531554
case LAZR_SPECIAL_PARTICLE_UPLINK_CANNON:
15541555
case SPECIAL_CLEANUP_AREA:
1555-
case SPECIAL_SNEAK_ATTACK:
15561556
case SPECIAL_BATTLESHIP_BOMBARDMENT:
15571557
//Don't allow "damaging" special powers in shrouded areas, but Fogged are okay.
15581558
return ThePartitionManager->getShroudStatusForPlayer( obj->getControllingPlayer()->getPlayerIndex(), loc ) != CELLSHROUD_SHROUDED;
@@ -1586,6 +1586,34 @@ Bool ActionManager::canDoSpecialPowerAtLocation( const Object *obj, const Coord3
15861586
case SPECIAL_CHANGE_BATTLE_PLANS:
15871587
return false;
15881588
}
1589+
1590+
// TheSuperHackers @fix stephanmeesters 04/04/2026 Some special powers can spawn a building.
1591+
// To avoid cheating, verify that it is legal to place this building.
1592+
switch( spTemplate->getSpecialPowerType() )
1593+
{
1594+
case SPECIAL_SNEAK_ATTACK:
1595+
{
1596+
#if RETAIL_COMPATIBLE_CRC
1597+
return ThePartitionManager->getShroudStatusForPlayer( obj->getControllingPlayer()->getPlayerIndex(), loc ) != CELLSHROUD_SHROUDED;
1598+
#else
1599+
const ThingTemplate* referenceThing = mod->getReferenceThingTemplate();
1600+
if (!referenceThing)
1601+
return FALSE;
1602+
1603+
const Real angle = referenceThing->getPlacementViewAngle();
1604+
1605+
return TheBuildAssistant->isLocationLegalToBuild(
1606+
loc, referenceThing, angle,
1607+
BuildAssistant::USE_QUICK_PATHFIND |
1608+
BuildAssistant::TERRAIN_RESTRICTIONS |
1609+
BuildAssistant::CLEAR_PATH |
1610+
BuildAssistant::NO_OBJECT_OVERLAP |
1611+
BuildAssistant::SHROUD_REVEALED |
1612+
BuildAssistant::IGNORE_STEALTHED,
1613+
obj, nullptr) == LBC_OK;
1614+
#endif
1615+
}
1616+
}
15891617
}
15901618
return false;
15911619
}

GeneralsMD/Code/GameEngine/Source/Common/System/BuildAssistant.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -656,7 +656,7 @@ void BuildAssistant::iterateFootprint( const ThingTemplate *build,
656656
LegalBuildCode BuildAssistant::isLocationClearOfObjects( const Coord3D *worldPos,
657657
const ThingTemplate *build,
658658
Real angle,
659-
Object *builderObject,
659+
const Object *builderObject,
660660
UnsignedInt options,
661661
Player *thePlayer)
662662
{
@@ -915,7 +915,7 @@ LegalBuildCode BuildAssistant::isLocationLegalToBuild( const Coord3D *worldPos,
915915
const ThingTemplate *build,
916916
Real angle,
917917
UnsignedInt options,
918-
Object *builderObject,
918+
const Object *builderObject,
919919
Player *player)
920920
{
921921

@@ -1002,7 +1002,7 @@ LegalBuildCode BuildAssistant::isLocationLegalToBuild( const Coord3D *worldPos,
10021002
// if clear path is requested check to see if the builder object can get there (unless it's a structure)
10031003
if( BitIsSet( options, CLEAR_PATH ) && builderObject && !builderObject->isKindOf( KINDOF_IMMOBILE ) )
10041004
{
1005-
AIUpdateInterface *ai = builderObject->getAIUpdateInterface();
1005+
const AIUpdateInterface *ai = builderObject->getAIUpdateInterface();
10061006

10071007
//
10081008
// if there is no AI interface for this object, it cannot possible pass a clear path

0 commit comments

Comments
 (0)