diff --git a/Core/GameEngine/Include/Common/Radar.h b/Core/GameEngine/Include/Common/Radar.h index 7ab715970e1..1800408f5f5 100644 --- a/Core/GameEngine/Include/Common/Radar.h +++ b/Core/GameEngine/Include/Common/Radar.h @@ -106,7 +106,6 @@ class RadarObject : public MemoryPoolObject, const RadarObject *friend_getNext( void ) const { return m_next; } Bool isTemporarilyHidden() const; - static Bool isTemporarilyHidden(const Object* obj); protected: @@ -258,12 +257,6 @@ class Radar : public Snapshot, * in exactly the same priority as the regular * object list for all other objects */ - // TheSuperHackers @bugfix xezon 22/11/2025 Now stores local heroes in a separate list, - // because they are treated with special icons but should otherwise work like all other - // radar objects. In retail version, the cached hero object data was able to dangle - // for a few frames and cause undefined behavior. - RadarObject *m_localHeroObjectList; ///< list of hero objects for the local player - Real m_terrainAverageZ; ///< average Z for terrain samples Real m_waterAverageZ; ///< average Z for water samples diff --git a/Core/GameEngine/Source/Common/System/Radar.cpp b/Core/GameEngine/Source/Common/System/Radar.cpp index 4b482a2c188..5db41793d41 100644 --- a/Core/GameEngine/Source/Common/System/Radar.cpp +++ b/Core/GameEngine/Source/Common/System/Radar.cpp @@ -81,7 +81,6 @@ void Radar::deleteListResources( void ) { deleteList(&m_objectList); deleteList(&m_localObjectList); - deleteList(&m_localHeroObjectList); #ifdef DEBUG_CRASHING for( Object *obj = TheGameLogic->getFirstObject(); obj; obj = obj->getNextObject() ) @@ -115,14 +114,8 @@ RadarObject::~RadarObject( void ) //------------------------------------------------------------------------------------------------- Bool RadarObject::isTemporarilyHidden() const { - return isTemporarilyHidden(m_object); -} + Drawable* draw = m_object->getDrawable(); -//------------------------------------------------------------------------------------------------- -//------------------------------------------------------------------------------------------------- -Bool RadarObject::isTemporarilyHidden(const Object* obj) -{ - Drawable* draw = obj->getDrawable(); if (draw == nullptr || draw->getStealthLook() == STEALTHLOOK_INVISIBLE || draw->isDrawableEffectivelyHidden()) return true; @@ -192,7 +185,6 @@ Radar::Radar( void ) m_radarWindow = nullptr; m_objectList = nullptr; m_localObjectList = nullptr; - m_localHeroObjectList = nullptr; std::fill(m_radarHidden, m_radarHidden + ARRAY_SIZE(m_radarHidden), false); std::fill(m_radarForceOn, m_radarForceOn + ARRAY_SIZE(m_radarForceOn), false); m_terrainAverageZ = 0.0f; @@ -416,10 +408,7 @@ Bool Radar::addObject( Object *obj ) // if( obj->isLocallyControlled() ) { - if ( obj->isHero() ) - list = &m_localHeroObjectList; - else - list = &m_localObjectList; + list = &m_localObjectList; } else { @@ -483,8 +472,6 @@ Bool Radar::removeObject( Object *obj ) if( obj->friend_getRadarData() == nullptr ) return FALSE; - if( deleteFromList( obj, &m_localHeroObjectList ) == TRUE ) - return TRUE; if( deleteFromList( obj, &m_localObjectList ) == TRUE ) return TRUE; else if( deleteFromList( obj, &m_objectList ) == TRUE ) @@ -718,14 +705,10 @@ Object *Radar::objectUnderRadarPixel( const ICoord2D *pixel ) // to the radar location // - // search the local hero object list - obj = searchListForRadarLocationMatch( m_localHeroObjectList, &radar ); - - // search the local object list if not found - if( obj == nullptr ) - obj = searchListForRadarLocationMatch( m_localObjectList, &radar ); + // search the local object list + obj = searchListForRadarLocationMatch( m_localObjectList, &radar ); - // search all other objects if still not found + // search all other objects if not found if( obj == nullptr ) obj = searchListForRadarLocationMatch( m_objectList, &radar ); @@ -1365,7 +1348,6 @@ static void xferRadarObjectList( Xfer *xfer, RadarObject **head ) * Version Info: * 1: Initial version * 2: TheSuperHackers @tweak Serialize m_radarHidden, m_radarForceOn for each player - * 3: TheSuperHackers @tweak Serialize m_localHeroObjectList */ // ------------------------------------------------------------------------------------------------ void Radar::xfer( Xfer *xfer ) @@ -1375,7 +1357,7 @@ void Radar::xfer( Xfer *xfer ) #if RETAIL_COMPATIBLE_XFER_SAVE XferVersion currentVersion = 1; #else - XferVersion currentVersion = 3; + XferVersion currentVersion = 2; #endif XferVersion version = currentVersion; xfer->xferVersion( &version, currentVersion ); @@ -1405,66 +1387,12 @@ void Radar::xfer( Xfer *xfer ) xfer->xferUser(&m_radarForceOn, sizeof(m_radarForceOn)); } - if (version <= 2) - { - if (xfer->getXferMode() == XFER_SAVE) - { - // TheSuperHackers @info For legacy xfer compatibility. - // Transfer all local hero objects to local object list. - RadarObject **fromList = &m_localHeroObjectList; - RadarObject **toList = &m_localObjectList; - while (*fromList != nullptr) - { - RadarObject* nextObject = (*fromList)->friend_getNext(); - (*fromList)->friend_setNext(nullptr); - linkRadarObject(*fromList, toList); - *fromList = nextObject; - } - } - } - else - { - xferRadarObjectList( xfer, &m_localHeroObjectList ); - } - // save our local object list xferRadarObjectList( xfer, &m_localObjectList ); // save the regular object list xferRadarObjectList( xfer, &m_objectList ); - if (version <= 2) - { - // TheSuperHackers @info For legacy xfer compatibility. - // Transfer hero local object(s) back to local hero object list. - // This needs to be done on both load and save. - RadarObject **fromList = &m_localObjectList; - RadarObject **toList = &m_localHeroObjectList; - RadarObject *currObject; - RadarObject *prevObject; - RadarObject *nextObject; - prevObject = nullptr; - for (currObject = *fromList; currObject != nullptr; currObject = nextObject) - { - nextObject = currObject->friend_getNext(); - if (currObject->friend_getObject()->isHero()) - { - if (prevObject != nullptr) - { - prevObject->friend_setNext(nextObject); - } - else - { - *fromList = nextObject; - } - currObject->friend_setNext(nullptr); - linkRadarObject(currObject, toList); - continue; - } - prevObject = currObject; - } - } - // save the radar event count and data UnsignedShort eventCountVerify = MAX_RADAR_EVENTS; UnsignedShort eventCount = eventCountVerify; diff --git a/Core/GameEngineDevice/Source/W3DDevice/Common/System/W3DRadar.cpp b/Core/GameEngineDevice/Source/W3DDevice/Common/System/W3DRadar.cpp index f62d68654a3..b2e6ba07c56 100644 --- a/Core/GameEngineDevice/Source/W3DDevice/Common/System/W3DRadar.cpp +++ b/Core/GameEngineDevice/Source/W3DDevice/Common/System/W3DRadar.cpp @@ -607,12 +607,17 @@ void W3DRadar::drawEvents( Int pixelX, Int pixelY, Int width, Int height ) void W3DRadar::drawIcons( Int pixelX, Int pixelY, Int width, Int height ) { Player *player = rts::getObservedOrLocalPlayer(); - for (RadarObject *heroObj = m_localHeroObjectList; heroObj; heroObj = heroObj->friend_getNext()) + for (RadarObject *heroObj = m_localObjectList; heroObj; heroObj = heroObj->friend_getNext()) { - if (canRenderObject(heroObj, player)) - { - drawHeroIcon(pixelX, pixelY, width, height, heroObj->friend_getObject()->getPosition()); - } + const Object *obj = heroObj->friend_getObject(); + + if (!obj->isHero()) + continue; + + if (!canRenderObject(heroObj, player)) + continue; + + drawHeroIcon(pixelX, pixelY, width, height, obj->getPosition()); } } @@ -628,7 +633,6 @@ void W3DRadar::updateObjectTexture(TextureClass *texture) // rebuild the object overlay renderObjectList( m_objectList, texture ); renderObjectList( m_localObjectList, texture ); - renderObjectList( m_localHeroObjectList, texture ); } //------------------------------------------------------------------------------------------------- diff --git a/GeneralsMD/Code/GameEngine/Include/Common/TunnelTracker.h b/GeneralsMD/Code/GameEngine/Include/Common/TunnelTracker.h index e1e4236531b..53e3be17edb 100644 --- a/GeneralsMD/Code/GameEngine/Include/Common/TunnelTracker.h +++ b/GeneralsMD/Code/GameEngine/Include/Common/TunnelTracker.h @@ -44,6 +44,7 @@ class TunnelTracker : public MemoryPoolObject, // contain list access void iterateContained( ContainIterateFunc func, void *userData, Bool reverse ); UnsignedInt getContainCount() const { return m_containListSize; } + UnsignedInt getHeroUnitsContained() const { return m_heroUnitsContained; } Int getContainMax() const; const ContainedItemsList* getContainedItemsList() const { return &m_containList; } void swapContainedItemsList(ContainedItemsList& newList); @@ -85,6 +86,7 @@ class TunnelTracker : public MemoryPoolObject, ContainedItemsList m_containList; ///< the contained object pointers list std::list< ObjectID > m_xferContainList;///< for loading of m_containList during post processing Int m_containListSize; ///< size of the contain list + UnsignedInt m_heroUnitsContained; ///< cached hero count UnsignedInt m_tunnelCount; ///< How many tunnels have registered so we know when we should kill our contain list UnsignedInt m_framesForFullHeal; ///< How many frames it takes to fully heal a unit Bool m_needsFullHealTimeUpdate; ///< Set to true when needing to recalc full heal time to batch the operation diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/OpenContain.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/OpenContain.h index b6561d72f85..c6f389ee3bd 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/OpenContain.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/OpenContain.h @@ -261,7 +261,6 @@ 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 - XferVersion m_xferVersion; ///< version of loaded save file for loadPostProcess 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/Include/GameLogic/Module/TunnelContain.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/TunnelContain.h index 3741eea95fc..17eeb97588e 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/TunnelContain.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/TunnelContain.h @@ -112,6 +112,7 @@ class TunnelContain : public OpenContain, public CreateModuleInterface // contain list access virtual void iterateContained( ContainIterateFunc func, void *userData, Bool reverse ); virtual UnsignedInt getContainCount() const; + virtual UnsignedInt getHeroUnitsContained() const; virtual Int getContainMax( void ) const; virtual const ContainedItemsList* getContainedItemsList() const; virtual UnsignedInt getFullTimeForHeal(void) const; ///< Returns the time in frames until a contained object becomes fully healed diff --git a/GeneralsMD/Code/GameEngine/Source/Common/RTS/TunnelTracker.cpp b/GeneralsMD/Code/GameEngine/Source/Common/RTS/TunnelTracker.cpp index 888551dd259..47b210d6f61 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/RTS/TunnelTracker.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/RTS/TunnelTracker.cpp @@ -52,6 +52,7 @@ TunnelTracker::TunnelTracker() { m_tunnelCount = 0; m_containListSize = 0; + m_heroUnitsContained = 0; m_curNemesisID = INVALID_ID; m_nemesisTimestamp = 0; m_framesForFullHeal = 0; @@ -187,6 +188,11 @@ void TunnelTracker::addToContainList( Object *obj ) { m_containList.push_back(obj); ++m_containListSize; + + if (obj->isKindOf(KINDOF_HERO)) + { + ++m_heroUnitsContained; + } } // ------------------------------------------------------------------------ @@ -199,6 +205,12 @@ void TunnelTracker::removeFromContain( Object *obj, Bool exposeStealthUnits ) // note that this invalidates the iterator! m_containList.erase(it); --m_containListSize; + + if (obj->isKindOf(KINDOF_HERO)) + { + DEBUG_ASSERTCRASH(m_heroUnitsContained > 0, ("TunnelTracker::removeFromContain - Removing hero but hero count is %d", m_heroUnitsContained)); + --m_heroUnitsContained; + } } } @@ -433,6 +445,8 @@ void TunnelTracker::loadPostProcess( void ) } // translate each object ids on the xferContainList into real object pointers in the contain list + m_containListSize = 0; + m_heroUnitsContained = 0; Object *obj; std::list< ObjectID >::const_iterator it; for( it = m_xferContainList.begin(); it != m_xferContainList.end(); ++it ) @@ -448,7 +462,7 @@ void TunnelTracker::loadPostProcess( void ) } // push on the back of the contain list - m_containList.push_back( obj ); + addToContainList( obj ); // Crap. This is in OpenContain as a fix, but not here. { diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp index fd99fe3d9b6..5baeb27b9bb 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp @@ -130,7 +130,6 @@ OpenContain::OpenContain( Thing *thing, const ModuleData* moduleData ) : UpdateM m_containListSize = 0; m_stealthUnitsContained = 0; m_heroUnitsContained = 0; - m_xferVersion = 1; m_doorCloseCountdown = 0; m_rallyPoint.zero(); @@ -1681,21 +1680,15 @@ void OpenContain::crc( Xfer *xfer ) * Version Info: * 1: Initial version * 2: Added m_passengerAllowedToFire - * 3: TheSuperHackers @tweak Serialize hero units contained count */ // ------------------------------------------------------------------------------------------------ void OpenContain::xfer( Xfer *xfer ) { // version -#if RETAIL_COMPATIBLE_XFER_SAVE XferVersion currentVersion = 2; -#else - XferVersion currentVersion = 3; -#endif XferVersion version = currentVersion; xfer->xferVersion( &version, currentVersion ); - m_xferVersion = version; // extend base class UpdateModule::xfer( xfer ); @@ -1720,7 +1713,7 @@ void OpenContain::xfer( Xfer *xfer ) else { - // the containment list should be emtpy at this time + // the containment list should be empty at this time if( m_containList.empty() == FALSE ) { #if 1 @@ -1769,18 +1762,12 @@ void OpenContain::xfer( Xfer *xfer ) xfer->xferUnsignedInt( &m_lastLoadSoundFrame ); // stealth units contained - xfer->xferUnsignedInt( &m_stealthUnitsContained ); - - // hero units contained - if (version >= 3) - { - xfer->xferUnsignedInt( &m_heroUnitsContained ); - } + xfer->xferUnsignedInt( &m_stealthUnitsContained ); // TheSuperHackers @todo This is redundant information in xfer. Remove it. // door close countdown xfer->xferUnsignedInt( &m_doorCloseCountdown ); - // conditionstate + // condition state m_conditionState.xfer( xfer ); // fire points @@ -1829,7 +1816,7 @@ void OpenContain::xfer( Xfer *xfer ) else { - // the map should be emtpy now + // the map should be empty now if( m_objectEnterExitInfo.empty() == FALSE ) { @@ -1887,6 +1874,9 @@ void OpenContain::loadPostProcess( void ) } // turn the contained id list into actual object pointers in the contain list + m_containListSize = 0; + m_stealthUnitsContained = 0; + m_heroUnitsContained = 0; Object *obj; std::list::const_iterator idIt; for( idIt = m_xferContainIDList.begin(); idIt != m_xferContainIDList.end(); ++idIt ) @@ -1905,7 +1895,7 @@ void OpenContain::loadPostProcess( void ) } // put object on list - m_containList.push_back( obj ); + addToContainList( obj ); // remove this object from the world if we need to if( isEnclosingContainerFor( obj ) ) @@ -1916,19 +1906,6 @@ void OpenContain::loadPostProcess( void ) } - if (m_xferVersion < 3) - { - // Restore hero count by iterating hero objects for old save versions - m_heroUnitsContained = 0; - for( ContainedItemsList::const_iterator it = m_containList.begin(); it != m_containList.end(); ++it ) - { - if( (*it)->isKindOf( KINDOF_HERO ) ) - { - m_heroUnitsContained++; - } - } - } - // sanity DEBUG_ASSERTCRASH( m_containListSize == m_containList.size(), ("OpenContain::loadPostProcess - contain list count mismatch") ); diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/TunnelContain.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/TunnelContain.cpp index b822682febd..4246fe6c403 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/TunnelContain.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/TunnelContain.cpp @@ -305,6 +305,16 @@ UnsignedInt TunnelContain::getContainCount() const return 0; } +UnsignedInt TunnelContain::getHeroUnitsContained() const +{ + Player *owningPlayer = getObject()->getControllingPlayer(); + if( owningPlayer && owningPlayer->getTunnelSystem() ) + { + return owningPlayer->getTunnelSystem()->getHeroUnitsContained(); + } + return 0; +} + Int TunnelContain::getContainMax( void ) const { Player *owningPlayer = getObject()->getControllingPlayer();