Skip to content

Commit 4951762

Browse files
authored
bugfix(experience): Prevent ranking of riders that are not trainable (#1970)
1 parent 123492c commit 4951762

File tree

7 files changed

+123
-63
lines changed

7 files changed

+123
-63
lines changed

Generals/Code/GameEngine/Include/GameLogic/ExperienceTracker.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ class ExperienceTracker : public MemoryPoolObject, public Snapshot
4646
Int getExperienceValue( const Object* killer ) const; ///< How much do give for being killed
4747
Int getCurrentExperience( void ) const { return m_currentExperience; }; ///< How much experience do I have at the moment?
4848
Bool isTrainable() const; ///< Can I gain experience?
49+
void setTrainable(Bool trainable); ///< Set whether I can gain experience
50+
void resetTrainable(); ///< Set to default trainable state from template
4951
Bool isAcceptingExperiencePoints() const; ///< Either I am trainable, or I have a Sink set up
5052

5153
void setVeterancyLevel( VeterancyLevel newLevel, Bool provideFeedback = TRUE ); ///< Set Level to this
@@ -71,4 +73,5 @@ class ExperienceTracker : public MemoryPoolObject, public Snapshot
7173
Int m_currentExperience; ///< Number of experience points
7274
ObjectID m_experienceSink; ///< ID of object I have pledged my experience point gains to
7375
Real m_experienceScalar; ///< Scales any experience gained by this multiplier.
76+
Bool m_isTrainable; ///< Can I gain experience?
7477
};

Generals/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp

Lines changed: 30 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4113,42 +4113,45 @@ GameMessageDisposition CommandTranslator::translateGameMessage(const GameMessage
41134113
case GameMessage::MSG_META_DEBUG_GIVE_VETERANCY:
41144114
case GameMessage::MSG_META_DEBUG_TAKE_VETERANCY:
41154115
{
4116+
if (TheGameLogic->isInMultiplayerGame())
4117+
break;
4118+
41164119
const DrawableList *list = TheInGameUI->getAllSelectedDrawables();
41174120
for (DrawableListCIt it = list->begin(); it != list->end(); ++it)
41184121
{
41194122
Drawable *pDraw = *it;
4120-
if (pDraw)
4123+
if (!pDraw)
4124+
continue;
4125+
4126+
Object *pObject = pDraw->getObject();
4127+
if (!pObject)
4128+
continue;
4129+
4130+
ExperienceTracker *et = pObject->getExperienceTracker();
4131+
if (!et || !et->isTrainable())
4132+
continue;
4133+
4134+
VeterancyLevel oldVet = et->getVeterancyLevel();
4135+
VeterancyLevel newVet = oldVet;
4136+
4137+
if (t == GameMessage::MSG_META_DEBUG_GIVE_VETERANCY)
41214138
{
4122-
Object *pObject = pDraw->getObject();
4123-
if (pObject)
4139+
if (oldVet < LEVEL_LAST)
41244140
{
4125-
ExperienceTracker *et = pObject->getExperienceTracker();
4126-
if (et)
4127-
{
4128-
if (et->isTrainable())
4129-
{
4130-
VeterancyLevel oldVet = et->getVeterancyLevel();
4131-
VeterancyLevel newVet = oldVet;
4132-
if (t == GameMessage::MSG_META_DEBUG_GIVE_VETERANCY)
4133-
{
4134-
if (oldVet < LEVEL_LAST)
4135-
{
4136-
newVet = (VeterancyLevel)((Int)oldVet + 1);
4137-
}
4138-
}
4139-
else
4140-
{
4141-
if (oldVet > LEVEL_FIRST)
4142-
{
4143-
newVet = (VeterancyLevel)((Int)oldVet - 1);
4144-
}
4145-
}
4146-
et->setVeterancyLevel(newVet);
4147-
}
4148-
}
4141+
newVet = (VeterancyLevel)((Int)oldVet + 1);
41494142
}
41504143
}
4144+
else
4145+
{
4146+
if (oldVet > LEVEL_FIRST)
4147+
{
4148+
newVet = (VeterancyLevel)((Int)oldVet - 1);
4149+
}
4150+
}
4151+
4152+
et->setVeterancyLevel(newVet);
41514153
}
4154+
41524155
disp = DESTROY_MESSAGE;
41534156
break;
41544157
}

Generals/Code/GameEngine/Source/GameLogic/Object/ExperienceTracker.cpp

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ ExperienceTracker::ExperienceTracker(Object *parent) :
4545
m_experienceScalar( 1.0f ),
4646
m_currentExperience(0)
4747
{
48+
resetTrainable();
4849
}
4950

5051
//-------------------------------------------------------------------------------------------------
@@ -65,7 +66,19 @@ Int ExperienceTracker::getExperienceValue( const Object* killer ) const
6566
//-------------------------------------------------------------------------------------------------
6667
Bool ExperienceTracker::isTrainable() const
6768
{
68-
return m_parent->getTemplate()->isTrainable();
69+
return m_isTrainable;
70+
}
71+
72+
//-------------------------------------------------------------------------------------------------
73+
void ExperienceTracker::setTrainable(Bool trainable)
74+
{
75+
m_isTrainable = trainable;
76+
}
77+
78+
//-------------------------------------------------------------------------------------------------
79+
void ExperienceTracker::resetTrainable()
80+
{
81+
m_isTrainable = m_parent->getTemplate()->isTrainable();
6982
}
7083

7184
//-------------------------------------------------------------------------------------------------
@@ -236,19 +249,27 @@ void ExperienceTracker::crc( Xfer *xfer )
236249
{
237250
xfer->xferInt( &m_currentExperience );
238251
xfer->xferUser( &m_currentLevel, sizeof( VeterancyLevel ) );
252+
#if !RETAIL_COMPATIBLE_CRC
253+
xfer->xferBool(&m_isTrainable);
254+
#endif
239255
}
240256

241257
//-----------------------------------------------------------------------------
242258
/** Xfer method
243259
* Version Info:
244260
* 1: Initial version
261+
* 2: TheSuperHackers @tweak Serialize m_isTrainable
245262
*/
246263
// ----------------------------------------------------------------------------
247264
void ExperienceTracker::xfer( Xfer *xfer )
248265
{
249266

250267
// version
268+
#if RETAIL_COMPATIBLE_XFER_SAVE
251269
XferVersion currentVersion = 1;
270+
#else
271+
XferVersion currentVersion = 2;
272+
#endif
252273
XferVersion version = currentVersion;
253274
xfer->xferVersion( &version, currentVersion );
254275

@@ -267,6 +288,8 @@ void ExperienceTracker::xfer( Xfer *xfer )
267288
// experience scalar
268289
xfer->xferReal( &m_experienceScalar );
269290

291+
if (version >= 2)
292+
xfer->xferBool(&m_isTrainable);
270293
}
271294

272295
//-----------------------------------------------------------------------------

GeneralsMD/Code/GameEngine/Include/GameLogic/ExperienceTracker.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ class ExperienceTracker : public MemoryPoolObject, public Snapshot
4646
Int getExperienceValue( const Object* killer ) const; ///< How much do give for being killed
4747
Int getCurrentExperience( void ) const { return m_currentExperience; }; ///< How much experience do I have at the moment?
4848
Bool isTrainable() const; ///< Can I gain experience?
49+
void setTrainable(Bool trainable); ///< Set whether I can gain experience
50+
void resetTrainable(); ///< Set to default trainable state from template
4951
Bool isAcceptingExperiencePoints() const; ///< Either I am trainable, or I have a Sink set up
5052

5153
void setVeterancyLevel( VeterancyLevel newLevel, Bool provideFeedback = TRUE ); ///< Set Level to this
@@ -71,4 +73,5 @@ class ExperienceTracker : public MemoryPoolObject, public Snapshot
7173
Int m_currentExperience; ///< Number of experience points
7274
ObjectID m_experienceSink; ///< ID of object I have pledged my experience point gains to
7375
Real m_experienceScalar; ///< Scales any experience gained by this multiplier.
76+
Bool m_isTrainable; ///< Can I gain experience?
7477
};

GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp

Lines changed: 33 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4499,47 +4499,46 @@ GameMessageDisposition CommandTranslator::translateGameMessage(const GameMessage
44994499
case GameMessage::MSG_META_DEBUG_GIVE_VETERANCY:
45004500
case GameMessage::MSG_META_DEBUG_TAKE_VETERANCY:
45014501
{
4502-
if ( !TheGameLogic->isInMultiplayerGame() )
4502+
if (TheGameLogic->isInMultiplayerGame())
4503+
break;
4504+
4505+
const DrawableList *list = TheInGameUI->getAllSelectedDrawables();
4506+
for (DrawableListCIt it = list->begin(); it != list->end(); ++it)
45034507
{
4508+
Drawable *pDraw = *it;
4509+
if (!pDraw)
4510+
continue;
45044511

4505-
const DrawableList *list = TheInGameUI->getAllSelectedDrawables();
4506-
for (DrawableListCIt it = list->begin(); it != list->end(); ++it)
4512+
Object *pObject = pDraw->getObject();
4513+
if (!pObject)
4514+
continue;
4515+
4516+
ExperienceTracker *et = pObject->getExperienceTracker();
4517+
if (!et || !et->isTrainable())
4518+
continue;
4519+
4520+
VeterancyLevel oldVet = et->getVeterancyLevel();
4521+
VeterancyLevel newVet = oldVet;
4522+
4523+
if (t == GameMessage::MSG_META_DEBUG_GIVE_VETERANCY)
45074524
{
4508-
Drawable *pDraw = *it;
4509-
if (pDraw)
4525+
if (oldVet < LEVEL_LAST)
45104526
{
4511-
Object *pObject = pDraw->getObject();
4512-
if (pObject)
4513-
{
4514-
ExperienceTracker *et = pObject->getExperienceTracker();
4515-
if (et)
4516-
{
4517-
if (et->isTrainable())
4518-
{
4519-
VeterancyLevel oldVet = et->getVeterancyLevel();
4520-
VeterancyLevel newVet = oldVet;
4521-
if (t == GameMessage::MSG_META_DEBUG_GIVE_VETERANCY)
4522-
{
4523-
if (oldVet < LEVEL_LAST)
4524-
{
4525-
newVet = (VeterancyLevel)((Int)oldVet + 1);
4526-
}
4527-
}
4528-
else
4529-
{
4530-
if (oldVet > LEVEL_FIRST)
4531-
{
4532-
newVet = (VeterancyLevel)((Int)oldVet - 1);
4533-
}
4534-
}
4535-
et->setVeterancyLevel(newVet);
4536-
}
4537-
}
4538-
}
4527+
newVet = (VeterancyLevel)((Int)oldVet + 1);
45394528
}
45404529
}
4541-
disp = DESTROY_MESSAGE;
4530+
else
4531+
{
4532+
if (oldVet > LEVEL_FIRST)
4533+
{
4534+
newVet = (VeterancyLevel)((Int)oldVet - 1);
4535+
}
4536+
}
4537+
4538+
et->setVeterancyLevel(newVet);
45424539
}
4540+
4541+
disp = DESTROY_MESSAGE;
45434542
break;
45444543
}
45454544

GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/RiderChangeContain.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,11 @@ void RiderChangeContain::onContaining( Object *rider, Bool wasSelected )
246246
//Transfer experience from the rider to the bike.
247247
ExperienceTracker *riderTracker = rider->getExperienceTracker();
248248
ExperienceTracker *bikeTracker = obj->getExperienceTracker();
249+
#if !RETAIL_COMPATIBLE_CRC
250+
// TheSuperHackers @bugfix Stubbjax 15/12/2025 Copy trainable flag from the rider to prevent
251+
// Workers and other untrainable riders from ranking up via the bike's experience tracker.
252+
bikeTracker->setTrainable(riderTracker->isTrainable());
253+
#endif
249254
bikeTracker->setVeterancyLevel( riderTracker->getVeterancyLevel(), FALSE );
250255
riderTracker->setExperienceAndLevel( 0, FALSE );
251256

@@ -301,6 +306,7 @@ void RiderChangeContain::onRemoving( Object *rider )
301306
//Transfer experience from the bike to the rider.
302307
ExperienceTracker *riderTracker = rider->getExperienceTracker();
303308
ExperienceTracker *bikeTracker = bike->getExperienceTracker();
309+
bikeTracker->resetTrainable();
304310
riderTracker->setVeterancyLevel( bikeTracker->getVeterancyLevel(), FALSE );
305311
bikeTracker->setExperienceAndLevel( 0, FALSE );
306312
}

GeneralsMD/Code/GameEngine/Source/GameLogic/Object/ExperienceTracker.cpp

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ ExperienceTracker::ExperienceTracker(Object *parent) :
4545
m_experienceScalar( 1.0f ),
4646
m_currentExperience(0)
4747
{
48+
resetTrainable();
4849
}
4950

5051
//-------------------------------------------------------------------------------------------------
@@ -65,7 +66,19 @@ Int ExperienceTracker::getExperienceValue( const Object* killer ) const
6566
//-------------------------------------------------------------------------------------------------
6667
Bool ExperienceTracker::isTrainable() const
6768
{
68-
return m_parent->getTemplate()->isTrainable();
69+
return m_isTrainable;
70+
}
71+
72+
//-------------------------------------------------------------------------------------------------
73+
void ExperienceTracker::setTrainable(Bool trainable)
74+
{
75+
m_isTrainable = trainable;
76+
}
77+
78+
//-------------------------------------------------------------------------------------------------
79+
void ExperienceTracker::resetTrainable()
80+
{
81+
m_isTrainable = m_parent->getTemplate()->isTrainable();
6982
}
7083

7184
//-------------------------------------------------------------------------------------------------
@@ -236,19 +249,27 @@ void ExperienceTracker::crc( Xfer *xfer )
236249
{
237250
xfer->xferInt( &m_currentExperience );
238251
xfer->xferUser( &m_currentLevel, sizeof( VeterancyLevel ) );
252+
#if !RETAIL_COMPATIBLE_CRC
253+
xfer->xferBool(&m_isTrainable);
254+
#endif
239255
}
240256

241257
//-----------------------------------------------------------------------------
242258
/** Xfer method
243259
* Version Info:
244260
* 1: Initial version
261+
* 2: TheSuperHackers @tweak Serialize m_isTrainable
245262
*/
246263
// ----------------------------------------------------------------------------
247264
void ExperienceTracker::xfer( Xfer *xfer )
248265
{
249266

250267
// version
268+
#if RETAIL_COMPATIBLE_XFER_SAVE
251269
XferVersion currentVersion = 1;
270+
#else
271+
XferVersion currentVersion = 2;
272+
#endif
252273
XferVersion version = currentVersion;
253274
xfer->xferVersion( &version, currentVersion );
254275

@@ -267,6 +288,8 @@ void ExperienceTracker::xfer( Xfer *xfer )
267288
// experience scalar
268289
xfer->xferReal( &m_experienceScalar );
269290

291+
if (version >= 2)
292+
xfer->xferBool(&m_isTrainable);
270293
}
271294

272295
//-----------------------------------------------------------------------------

0 commit comments

Comments
 (0)