diff --git a/Generals/Code/GameEngine/Include/GameLogic/Module/ContainModule.h b/Generals/Code/GameEngine/Include/GameLogic/Module/ContainModule.h index a07b9a856b..b62b2a7f24 100644 --- a/Generals/Code/GameEngine/Include/GameLogic/Module/ContainModule.h +++ b/Generals/Code/GameEngine/Include/GameLogic/Module/ContainModule.h @@ -155,6 +155,7 @@ class ContainModuleInterface virtual const Object *friend_getRider() const = 0; ///< Damn. The draw order dependency bug for riders means that our draw module needs to cheat to get around it. virtual Real getContainedItemsMass() const = 0; virtual UnsignedInt getStealthUnitsContained() const = 0; + virtual UnsignedInt getHeroUnitsContained() const = 0; virtual Bool calcBestGarrisonPosition( Coord3D *sourcePos, const Coord3D *targetPos ) = 0; virtual Bool attemptBestFirePointPosition( Object *source, Weapon *weapon, Object *victim ) = 0; diff --git a/Generals/Code/GameEngine/Include/GameLogic/Module/OpenContain.h b/Generals/Code/GameEngine/Include/GameLogic/Module/OpenContain.h index fecfaa2e13..922fd44b72 100644 --- a/Generals/Code/GameEngine/Include/GameLogic/Module/OpenContain.h +++ b/Generals/Code/GameEngine/Include/GameLogic/Module/OpenContain.h @@ -164,6 +164,7 @@ class OpenContain : public UpdateModule, virtual const Object *friend_getRider() const{return NULL;} ///< Damn. The draw order dependency bug for riders means that our draw module needs to cheat to get around it. virtual Real getContainedItemsMass() const; virtual UnsignedInt getStealthUnitsContained() const { return m_stealthUnitsContained; } + virtual UnsignedInt getHeroUnitsContained() const { return m_heroUnitsContained; } virtual PlayerMaskType getPlayerWhoEntered(void) const { return m_playerEnteredMask; } @@ -238,6 +239,7 @@ class OpenContain : public UpdateModule, ObjectEnterExitMap m_objectEnterExitInfo; UnsignedInt m_stealthUnitsContained; ///< number of stealth units that can't be seen by enemy players. + UnsignedInt m_heroUnitsContained; ///< cached hero count Int m_whichExitPath; ///< Cycles from 1 to n and is used only in modules whose data has numberOfExitPaths > 1. UnsignedInt m_doorCloseCountdown; ///< When should I shut my door. diff --git a/Generals/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp b/Generals/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp index 9cff8d09cd..72dd98ff8f 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp @@ -125,6 +125,7 @@ OpenContain::OpenContain( Thing *thing, const ModuleData* moduleData ) : UpdateM m_lastLoadSoundFrame = 0; m_containListSize = 0; m_stealthUnitsContained = 0; + m_heroUnitsContained = 0; m_doorCloseCountdown = 0; m_rallyPoint.zero(); @@ -352,6 +353,10 @@ void OpenContain::addToContainList( Object *rider ) { m_stealthUnitsContained++; } + if( rider->isKindOf( KINDOF_HERO ) ) + { + m_heroUnitsContained++; + } } //------------------------------------------------------------------------------------------------- @@ -542,6 +547,11 @@ void OpenContain::removeFromContainViaIterator( ContainedItemsList::iterator it, } } } + if( rider->isKindOf( KINDOF_HERO ) ) + { + DEBUG_ASSERTCRASH( m_heroUnitsContained > 0, ("Removing hero but hero count is 0") ); + m_heroUnitsContained--; + } if (isEnclosingContainerFor( rider )) @@ -620,7 +630,7 @@ void OpenContain::scatterToNearbyPosition(Object* rider) } //------------------------------------------------------------------------------------------------- -void OpenContain::onContaining( Object * /*rider*/ ) +void OpenContain::onContaining( Object *rider ) { // Play audio if( m_loadSoundsEnabled ) @@ -1442,13 +1452,18 @@ void OpenContain::crc( Xfer *xfer ) // ------------------------------------------------------------------------------------------------ /** Xfer method * Version Info: - * 1: Initial version */ + * 1: Initial version + * 2: Added m_heroUnitsContained cached hero count (bobtista) */ // ------------------------------------------------------------------------------------------------ void OpenContain::xfer( Xfer *xfer ) { // version - const XferVersion currentVersion = 1; +#if RETAIL_COMPATIBLE_XFER_SAVE + XferVersion currentVersion = 1; +#else + XferVersion currentVersion = 2; +#endif XferVersion version = currentVersion; xfer->xferVersion( &version, currentVersion ); @@ -1526,6 +1541,24 @@ void OpenContain::xfer( Xfer *xfer ) // stealth units contained xfer->xferUnsignedInt( &m_stealthUnitsContained ); + // hero units contained +#if !RETAIL_COMPATIBLE_XFER_SAVE + if (version >= 2) + { + xfer->xferUnsignedInt( &m_heroUnitsContained ); + } + else if (xfer->getXferMode() == XFER_LOAD) + { + m_heroUnitsContained = 0; + } +#else + // In retail compatibility mode, hero count is restored by iterating hero objects in loadPostProcess + if (xfer->getXferMode() == XFER_LOAD) + { + m_heroUnitsContained = 0; + } +#endif + // door close countdown xfer->xferUnsignedInt( &m_doorCloseCountdown ); @@ -1658,6 +1691,18 @@ void OpenContain::loadPostProcess( void ) } +#if RETAIL_COMPATIBLE_XFER_SAVE + // In retail compatibility mode, restore hero count by iterating hero objects + m_heroUnitsContained = 0; + for( ContainedItemsList::const_iterator it = m_containList.begin(); it != m_containList.end(); ++it ) + { + if( (*it)->isKindOf( KINDOF_HERO ) ) + { + m_heroUnitsContained++; + } + } +#endif + // sanity DEBUG_ASSERTCRASH( m_containListSize == m_containList.size(), ("OpenContain::loadPostProcess - contain list count mismatch") ); diff --git a/Generals/Code/GameEngine/Source/GameLogic/Object/Object.cpp b/Generals/Code/GameEngine/Source/GameLogic/Object/Object.cpp index d0f4b52301..f9b16b8cc8 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/Object/Object.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/Object/Object.cpp @@ -593,25 +593,13 @@ Object::~Object() } //------------------------------------------------------------------------------------------------- -void localIsHero( Object *obj, void* userData ) -{ - Bool *hero = (Bool*)userData; - - if( obj && obj->isKindOf( KINDOF_HERO ) ) - { - *hero = TRUE; - } -} - -//------------------------------------------------------------------------------------------------- +// TheSuperHackers @performance bobtista 13/11/2025 Use cached hero count for O(1) lookup instead of O(n) iteration. Bool Object::isHero() const { ContainModuleInterface *contain = getContain(); if( contain ) { - Bool heroInside = FALSE; - contain->iterateContained( localIsHero, (void*)(&heroInside), FALSE ); - if( heroInside ) + if( contain->getHeroUnitsContained() > 0 ) { return TRUE; } diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/ContainModule.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/ContainModule.h index f85817f831..5a2db53fd1 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/ContainModule.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/ContainModule.h @@ -175,6 +175,7 @@ class ContainModuleInterface virtual const Object *friend_getRider() const = 0; ///< Damn. The draw order dependency bug for riders means that our draw module needs to cheat to get around it. virtual Real getContainedItemsMass() const = 0; virtual UnsignedInt getStealthUnitsContained() const = 0; + virtual UnsignedInt getHeroUnitsContained() const = 0; virtual Bool calcBestGarrisonPosition( Coord3D *sourcePos, const Coord3D *targetPos ) = 0; virtual Bool attemptBestFirePointPosition( Object *source, Weapon *weapon, Object *victim ) = 0; diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/OpenContain.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/OpenContain.h index 180fc86aac..027b29513f 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/OpenContain.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/OpenContain.h @@ -174,6 +174,7 @@ class OpenContain : public UpdateModule, virtual const Object *friend_getRider() const{return NULL;} ///< Damn. The draw order dependency bug for riders means that our draw module needs to cheat to get around it. virtual Real getContainedItemsMass() const; virtual UnsignedInt getStealthUnitsContained() const { return m_stealthUnitsContained; } + virtual UnsignedInt getHeroUnitsContained() const { return m_heroUnitsContained; } virtual PlayerMaskType getPlayerWhoEntered(void) const { return m_playerEnteredMask; } @@ -259,6 +260,7 @@ class OpenContain : public UpdateModule, ObjectEnterExitMap m_objectEnterExitInfo; UnsignedInt m_stealthUnitsContained; ///< number of stealth units that can't be seen by enemy players. + UnsignedInt m_heroUnitsContained; ///< cached hero count Int m_whichExitPath; ///< Cycles from 1 to n and is used only in modules whose data has numberOfExitPaths > 1. UnsignedInt m_doorCloseCountdown; ///< When should I shut my door. diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp index 2f2bbea878..7b12b481f2 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp @@ -129,6 +129,7 @@ OpenContain::OpenContain( Thing *thing, const ModuleData* moduleData ) : UpdateM m_lastLoadSoundFrame = 0; m_containListSize = 0; m_stealthUnitsContained = 0; + m_heroUnitsContained = 0; m_doorCloseCountdown = 0; m_rallyPoint.zero(); @@ -374,6 +375,10 @@ void OpenContain::addToContainList( Object *rider ) { m_stealthUnitsContained++; } + if( rider->isKindOf( KINDOF_HERO ) ) + { + m_heroUnitsContained++; + } } //------------------------------------------------------------------------------------------------- @@ -660,6 +665,11 @@ void OpenContain::removeFromContainViaIterator( ContainedItemsList::iterator it, } } } + if( rider->isKindOf( KINDOF_HERO ) ) + { + DEBUG_ASSERTCRASH( m_heroUnitsContained > 0, ("Removing hero but hero count is 0") ); + m_heroUnitsContained--; + } if (isEnclosingContainerFor( rider )) @@ -1667,13 +1677,19 @@ void OpenContain::crc( Xfer *xfer ) // ------------------------------------------------------------------------------------------------ /** Xfer method * Version Info: - * 1: Initial version */ + * 1: Initial version + * 2: Added m_passengerAllowedToFire + * 3: Added m_heroUnitsContained cached hero count (bobtista) */ // ------------------------------------------------------------------------------------------------ void OpenContain::xfer( Xfer *xfer ) { // version - const XferVersion currentVersion = 2; +#if RETAIL_COMPATIBLE_XFER_SAVE + XferVersion currentVersion = 2; +#else + XferVersion currentVersion = 3; +#endif XferVersion version = currentVersion; xfer->xferVersion( &version, currentVersion ); @@ -1751,6 +1767,24 @@ void OpenContain::xfer( Xfer *xfer ) // stealth units contained xfer->xferUnsignedInt( &m_stealthUnitsContained ); + // hero units contained +#if !RETAIL_COMPATIBLE_XFER_SAVE + if (version >= 3) + { + xfer->xferUnsignedInt( &m_heroUnitsContained ); + } + else if (xfer->getXferMode() == XFER_LOAD) + { + m_heroUnitsContained = 0; + } +#else + // In retail compatibility mode, hero count is restored by iterating hero objects in loadPostProcess + if (xfer->getXferMode() == XFER_LOAD) + { + m_heroUnitsContained = 0; + } +#endif + // door close countdown xfer->xferUnsignedInt( &m_doorCloseCountdown ); @@ -1890,6 +1924,18 @@ void OpenContain::loadPostProcess( void ) } +#if RETAIL_COMPATIBLE_XFER_SAVE + // In retail compatibility mode, restore hero count by iterating hero objects + m_heroUnitsContained = 0; + for( ContainedItemsList::const_iterator it = m_containList.begin(); it != m_containList.end(); ++it ) + { + if( (*it)->isKindOf( KINDOF_HERO ) ) + { + m_heroUnitsContained++; + } + } +#endif + // sanity DEBUG_ASSERTCRASH( m_containListSize == m_containList.size(), ("OpenContain::loadPostProcess - contain list count mismatch") ); diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp index 598e266bea..401a8460bd 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp @@ -2042,25 +2042,14 @@ Bool Object::isNonFactionStructure(void) const return isStructure() && !isFactionStructure(); } -void localIsHero( Object *obj, void* userData ) -{ - Bool *hero = (Bool*)userData; - - if( obj && obj->isKindOf( KINDOF_HERO ) ) - { - *hero = TRUE; - } -} - //------------------------------------------------------------------------------------------------- +// TheSuperHackers @performance bobtista 13/11/2025 Use cached hero count for O(1) lookup instead of O(n) iteration. Bool Object::isHero(void) const { ContainModuleInterface *contain = getContain(); if( contain ) { - Bool heroInside = FALSE; - contain->iterateContained( localIsHero, (void*)(&heroInside), FALSE ); - if( heroInside ) + if( contain->getHeroUnitsContained() > 0 ) { return TRUE; }