Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Generals/Code/GameEngine/Include/Common/TunnelTracker.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class TunnelTracker : public MemoryPoolObject,

Bool isValidContainerFor(const Object* obj, Bool checkCapacity) const;
void addToContainList( Object *obj ); ///< add 'obj' to contain list
void removeFromContain( Object *obj, Bool exposeStealthUnits = FALSE ); ///< remove 'obj' from contain list
Bool removeFromContain( Object *obj, Bool exposeStealthUnits = FALSE ); ///< remove 'obj' from contain list
Bool isInContainer( Object *obj ); ///< Is this thing inside?

void onTunnelCreated( const Object *newTunnel ); ///< A tunnel was made
Expand Down
3 changes: 3 additions & 0 deletions Generals/Code/GameEngine/Include/GameLogic/Object.h
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,9 @@ class Object : public Thing, public Snapshot
void onRemovedFrom( Object *removedFrom );
Int getTransportSlotCount() const;
void friend_setContainedBy( Object *containedBy );
#if RTS_GENERALS && RETAIL_COMPATIBLE_CRC
void friend_removeFromTunnelContain();
#endif
const Object* getEnclosingContainedBy() const; ///< Find the first enclosing container in the containment chain.
const Object* getOuterObject() const; ///< Get the top-level object

Expand Down
5 changes: 4 additions & 1 deletion Generals/Code/GameEngine/Source/Common/RTS/TunnelTracker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ void TunnelTracker::addToContainList( Object *obj )
}

// ------------------------------------------------------------------------
void TunnelTracker::removeFromContain( Object *obj, Bool exposeStealthUnits )
Bool TunnelTracker::removeFromContain( Object *obj, Bool exposeStealthUnits )
{

ContainedItemsList::iterator it = std::find(m_containList.begin(), m_containList.end(), obj);
Expand All @@ -212,8 +212,11 @@ void TunnelTracker::removeFromContain( Object *obj, Bool exposeStealthUnits )
DEBUG_ASSERTCRASH(m_heroUnitsContained > 0, ("TunnelTracker::removeFromContain - Removing hero but hero count is %d", m_heroUnitsContained));
--m_heroUnitsContained;
}

return true;
}

return false;
}

// ------------------------------------------------------------------------
Expand Down
16 changes: 16 additions & 0 deletions Generals/Code/GameEngine/Source/GameLogic/AI/AIStates.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4826,6 +4826,22 @@ StateReturnType AIAttackAimAtTargetState::onEnter()
if( containedBy && weapon )
{
ContainModuleInterface *contain = containedBy->getContain();

#if RTS_GENERALS && RETAIL_COMPATIBLE_CRC
// TheSuperHackers @bugfix Caball009 22/09/2026 Return early to prevent a crash.
// This is needed when a Tunnel Network with units is transferred to another player and then sold or destroyed.
// The units are not properly transferred and are left in an invalid state.
if (!contain)
{
source->friend_removeFromTunnelContain();

// destroy this object, because it serves no purpose in its invalid state
TheGameLogic->destroyObject(source);

return STATE_FAILURE;
}
#endif

if (victim)
{
inFiringRange = contain->attemptBestFirePointPosition( source, weapon, victim );
Expand Down
19 changes: 19 additions & 0 deletions Generals/Code/GameEngine/Source/GameLogic/Object/Object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@
#include "Common/Team.h"
#include "Common/ThingFactory.h"
#include "Common/ThingTemplate.h"
#if RTS_GENERALS && RETAIL_COMPATIBLE_CRC
#include "Common/TunnelTracker.h"
#endif
#include "Common/Upgrade.h"
#include "Common/WellKnownKeys.h"
#include "Common/Xfer.h"
Expand Down Expand Up @@ -636,6 +639,22 @@ void Object::onRemovedFrom( Object *removedFrom )
m_containedByFrame = 0;
}

#if RTS_GENERALS && RETAIL_COMPATIBLE_CRC
void Object::friend_removeFromTunnelContain()
{
for (Int i = 0; i < ThePlayerList->getPlayerCount(); ++i)
{
TunnelTracker* tracker = ThePlayerList->getNthPlayer(i)->getTunnelSystem();
if (tracker && tracker->removeFromContain(this))
{
break;
}
}

onRemovedFrom(nullptr);
}
#endif

//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
Int Object::getTransportSlotCount() const
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4318,6 +4318,22 @@ Object* AIUpdateInterface::getNextMoodTarget( Bool calledByAI, Bool calledDuring
//Not contained
return nullptr;
}

#if RTS_GENERALS && RETAIL_COMPATIBLE_CRC
// TheSuperHackers @bugfix Caball009 22/09/2026 Return early to prevent a crash.
// This is needed when a Tunnel Network with units is transferred to another player and then sold or destroyed.
// The units are not properly transferred and are left in an invalid state.
if (!container->getContain())
{
obj->friend_removeFromTunnelContain();

// destroy this object, because it serves no purpose in its invalid state
TheGameLogic->destroyObject(obj);

return nullptr;
}
#endif

if( !container->getContain()->isPassengerAllowedToFire() )
{
//Container doesn't allow for passenger to shoot.
Expand Down
Loading