diff --git a/Generals/Code/GameEngine/Include/GameLogic/ExperienceTracker.h b/Generals/Code/GameEngine/Include/GameLogic/ExperienceTracker.h index d524772aac..8f33f93d11 100644 --- a/Generals/Code/GameEngine/Include/GameLogic/ExperienceTracker.h +++ b/Generals/Code/GameEngine/Include/GameLogic/ExperienceTracker.h @@ -46,6 +46,8 @@ class ExperienceTracker : public MemoryPoolObject, public Snapshot Int getExperienceValue( const Object* killer ) const; ///< How much do give for being killed Int getCurrentExperience( void ) const { return m_currentExperience; }; ///< How much experience do I have at the moment? Bool isTrainable() const; ///< Can I gain experience? + void setTrainable(Bool trainable); ///< Set whether I can gain experience + void resetTrainable(); ///< Set to default trainable state from template Bool isAcceptingExperiencePoints() const; ///< Either I am trainable, or I have a Sink set up void setVeterancyLevel( VeterancyLevel newLevel, Bool provideFeedback = TRUE ); ///< Set Level to this @@ -71,4 +73,5 @@ class ExperienceTracker : public MemoryPoolObject, public Snapshot Int m_currentExperience; ///< Number of experience points ObjectID m_experienceSink; ///< ID of object I have pledged my experience point gains to Real m_experienceScalar; ///< Scales any experience gained by this multiplier. + Bool m_isTrainable; ///< Can I gain experience? }; diff --git a/Generals/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp b/Generals/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp index f0590f22b3..35a406d462 100644 --- a/Generals/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp +++ b/Generals/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp @@ -4113,42 +4113,45 @@ GameMessageDisposition CommandTranslator::translateGameMessage(const GameMessage case GameMessage::MSG_META_DEBUG_GIVE_VETERANCY: case GameMessage::MSG_META_DEBUG_TAKE_VETERANCY: { + if (TheGameLogic->isInMultiplayerGame()) + break; + const DrawableList *list = TheInGameUI->getAllSelectedDrawables(); for (DrawableListCIt it = list->begin(); it != list->end(); ++it) { Drawable *pDraw = *it; - if (pDraw) + if (!pDraw) + continue; + + Object *pObject = pDraw->getObject(); + if (!pObject) + continue; + + ExperienceTracker *et = pObject->getExperienceTracker(); + if (!et || !et->isTrainable()) + continue; + + VeterancyLevel oldVet = et->getVeterancyLevel(); + VeterancyLevel newVet = oldVet; + + if (t == GameMessage::MSG_META_DEBUG_GIVE_VETERANCY) { - Object *pObject = pDraw->getObject(); - if (pObject) + if (oldVet < LEVEL_LAST) { - ExperienceTracker *et = pObject->getExperienceTracker(); - if (et) - { - if (et->isTrainable()) - { - VeterancyLevel oldVet = et->getVeterancyLevel(); - VeterancyLevel newVet = oldVet; - if (t == GameMessage::MSG_META_DEBUG_GIVE_VETERANCY) - { - if (oldVet < LEVEL_LAST) - { - newVet = (VeterancyLevel)((Int)oldVet + 1); - } - } - else - { - if (oldVet > LEVEL_FIRST) - { - newVet = (VeterancyLevel)((Int)oldVet - 1); - } - } - et->setVeterancyLevel(newVet); - } - } + newVet = (VeterancyLevel)((Int)oldVet + 1); } } + else + { + if (oldVet > LEVEL_FIRST) + { + newVet = (VeterancyLevel)((Int)oldVet - 1); + } + } + + et->setVeterancyLevel(newVet); } + disp = DESTROY_MESSAGE; break; } diff --git a/Generals/Code/GameEngine/Source/GameLogic/Object/ExperienceTracker.cpp b/Generals/Code/GameEngine/Source/GameLogic/Object/ExperienceTracker.cpp index f6a1b05032..2518b38a34 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/Object/ExperienceTracker.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/Object/ExperienceTracker.cpp @@ -45,6 +45,7 @@ ExperienceTracker::ExperienceTracker(Object *parent) : m_experienceScalar( 1.0f ), m_currentExperience(0) { + resetTrainable(); } //------------------------------------------------------------------------------------------------- @@ -65,7 +66,19 @@ Int ExperienceTracker::getExperienceValue( const Object* killer ) const //------------------------------------------------------------------------------------------------- Bool ExperienceTracker::isTrainable() const { - return m_parent->getTemplate()->isTrainable(); + return m_isTrainable; +} + +//------------------------------------------------------------------------------------------------- +void ExperienceTracker::setTrainable(Bool trainable) +{ + m_isTrainable = trainable; +} + +//------------------------------------------------------------------------------------------------- +void ExperienceTracker::resetTrainable() +{ + m_isTrainable = m_parent->getTemplate()->isTrainable(); } //------------------------------------------------------------------------------------------------- @@ -236,19 +249,27 @@ void ExperienceTracker::crc( Xfer *xfer ) { xfer->xferInt( &m_currentExperience ); xfer->xferUser( &m_currentLevel, sizeof( VeterancyLevel ) ); +#if !RETAIL_COMPATIBLE_CRC + xfer->xferBool(&m_isTrainable); +#endif } //----------------------------------------------------------------------------- /** Xfer method * Version Info: * 1: Initial version + * 2: TheSuperHackers @tweak Serialize m_isTrainable */ // ---------------------------------------------------------------------------- void ExperienceTracker::xfer( Xfer *xfer ) { // version +#if RETAIL_COMPATIBLE_XFER_SAVE XferVersion currentVersion = 1; +#else + XferVersion currentVersion = 2; +#endif XferVersion version = currentVersion; xfer->xferVersion( &version, currentVersion ); @@ -267,6 +288,8 @@ void ExperienceTracker::xfer( Xfer *xfer ) // experience scalar xfer->xferReal( &m_experienceScalar ); + if (version >= 2) + xfer->xferBool(&m_isTrainable); } //----------------------------------------------------------------------------- diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/ExperienceTracker.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/ExperienceTracker.h index a275b24b11..671183d092 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/ExperienceTracker.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/ExperienceTracker.h @@ -46,6 +46,8 @@ class ExperienceTracker : public MemoryPoolObject, public Snapshot Int getExperienceValue( const Object* killer ) const; ///< How much do give for being killed Int getCurrentExperience( void ) const { return m_currentExperience; }; ///< How much experience do I have at the moment? Bool isTrainable() const; ///< Can I gain experience? + void setTrainable(Bool trainable); ///< Set whether I can gain experience + void resetTrainable(); ///< Set to default trainable state from template Bool isAcceptingExperiencePoints() const; ///< Either I am trainable, or I have a Sink set up void setVeterancyLevel( VeterancyLevel newLevel, Bool provideFeedback = TRUE ); ///< Set Level to this @@ -71,4 +73,5 @@ class ExperienceTracker : public MemoryPoolObject, public Snapshot Int m_currentExperience; ///< Number of experience points ObjectID m_experienceSink; ///< ID of object I have pledged my experience point gains to Real m_experienceScalar; ///< Scales any experience gained by this multiplier. + Bool m_isTrainable; ///< Can I gain experience? }; diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp index c4056a6c92..653756fa31 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp @@ -4499,47 +4499,46 @@ GameMessageDisposition CommandTranslator::translateGameMessage(const GameMessage case GameMessage::MSG_META_DEBUG_GIVE_VETERANCY: case GameMessage::MSG_META_DEBUG_TAKE_VETERANCY: { - if ( !TheGameLogic->isInMultiplayerGame() ) + if (TheGameLogic->isInMultiplayerGame()) + break; + + const DrawableList *list = TheInGameUI->getAllSelectedDrawables(); + for (DrawableListCIt it = list->begin(); it != list->end(); ++it) { + Drawable *pDraw = *it; + if (!pDraw) + continue; - const DrawableList *list = TheInGameUI->getAllSelectedDrawables(); - for (DrawableListCIt it = list->begin(); it != list->end(); ++it) + Object *pObject = pDraw->getObject(); + if (!pObject) + continue; + + ExperienceTracker *et = pObject->getExperienceTracker(); + if (!et || !et->isTrainable()) + continue; + + VeterancyLevel oldVet = et->getVeterancyLevel(); + VeterancyLevel newVet = oldVet; + + if (t == GameMessage::MSG_META_DEBUG_GIVE_VETERANCY) { - Drawable *pDraw = *it; - if (pDraw) + if (oldVet < LEVEL_LAST) { - Object *pObject = pDraw->getObject(); - if (pObject) - { - ExperienceTracker *et = pObject->getExperienceTracker(); - if (et) - { - if (et->isTrainable()) - { - VeterancyLevel oldVet = et->getVeterancyLevel(); - VeterancyLevel newVet = oldVet; - if (t == GameMessage::MSG_META_DEBUG_GIVE_VETERANCY) - { - if (oldVet < LEVEL_LAST) - { - newVet = (VeterancyLevel)((Int)oldVet + 1); - } - } - else - { - if (oldVet > LEVEL_FIRST) - { - newVet = (VeterancyLevel)((Int)oldVet - 1); - } - } - et->setVeterancyLevel(newVet); - } - } - } + newVet = (VeterancyLevel)((Int)oldVet + 1); } } - disp = DESTROY_MESSAGE; + else + { + if (oldVet > LEVEL_FIRST) + { + newVet = (VeterancyLevel)((Int)oldVet - 1); + } + } + + et->setVeterancyLevel(newVet); } + + disp = DESTROY_MESSAGE; break; } diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/RiderChangeContain.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/RiderChangeContain.cpp index ed9babbea3..563071c09d 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/RiderChangeContain.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/RiderChangeContain.cpp @@ -246,6 +246,11 @@ void RiderChangeContain::onContaining( Object *rider, Bool wasSelected ) //Transfer experience from the rider to the bike. ExperienceTracker *riderTracker = rider->getExperienceTracker(); ExperienceTracker *bikeTracker = obj->getExperienceTracker(); +#if !RETAIL_COMPATIBLE_CRC + // TheSuperHackers @bugfix Stubbjax 15/12/2025 Copy trainable flag from the rider to prevent + // Workers and other untrainable riders from ranking up via the bike's experience tracker. + bikeTracker->setTrainable(riderTracker->isTrainable()); +#endif bikeTracker->setVeterancyLevel( riderTracker->getVeterancyLevel(), FALSE ); riderTracker->setExperienceAndLevel( 0, FALSE ); @@ -301,6 +306,7 @@ void RiderChangeContain::onRemoving( Object *rider ) //Transfer experience from the bike to the rider. ExperienceTracker *riderTracker = rider->getExperienceTracker(); ExperienceTracker *bikeTracker = bike->getExperienceTracker(); + bikeTracker->resetTrainable(); riderTracker->setVeterancyLevel( bikeTracker->getVeterancyLevel(), FALSE ); bikeTracker->setExperienceAndLevel( 0, FALSE ); } diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/ExperienceTracker.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/ExperienceTracker.cpp index d589e30d33..21364efd90 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/ExperienceTracker.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/ExperienceTracker.cpp @@ -45,6 +45,7 @@ ExperienceTracker::ExperienceTracker(Object *parent) : m_experienceScalar( 1.0f ), m_currentExperience(0) { + resetTrainable(); } //------------------------------------------------------------------------------------------------- @@ -65,7 +66,19 @@ Int ExperienceTracker::getExperienceValue( const Object* killer ) const //------------------------------------------------------------------------------------------------- Bool ExperienceTracker::isTrainable() const { - return m_parent->getTemplate()->isTrainable(); + return m_isTrainable; +} + +//------------------------------------------------------------------------------------------------- +void ExperienceTracker::setTrainable(Bool trainable) +{ + m_isTrainable = trainable; +} + +//------------------------------------------------------------------------------------------------- +void ExperienceTracker::resetTrainable() +{ + m_isTrainable = m_parent->getTemplate()->isTrainable(); } //------------------------------------------------------------------------------------------------- @@ -236,19 +249,27 @@ void ExperienceTracker::crc( Xfer *xfer ) { xfer->xferInt( &m_currentExperience ); xfer->xferUser( &m_currentLevel, sizeof( VeterancyLevel ) ); +#if !RETAIL_COMPATIBLE_CRC + xfer->xferBool(&m_isTrainable); +#endif } //----------------------------------------------------------------------------- /** Xfer method * Version Info: * 1: Initial version + * 2: TheSuperHackers @tweak Serialize m_isTrainable */ // ---------------------------------------------------------------------------- void ExperienceTracker::xfer( Xfer *xfer ) { // version +#if RETAIL_COMPATIBLE_XFER_SAVE XferVersion currentVersion = 1; +#else + XferVersion currentVersion = 2; +#endif XferVersion version = currentVersion; xfer->xferVersion( &version, currentVersion ); @@ -267,6 +288,8 @@ void ExperienceTracker::xfer( Xfer *xfer ) // experience scalar xfer->xferReal( &m_experienceScalar ); + if (version >= 2) + xfer->xferBool(&m_isTrainable); } //-----------------------------------------------------------------------------