From eb7e1309877105e846daab9431b9e0bb205fa1b1 Mon Sep 17 00:00:00 2001 From: Stubbjax Date: Wed, 10 Dec 2025 18:35:49 +1100 Subject: [PATCH 01/11] refactor: Reduce debug veterancy message nesting --- .../GameClient/MessageStream/CommandXlat.cpp | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp index c4056a6c92..39f5774c87 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp @@ -4499,47 +4499,47 @@ 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) + const DrawableList *list = TheInGameUI->getAllSelectedDrawables(); + for (DrawableListCIt it = list->begin(); it != list->end(); ++it) + { + Drawable *pDraw = *it; + if (pDraw) { - Drawable *pDraw = *it; - if (pDraw) + Object *pObject = pDraw->getObject(); + if (pObject) { - Object *pObject = pDraw->getObject(); - if (pObject) + ExperienceTracker *et = pObject->getExperienceTracker(); + if (et) { - ExperienceTracker *et = pObject->getExperienceTracker(); - if (et) + if (et->isTrainable()) { - if (et->isTrainable()) + VeterancyLevel oldVet = et->getVeterancyLevel(); + VeterancyLevel newVet = oldVet; + if (t == GameMessage::MSG_META_DEBUG_GIVE_VETERANCY) { - VeterancyLevel oldVet = et->getVeterancyLevel(); - VeterancyLevel newVet = oldVet; - if (t == GameMessage::MSG_META_DEBUG_GIVE_VETERANCY) + if (oldVet < LEVEL_LAST) { - if (oldVet < LEVEL_LAST) - { - newVet = (VeterancyLevel)((Int)oldVet + 1); - } + newVet = (VeterancyLevel)((Int)oldVet + 1); } - else + } + else + { + if (oldVet > LEVEL_FIRST) { - if (oldVet > LEVEL_FIRST) - { - newVet = (VeterancyLevel)((Int)oldVet - 1); - } + newVet = (VeterancyLevel)((Int)oldVet - 1); } - et->setVeterancyLevel(newVet); } + et->setVeterancyLevel(newVet); } } } } - disp = DESTROY_MESSAGE; } + + disp = DESTROY_MESSAGE; break; } From 7d61b07f37991562aea660721565b96f5c09fd38 Mon Sep 17 00:00:00 2001 From: Stubbjax Date: Wed, 10 Dec 2025 18:36:52 +1100 Subject: [PATCH 02/11] unify: Apply single-player condition to veterancy debug messages in Generals --- .../Source/GameClient/MessageStream/CommandXlat.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Generals/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp b/Generals/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp index f0590f22b3..2409a9d2ee 100644 --- a/Generals/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp +++ b/Generals/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp @@ -4113,6 +4113,9 @@ 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) { @@ -4149,6 +4152,7 @@ GameMessageDisposition CommandTranslator::translateGameMessage(const GameMessage } } } + disp = DESTROY_MESSAGE; break; } From 0413b62736ee37b342d891faf1db13bed684f1ed Mon Sep 17 00:00:00 2001 From: Stubbjax Date: Wed, 10 Dec 2025 19:21:07 +1100 Subject: [PATCH 03/11] refactor: Further reduce debug veterancy message nesting --- .../GameClient/MessageStream/CommandXlat.cpp | 53 +++++++++---------- .../GameClient/MessageStream/CommandXlat.cpp | 53 +++++++++---------- 2 files changed, 52 insertions(+), 54 deletions(-) diff --git a/Generals/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp b/Generals/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp index 2409a9d2ee..35a406d462 100644 --- a/Generals/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp +++ b/Generals/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp @@ -4120,37 +4120,36 @@ GameMessageDisposition CommandTranslator::translateGameMessage(const GameMessage 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; diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp index 39f5774c87..653756fa31 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp @@ -4506,37 +4506,36 @@ GameMessageDisposition CommandTranslator::translateGameMessage(const GameMessage 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; From daf7f87852ebb8689f76b963ba1f41237c951649 Mon Sep 17 00:00:00 2001 From: Stubbjax Date: Wed, 10 Dec 2025 20:21:47 +1100 Subject: [PATCH 04/11] bugfix: Prevent ranking of riders that are not trainable --- .../GameClient/MessageStream/CommandXlat.cpp | 17 +++++++++++++++-- .../CrateCollide/SalvageCrateCollide.cpp | 14 ++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp index 653756fa31..d4468015e2 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp @@ -4513,8 +4513,21 @@ GameMessageDisposition CommandTranslator::translateGameMessage(const GameMessage if (!pObject) continue; - ExperienceTracker *et = pObject->getExperienceTracker(); - if (!et || !et->isTrainable()) + ExperienceTracker* et; + Bool isRiderTrainable = true; + + const ContainModuleInterface* contain = pObject->getContain(); + if (contain && contain->isRiderChangeContain()) + { + if (Object* rider = (Object*)contain->friend_getRider()) + { + et = rider->getExperienceTracker(); + isRiderTrainable = et && et->isTrainable(); + } + } + + et = pObject->getExperienceTracker(); + if (!et || !isRiderTrainable || !et->isTrainable()) continue; VeterancyLevel oldVet = et->getVeterancyLevel(); diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/SalvageCrateCollide.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/SalvageCrateCollide.cpp index 047cc1d6d8..f3b3443c9c 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/SalvageCrateCollide.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/SalvageCrateCollide.cpp @@ -42,6 +42,7 @@ #include "GameClient/GameText.h" #include "GameClient/InGameUI.h" #include "GameLogic/ExperienceTracker.h" +#include "GameLogic/Module/ContainModule.h" #include "GameLogic/Object.h" //------------------------------------------------------------------------------------------------- @@ -163,6 +164,19 @@ Bool SalvageCrateCollide::eligibleForLevel( Object *other ) if( !other->getExperienceTracker()->isTrainable() ) return FALSE; +#if !RETAIL_COMPATIBLE_CRC + const ContainModuleInterface* contain = other->getContain(); + if (contain && contain->isRiderChangeContain()) + { + if (Object* rider = (Object*)contain->friend_getRider()) + { + const ExperienceTracker* et = rider->getExperienceTracker(); + if (!et || !et->isTrainable()) + return FALSE; + } + } +#endif + return TRUE; } From 0d636271d5b6550dbec86a2937910bb4e9cd260d Mon Sep 17 00:00:00 2001 From: Stubbjax Date: Wed, 10 Dec 2025 21:54:48 +1100 Subject: [PATCH 05/11] docs: Add comments --- .../GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp | 1 + .../Object/Collide/CrateCollide/SalvageCrateCollide.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp index d4468015e2..b19b6e6db3 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp @@ -4516,6 +4516,7 @@ GameMessageDisposition CommandTranslator::translateGameMessage(const GameMessage ExperienceTracker* et; Bool isRiderTrainable = true; + // TheSuperHackers @bugfix Stubbjax 10/12/2025 Do not allow ranking if the rider is not trainable. const ContainModuleInterface* contain = pObject->getContain(); if (contain && contain->isRiderChangeContain()) { diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/SalvageCrateCollide.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/SalvageCrateCollide.cpp index f3b3443c9c..53f57db17b 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/SalvageCrateCollide.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/SalvageCrateCollide.cpp @@ -165,6 +165,7 @@ Bool SalvageCrateCollide::eligibleForLevel( Object *other ) return FALSE; #if !RETAIL_COMPATIBLE_CRC + // TheSuperHackers @bugfix Stubbjax 10/12/2025 Do not allow ranking if the rider is not trainable. const ContainModuleInterface* contain = other->getContain(); if (contain && contain->isRiderChangeContain()) { From fce6f1dc67a78e5e1d7cd21b0e2160f0086b9e67 Mon Sep 17 00:00:00 2001 From: Stubbjax Date: Thu, 11 Dec 2025 02:40:32 +1100 Subject: [PATCH 06/11] refactor: Abstract conditions into an object function --- .../Code/GameEngine/Include/GameLogic/Object.h | 1 + .../GameClient/MessageStream/CommandXlat.cpp | 2 +- .../CrateCollide/SalvageCrateCollide.cpp | 2 +- .../CrateCollide/VeterancyCrateCollide.cpp | 2 +- .../Source/GameLogic/Object/Object.cpp | 16 ++++++++++++++++ .../Code/GameEngine/Include/GameLogic/Object.h | 1 + .../GameClient/MessageStream/CommandXlat.cpp | 18 ++---------------- .../CrateCollide/SalvageCrateCollide.cpp | 17 +---------------- .../CrateCollide/VeterancyCrateCollide.cpp | 2 +- .../Source/GameLogic/Object/Object.cpp | 16 ++++++++++++++++ 10 files changed, 41 insertions(+), 36 deletions(-) diff --git a/Generals/Code/GameEngine/Include/GameLogic/Object.h b/Generals/Code/GameEngine/Include/GameLogic/Object.h index 49617d3fc0..96bf1db3df 100644 --- a/Generals/Code/GameEngine/Include/GameLogic/Object.h +++ b/Generals/Code/GameEngine/Include/GameLogic/Object.h @@ -216,6 +216,7 @@ class Object : public Thing, public Snapshot ExperienceTracker* getExperienceTracker() {return m_experienceTracker;} const ExperienceTracker* getExperienceTracker() const {return m_experienceTracker;} VeterancyLevel getVeterancyLevel() const; + Bool isTrainable() const; //< Can this unit - or its rider - gain veterancy? inline const AsciiString& getName() const { return m_name; } inline void setName( const AsciiString& newName ) { m_name = newName; } diff --git a/Generals/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp b/Generals/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp index 35a406d462..0542862ac2 100644 --- a/Generals/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp +++ b/Generals/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp @@ -4128,7 +4128,7 @@ GameMessageDisposition CommandTranslator::translateGameMessage(const GameMessage continue; ExperienceTracker *et = pObject->getExperienceTracker(); - if (!et || !et->isTrainable()) + if (!et || !pObject->isTrainable()) continue; VeterancyLevel oldVet = et->getVeterancyLevel(); diff --git a/Generals/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/SalvageCrateCollide.cpp b/Generals/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/SalvageCrateCollide.cpp index 3ba3e04856..d28b3f8117 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/SalvageCrateCollide.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/SalvageCrateCollide.cpp @@ -135,7 +135,7 @@ Bool SalvageCrateCollide::eligibleForLevel( Object *other ) return FALSE; // Sorry, you can't gain levels - if( !other->getExperienceTracker()->isTrainable() ) + if (!other->isTrainable()) return FALSE; return TRUE; diff --git a/Generals/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/VeterancyCrateCollide.cpp b/Generals/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/VeterancyCrateCollide.cpp index 427271044f..e5138710bf 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/VeterancyCrateCollide.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/VeterancyCrateCollide.cpp @@ -90,7 +90,7 @@ Bool VeterancyCrateCollide::isValidToExecute( const Object *other ) const return false; const ExperienceTracker *et = other->getExperienceTracker(); - if( !et || !et->isTrainable() ) + if (!et || !other->isTrainable()) { //If the other unit can't gain experience, then we can't help promote it! return false; diff --git a/Generals/Code/GameEngine/Source/GameLogic/Object/Object.cpp b/Generals/Code/GameEngine/Source/GameLogic/Object/Object.cpp index d0f4b52301..a42ba608b2 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/Object/Object.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/Object/Object.cpp @@ -2694,6 +2694,22 @@ VeterancyLevel Object::getVeterancyLevel() const return m_experienceTracker ? m_experienceTracker->getVeterancyLevel() : LEVEL_REGULAR; } +//------------------------------------------------------------------------------------------------- +Bool Object::isTrainable() const +{ + const ExperienceTracker* expTracker = m_experienceTracker; + +#if !RETAIL_COMPATIBLE_CRC + if (m_contain) + { + if (const Object* rider = m_contain->friend_getRider()) + expTracker = rider->getExperienceTracker(); + } +#endif + + return expTracker && expTracker->isTrainable(); +} + //------------------------------------------------------------------------------------------------- void Object::friend_bindToDrawable( Drawable *draw ) { diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h index a2f51fe28b..6ef23f97c6 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h @@ -232,6 +232,7 @@ class Object : public Thing, public Snapshot ExperienceTracker* getExperienceTracker() {return m_experienceTracker;} const ExperienceTracker* getExperienceTracker() const {return m_experienceTracker;} VeterancyLevel getVeterancyLevel() const; + Bool isTrainable() const; //< Can this unit - or its rider - gain veterancy? inline const AsciiString& getName() const { return m_name; } inline void setName( const AsciiString& newName ) { m_name = newName; } diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp index b19b6e6db3..ca6aee6983 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp @@ -4513,22 +4513,8 @@ GameMessageDisposition CommandTranslator::translateGameMessage(const GameMessage if (!pObject) continue; - ExperienceTracker* et; - Bool isRiderTrainable = true; - - // TheSuperHackers @bugfix Stubbjax 10/12/2025 Do not allow ranking if the rider is not trainable. - const ContainModuleInterface* contain = pObject->getContain(); - if (contain && contain->isRiderChangeContain()) - { - if (Object* rider = (Object*)contain->friend_getRider()) - { - et = rider->getExperienceTracker(); - isRiderTrainable = et && et->isTrainable(); - } - } - - et = pObject->getExperienceTracker(); - if (!et || !isRiderTrainable || !et->isTrainable()) + ExperienceTracker *et = pObject->getExperienceTracker(); + if (!et || !pObject->isTrainable()) continue; VeterancyLevel oldVet = et->getVeterancyLevel(); diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/SalvageCrateCollide.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/SalvageCrateCollide.cpp index 53f57db17b..0c176f204b 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/SalvageCrateCollide.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/SalvageCrateCollide.cpp @@ -42,7 +42,6 @@ #include "GameClient/GameText.h" #include "GameClient/InGameUI.h" #include "GameLogic/ExperienceTracker.h" -#include "GameLogic/Module/ContainModule.h" #include "GameLogic/Object.h" //------------------------------------------------------------------------------------------------- @@ -161,23 +160,9 @@ Bool SalvageCrateCollide::eligibleForLevel( Object *other ) return FALSE; // Sorry, you can't gain levels - if( !other->getExperienceTracker()->isTrainable() ) + if (!other->isTrainable()) return FALSE; -#if !RETAIL_COMPATIBLE_CRC - // TheSuperHackers @bugfix Stubbjax 10/12/2025 Do not allow ranking if the rider is not trainable. - const ContainModuleInterface* contain = other->getContain(); - if (contain && contain->isRiderChangeContain()) - { - if (Object* rider = (Object*)contain->friend_getRider()) - { - const ExperienceTracker* et = rider->getExperienceTracker(); - if (!et || !et->isTrainable()) - return FALSE; - } - } -#endif - return TRUE; } diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/VeterancyCrateCollide.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/VeterancyCrateCollide.cpp index 0c35d459a9..9053f5f19b 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/VeterancyCrateCollide.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/VeterancyCrateCollide.cpp @@ -90,7 +90,7 @@ Bool VeterancyCrateCollide::isValidToExecute( const Object *other ) const return false; const ExperienceTracker *et = other->getExperienceTracker(); - if( !et || !et->isTrainable() ) + if (!et || !other->isTrainable()) { //If the other unit can't gain experience, then we can't help promote it! return false; diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp index 598e266bea..588a5ee603 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp @@ -2988,6 +2988,22 @@ VeterancyLevel Object::getVeterancyLevel() const return m_experienceTracker ? m_experienceTracker->getVeterancyLevel() : LEVEL_REGULAR; } +//------------------------------------------------------------------------------------------------- +Bool Object::isTrainable() const +{ + const ExperienceTracker* expTracker = m_experienceTracker; + +#if !RETAIL_COMPATIBLE_CRC + if (m_contain && m_contain->isRiderChangeContain()) + { + if (const Object* rider = m_contain->friend_getRider()) + expTracker = rider->getExperienceTracker(); + } +#endif + + return expTracker && expTracker->isTrainable(); +} + //------------------------------------------------------------------------------------------------- void Object::friend_bindToDrawable( Drawable *draw ) { From fca2191ec59c71dc2369a25ee503be768214497d Mon Sep 17 00:00:00 2001 From: Stubbjax Date: Sat, 13 Dec 2025 12:07:17 +1100 Subject: [PATCH 07/11] docs: Streamline comment style --- Generals/Code/GameEngine/Include/GameLogic/Object.h | 2 +- GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Generals/Code/GameEngine/Include/GameLogic/Object.h b/Generals/Code/GameEngine/Include/GameLogic/Object.h index 96bf1db3df..a1cf9012d7 100644 --- a/Generals/Code/GameEngine/Include/GameLogic/Object.h +++ b/Generals/Code/GameEngine/Include/GameLogic/Object.h @@ -216,7 +216,7 @@ class Object : public Thing, public Snapshot ExperienceTracker* getExperienceTracker() {return m_experienceTracker;} const ExperienceTracker* getExperienceTracker() const {return m_experienceTracker;} VeterancyLevel getVeterancyLevel() const; - Bool isTrainable() const; //< Can this unit - or its rider - gain veterancy? + Bool isTrainable() const; ///< Can this unit - or its rider - gain veterancy? inline const AsciiString& getName() const { return m_name; } inline void setName( const AsciiString& newName ) { m_name = newName; } diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h index 6ef23f97c6..da4eb7266b 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h @@ -232,7 +232,7 @@ class Object : public Thing, public Snapshot ExperienceTracker* getExperienceTracker() {return m_experienceTracker;} const ExperienceTracker* getExperienceTracker() const {return m_experienceTracker;} VeterancyLevel getVeterancyLevel() const; - Bool isTrainable() const; //< Can this unit - or its rider - gain veterancy? + Bool isTrainable() const; ///< Can this unit - or its rider - gain veterancy? inline const AsciiString& getName() const { return m_name; } inline void setName( const AsciiString& newName ) { m_name = newName; } From 1c9bef0bf8c38a4f6b3343865745fa16ced8bf4f Mon Sep 17 00:00:00 2001 From: Stubbjax Date: Sat, 13 Dec 2025 12:08:52 +1100 Subject: [PATCH 08/11] tweak: Minor optimisation --- .../Object/Collide/CrateCollide/VeterancyCrateCollide.cpp | 4 ++-- .../Object/Collide/CrateCollide/VeterancyCrateCollide.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Generals/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/VeterancyCrateCollide.cpp b/Generals/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/VeterancyCrateCollide.cpp index e5138710bf..4ab692ca61 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/VeterancyCrateCollide.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/VeterancyCrateCollide.cpp @@ -89,13 +89,13 @@ Bool VeterancyCrateCollide::isValidToExecute( const Object *other ) const if (levelsToGain <= 0) return false; - const ExperienceTracker *et = other->getExperienceTracker(); - if (!et || !other->isTrainable()) + if (!other->isTrainable()) { //If the other unit can't gain experience, then we can't help promote it! return false; } + const ExperienceTracker* et = other->getExperienceTracker(); if (!et || !et->canGainExpForLevel(levelsToGain)) return false; diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/VeterancyCrateCollide.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/VeterancyCrateCollide.cpp index 9053f5f19b..0639253d48 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/VeterancyCrateCollide.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/VeterancyCrateCollide.cpp @@ -89,13 +89,13 @@ Bool VeterancyCrateCollide::isValidToExecute( const Object *other ) const if (levelsToGain <= 0) return false; - const ExperienceTracker *et = other->getExperienceTracker(); - if (!et || !other->isTrainable()) + if (!other->isTrainable()) { //If the other unit can't gain experience, then we can't help promote it! return false; } + const ExperienceTracker* et = other->getExperienceTracker(); if (!et || !et->canGainExpForLevel(levelsToGain)) return false; From baa7a1ff975a0cb578cfb9109a3657b8f0ddf534 Mon Sep 17 00:00:00 2001 From: Stubbjax Date: Sat, 13 Dec 2025 15:34:02 +1100 Subject: [PATCH 09/11] refactor: Transfer trainable flag on rider change --- .../Include/GameLogic/ExperienceTracker.h | 3 +++ .../GameLogic/Object/ExperienceTracker.cpp | 24 ++++++++++++++++++- .../Include/GameLogic/ExperienceTracker.h | 3 +++ .../Object/Contain/RiderChangeContain.cpp | 4 ++++ .../GameLogic/Object/ExperienceTracker.cpp | 24 ++++++++++++++++++- 5 files changed, 56 insertions(+), 2 deletions(-) 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/GameLogic/Object/ExperienceTracker.cpp b/Generals/Code/GameEngine/Source/GameLogic/Object/ExperienceTracker.cpp index f6a1b05032..8bcd59de12 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,6 +249,9 @@ void ExperienceTracker::crc( Xfer *xfer ) { xfer->xferInt( &m_currentExperience ); xfer->xferUser( &m_currentLevel, sizeof( VeterancyLevel ) ); +#if !RETAIL_COMPATIBLE_CRC + xfer->xferBool(&m_isTrainable); +#endif } //----------------------------------------------------------------------------- @@ -248,7 +264,11 @@ 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 +287,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/GameLogic/Object/Contain/RiderChangeContain.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/RiderChangeContain.cpp index ed9babbea3..180efc9f73 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/RiderChangeContain.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/RiderChangeContain.cpp @@ -246,6 +246,9 @@ 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 + bikeTracker->setTrainable(riderTracker->isTrainable()); +#endif bikeTracker->setVeterancyLevel( riderTracker->getVeterancyLevel(), FALSE ); riderTracker->setExperienceAndLevel( 0, FALSE ); @@ -301,6 +304,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..2fbbe40e86 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,6 +249,9 @@ void ExperienceTracker::crc( Xfer *xfer ) { xfer->xferInt( &m_currentExperience ); xfer->xferUser( &m_currentLevel, sizeof( VeterancyLevel ) ); +#if !RETAIL_COMPATIBLE_CRC + xfer->xferBool(&m_isTrainable); +#endif } //----------------------------------------------------------------------------- @@ -248,7 +264,11 @@ 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 +287,8 @@ void ExperienceTracker::xfer( Xfer *xfer ) // experience scalar xfer->xferReal( &m_experienceScalar ); + if (version >= 2) + xfer->xferBool(&m_isTrainable); } //----------------------------------------------------------------------------- From 0eb2e408dc6380ecf0c99783d66f2e8c001e7718 Mon Sep 17 00:00:00 2001 From: Stubbjax Date: Sat, 13 Dec 2025 16:35:48 +1100 Subject: [PATCH 10/11] refactor: Revert previous solution --- .../Code/GameEngine/Include/GameLogic/Object.h | 1 - .../GameClient/MessageStream/CommandXlat.cpp | 2 +- .../Collide/CrateCollide/SalvageCrateCollide.cpp | 2 +- .../CrateCollide/VeterancyCrateCollide.cpp | 4 ++-- .../Source/GameLogic/Object/Object.cpp | 16 ---------------- .../Code/GameEngine/Include/GameLogic/Object.h | 1 - .../GameClient/MessageStream/CommandXlat.cpp | 2 +- .../Collide/CrateCollide/SalvageCrateCollide.cpp | 2 +- .../CrateCollide/VeterancyCrateCollide.cpp | 4 ++-- .../Source/GameLogic/Object/Object.cpp | 16 ---------------- 10 files changed, 8 insertions(+), 42 deletions(-) diff --git a/Generals/Code/GameEngine/Include/GameLogic/Object.h b/Generals/Code/GameEngine/Include/GameLogic/Object.h index a1cf9012d7..49617d3fc0 100644 --- a/Generals/Code/GameEngine/Include/GameLogic/Object.h +++ b/Generals/Code/GameEngine/Include/GameLogic/Object.h @@ -216,7 +216,6 @@ class Object : public Thing, public Snapshot ExperienceTracker* getExperienceTracker() {return m_experienceTracker;} const ExperienceTracker* getExperienceTracker() const {return m_experienceTracker;} VeterancyLevel getVeterancyLevel() const; - Bool isTrainable() const; ///< Can this unit - or its rider - gain veterancy? inline const AsciiString& getName() const { return m_name; } inline void setName( const AsciiString& newName ) { m_name = newName; } diff --git a/Generals/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp b/Generals/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp index 0542862ac2..35a406d462 100644 --- a/Generals/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp +++ b/Generals/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp @@ -4128,7 +4128,7 @@ GameMessageDisposition CommandTranslator::translateGameMessage(const GameMessage continue; ExperienceTracker *et = pObject->getExperienceTracker(); - if (!et || !pObject->isTrainable()) + if (!et || !et->isTrainable()) continue; VeterancyLevel oldVet = et->getVeterancyLevel(); diff --git a/Generals/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/SalvageCrateCollide.cpp b/Generals/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/SalvageCrateCollide.cpp index d28b3f8117..3ba3e04856 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/SalvageCrateCollide.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/SalvageCrateCollide.cpp @@ -135,7 +135,7 @@ Bool SalvageCrateCollide::eligibleForLevel( Object *other ) return FALSE; // Sorry, you can't gain levels - if (!other->isTrainable()) + if( !other->getExperienceTracker()->isTrainable() ) return FALSE; return TRUE; diff --git a/Generals/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/VeterancyCrateCollide.cpp b/Generals/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/VeterancyCrateCollide.cpp index 4ab692ca61..427271044f 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/VeterancyCrateCollide.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/VeterancyCrateCollide.cpp @@ -89,13 +89,13 @@ Bool VeterancyCrateCollide::isValidToExecute( const Object *other ) const if (levelsToGain <= 0) return false; - if (!other->isTrainable()) + const ExperienceTracker *et = other->getExperienceTracker(); + if( !et || !et->isTrainable() ) { //If the other unit can't gain experience, then we can't help promote it! return false; } - const ExperienceTracker* et = other->getExperienceTracker(); if (!et || !et->canGainExpForLevel(levelsToGain)) return false; diff --git a/Generals/Code/GameEngine/Source/GameLogic/Object/Object.cpp b/Generals/Code/GameEngine/Source/GameLogic/Object/Object.cpp index a42ba608b2..d0f4b52301 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/Object/Object.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/Object/Object.cpp @@ -2694,22 +2694,6 @@ VeterancyLevel Object::getVeterancyLevel() const return m_experienceTracker ? m_experienceTracker->getVeterancyLevel() : LEVEL_REGULAR; } -//------------------------------------------------------------------------------------------------- -Bool Object::isTrainable() const -{ - const ExperienceTracker* expTracker = m_experienceTracker; - -#if !RETAIL_COMPATIBLE_CRC - if (m_contain) - { - if (const Object* rider = m_contain->friend_getRider()) - expTracker = rider->getExperienceTracker(); - } -#endif - - return expTracker && expTracker->isTrainable(); -} - //------------------------------------------------------------------------------------------------- void Object::friend_bindToDrawable( Drawable *draw ) { diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h index da4eb7266b..a2f51fe28b 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h @@ -232,7 +232,6 @@ class Object : public Thing, public Snapshot ExperienceTracker* getExperienceTracker() {return m_experienceTracker;} const ExperienceTracker* getExperienceTracker() const {return m_experienceTracker;} VeterancyLevel getVeterancyLevel() const; - Bool isTrainable() const; ///< Can this unit - or its rider - gain veterancy? inline const AsciiString& getName() const { return m_name; } inline void setName( const AsciiString& newName ) { m_name = newName; } diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp index ca6aee6983..653756fa31 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp @@ -4514,7 +4514,7 @@ GameMessageDisposition CommandTranslator::translateGameMessage(const GameMessage continue; ExperienceTracker *et = pObject->getExperienceTracker(); - if (!et || !pObject->isTrainable()) + if (!et || !et->isTrainable()) continue; VeterancyLevel oldVet = et->getVeterancyLevel(); diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/SalvageCrateCollide.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/SalvageCrateCollide.cpp index 0c176f204b..047cc1d6d8 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/SalvageCrateCollide.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/SalvageCrateCollide.cpp @@ -160,7 +160,7 @@ Bool SalvageCrateCollide::eligibleForLevel( Object *other ) return FALSE; // Sorry, you can't gain levels - if (!other->isTrainable()) + if( !other->getExperienceTracker()->isTrainable() ) return FALSE; return TRUE; diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/VeterancyCrateCollide.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/VeterancyCrateCollide.cpp index 0639253d48..0c35d459a9 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/VeterancyCrateCollide.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Collide/CrateCollide/VeterancyCrateCollide.cpp @@ -89,13 +89,13 @@ Bool VeterancyCrateCollide::isValidToExecute( const Object *other ) const if (levelsToGain <= 0) return false; - if (!other->isTrainable()) + const ExperienceTracker *et = other->getExperienceTracker(); + if( !et || !et->isTrainable() ) { //If the other unit can't gain experience, then we can't help promote it! return false; } - const ExperienceTracker* et = other->getExperienceTracker(); if (!et || !et->canGainExpForLevel(levelsToGain)) return false; diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp index 588a5ee603..598e266bea 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp @@ -2988,22 +2988,6 @@ VeterancyLevel Object::getVeterancyLevel() const return m_experienceTracker ? m_experienceTracker->getVeterancyLevel() : LEVEL_REGULAR; } -//------------------------------------------------------------------------------------------------- -Bool Object::isTrainable() const -{ - const ExperienceTracker* expTracker = m_experienceTracker; - -#if !RETAIL_COMPATIBLE_CRC - if (m_contain && m_contain->isRiderChangeContain()) - { - if (const Object* rider = m_contain->friend_getRider()) - expTracker = rider->getExperienceTracker(); - } -#endif - - return expTracker && expTracker->isTrainable(); -} - //------------------------------------------------------------------------------------------------- void Object::friend_bindToDrawable( Drawable *draw ) { From 26e4def05724db92c9512fbde4c0d811f1ad35bc Mon Sep 17 00:00:00 2001 From: Stubbjax Date: Wed, 17 Dec 2025 20:55:04 +1100 Subject: [PATCH 11/11] docs: Add comments --- .../GameEngine/Source/GameLogic/Object/ExperienceTracker.cpp | 1 + .../Source/GameLogic/Object/Contain/RiderChangeContain.cpp | 2 ++ .../GameEngine/Source/GameLogic/Object/ExperienceTracker.cpp | 1 + 3 files changed, 4 insertions(+) diff --git a/Generals/Code/GameEngine/Source/GameLogic/Object/ExperienceTracker.cpp b/Generals/Code/GameEngine/Source/GameLogic/Object/ExperienceTracker.cpp index 8bcd59de12..2518b38a34 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/Object/ExperienceTracker.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/Object/ExperienceTracker.cpp @@ -258,6 +258,7 @@ void ExperienceTracker::crc( Xfer *xfer ) /** Xfer method * Version Info: * 1: Initial version + * 2: TheSuperHackers @tweak Serialize m_isTrainable */ // ---------------------------------------------------------------------------- void ExperienceTracker::xfer( Xfer *xfer ) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/RiderChangeContain.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/RiderChangeContain.cpp index 180efc9f73..563071c09d 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/RiderChangeContain.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/RiderChangeContain.cpp @@ -247,6 +247,8 @@ void RiderChangeContain::onContaining( Object *rider, Bool wasSelected ) 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 ); diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/ExperienceTracker.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/ExperienceTracker.cpp index 2fbbe40e86..21364efd90 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/ExperienceTracker.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/ExperienceTracker.cpp @@ -258,6 +258,7 @@ void ExperienceTracker::crc( Xfer *xfer ) /** Xfer method * Version Info: * 1: Initial version + * 2: TheSuperHackers @tweak Serialize m_isTrainable */ // ---------------------------------------------------------------------------- void ExperienceTracker::xfer( Xfer *xfer )