diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h index 29d391b81a3..59045614419 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h @@ -449,10 +449,14 @@ class Object : public Thing, public Snapshot void onContainedBy( Object *containedBy ); void onRemovedFrom( Object *removedFrom ); Int getTransportSlotCount() const; - void friend_setContainedBy( Object *containedBy ) { m_containedBy = containedBy; } + void friend_setContainedBy( Object *containedBy ); const Object* getEnclosingContainedBy() const; ///< Find the first enclosing container in the containment chain. const Object* getOuterObject() const; ///< Get the top-level object +#if RETAIL_COMPATIBLE_CRC + void friend_setContainedByID(ObjectID id) { m_containedByID = id; } +#endif + // Special Powers ------------------------------------------------------------------------------- SpecialPowerModuleInterface *getSpecialPowerModule( const SpecialPowerTemplate *specialPowerTemplate ) const; void doSpecialPower( const SpecialPowerTemplate *specialPowerTemplate, UnsignedInt commandOptions, Bool forced = false ); ///< execute power diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/HelixContain.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/HelixContain.cpp index aff9d91941c..c900e02d348 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/HelixContain.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/HelixContain.cpp @@ -250,10 +250,28 @@ void HelixContain::addToContainList( Object *obj ) if ( portable ) TheGameLogic->destroyObject( portable ); - m_portableStructureID = obj->getID(); - obj->friend_setContainedBy( getObject() );//fool portable into thinking my object is his container + portable = obj; + m_portableStructureID = portable->getID(); + portable->friend_setContainedBy( getObject() );//fool portable into thinking my object is his container +#if RETAIL_COMPATIBLE_CRC + Object* containedBy = getObject(); + + // TheSuperHackers @info Set INVALID_ID if the container object was destroyed + // to indicate that the pointer will become a dangling pointer in the next frame. + if (containedBy && !containedBy->isDestroyed()) + { + portable->friend_setContainedByID(containedBy->getID()); + } + else + { + portable->friend_setContainedByID(INVALID_ID); + } +#else + DEBUG_ASSERTCRASH(getObject() == nullptr || !getObject()->isDestroyed(), + ("HelixContain::addToContainList - Adding to a destroyed container")); +#endif } else TransportContain::addToContainList( obj ); @@ -268,10 +286,28 @@ void HelixContain::addToContain( Object *obj ) if ( portable ) TheGameLogic->destroyObject( portable ); - m_portableStructureID = obj->getID(); - obj->friend_setContainedBy( getObject() );//fool portable into thinking my object is his container + portable = obj; + + m_portableStructureID = portable->getID(); + portable->friend_setContainedBy( getObject() );//fool portable into thinking my object is his container +#if RETAIL_COMPATIBLE_CRC + Object* containedBy = getObject(); + // TheSuperHackers @info Set INVALID_ID if the container object was destroyed + // to indicate that the pointer will become a dangling pointer in the next frame. + if (containedBy && !containedBy->isDestroyed()) + { + portable->friend_setContainedByID(containedBy->getID()); + } + else + { + portable->friend_setContainedByID(INVALID_ID); + } +#else + DEBUG_ASSERTCRASH(getObject() == nullptr || !getObject()->isDestroyed(), + ("HelixContain::addToContain - Adding to a destroyed container")); +#endif } else TransportContain::addToContain( obj ); @@ -284,10 +320,16 @@ void HelixContain::removeFromContain( Object *obj, Bool exposeStealthUnits ) { Object *portable = getPortableStructure(); if ( portable ) + { +#if RETAIL_COMPATIBLE_CRC + portable->friend_setContainedByID(INVALID_ID); +#else + portable->friend_setContainedBy(nullptr); +#endif m_portableStructureID = INVALID_ID; //portable->kill(); - + } } else { diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp index 0bd898aae75..8ddeb128638 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp @@ -749,6 +749,15 @@ Int Object::getTransportSlotCount() const return count; } +void Object::friend_setContainedBy(Object* containedBy) +{ + m_containedBy = containedBy; + +#if !RETAIL_COMPATIBLE_CRC + m_containedByFrame = containedBy ? TheGameLogic->getFrame() : 0; +#endif +} + const Object* Object::getEnclosingContainedBy() const { for (const Object* child = this, *container = getContainedBy(); container; child = container, container = container->getContainedBy()) @@ -4290,8 +4299,9 @@ void Object::xfer( Xfer *xfer ) // No, the contain module is just going to friend_ reach in and set this for us. // Containers more complicated than Open (like Tunnel) can't do that. Our variable, // our responsibility. -#if !RETAIL_COMPATIBLE_CRC +#if RETAIL_COMPATIBLE_CRC // TheSuperHackers @tweak Contained by ID is already set with retail compatibility; don't overwrite it. +#else if( xfer->getXferMode() == XFER_SAVE ) { if( m_containedBy != nullptr )