diff --git a/GeneralsMD/Code/GameEngine/Include/Common/TunnelTracker.h b/GeneralsMD/Code/GameEngine/Include/Common/TunnelTracker.h index cde04a1f850..513131c3564 100644 --- a/GeneralsMD/Code/GameEngine/Include/Common/TunnelTracker.h +++ b/GeneralsMD/Code/GameEngine/Include/Common/TunnelTracker.h @@ -49,6 +49,7 @@ class TunnelTracker : public MemoryPoolObject, UnsignedInt getContainCount() const { return m_containListSize; } Int getContainMax() const; const ContainedItemsList* getContainedItemsList() const { return &m_containList; } + void swapContainedItemsList(ContainedItemsList& newList); Bool isValidContainerFor(const Object* obj, Bool checkCapacity) const; void addToContainList( Object *obj ); ///< add 'obj' to contain list diff --git a/GeneralsMD/Code/GameEngine/Source/Common/RTS/TunnelTracker.cpp b/GeneralsMD/Code/GameEngine/Source/Common/RTS/TunnelTracker.cpp index 1a4a2141574..748e9aa0039 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/RTS/TunnelTracker.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/RTS/TunnelTracker.cpp @@ -107,6 +107,13 @@ Int TunnelTracker::getContainMax() const return TheGlobalData->m_maxTunnelCapacity; } +// ------------------------------------------------------------------------ +void TunnelTracker::swapContainedItemsList(ContainedItemsList& newList) +{ + m_containList.swap(newList); + m_containListSize = (Int)m_containList.size(); +} + // ------------------------------------------------------------------------ void TunnelTracker::updateNemesis(const Object *target) { diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/TunnelContain.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/TunnelContain.cpp index b5402ba3123..f5f73efbf49 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/TunnelContain.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/TunnelContain.cpp @@ -149,18 +149,27 @@ void TunnelContain::harmAndForceExitAllContained( DamageInfo *info ) //------------------------------------------------------------------------------------------------- void TunnelContain::killAllContained( void ) { + // TheSuperHackers @bugfix xezon 04/06/2025 Empty the TunnelSystem's Contained Items List + // straight away to prevent a potential child call to catastrophically modify it as well. + // This scenario can happen if the killed occupant(s) apply deadly damage on death + // to the host container, which then attempts to remove all remaining occupants + // on the death of the host container. This is reproducible by shooting with + // Neutron Shells on a GLA Tunnel containing GLA Terrorists. + + ContainedItemsList list; Player *owningPlayer = getObject()->getControllingPlayer(); - const ContainedItemsList *fullList = owningPlayer->getTunnelSystem()->getContainedItemsList(); + owningPlayer->getTunnelSystem()->swapContainedItemsList(list); - Object *obj; - ContainedItemsList::const_iterator it; - it = (*fullList).begin(); - while( it != (*fullList).end() ) + ContainedItemsList::iterator it = list.begin(); + + while ( it != list.end() ) { - obj = *it; - it++; + Object *obj = *it++; + DEBUG_ASSERTCRASH( obj, ("Contain list must not contain NULL element")); + removeFromContain( obj, true ); - obj->kill(); + + obj->kill(); } } //-------------------------------------------------------------------------------------------------