Skip to content

Commit 9a402fb

Browse files
authored
fix(particlesys): Decouple particle systems from logic crc (TheSuperHackers#2742)
1 parent bf8d5be commit 9a402fb

12 files changed

Lines changed: 123 additions & 34 deletions

File tree

Core/GameEngine/Include/Common/RandomValue.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,24 @@ extern void InitRandom( UnsignedInt seed );
3535
extern UnsignedInt GetGameLogicRandomSeed(); ///< Get the seed (used for replays)
3636
extern UnsignedInt GetGameLogicRandomSeedCRC();///< Get the seed (used for CRCs)
3737

38+
struct RandomValueClass
39+
{
40+
virtual Int GetRandomValueInt( Int lo, Int hi, const char *file, Int line ) const = 0;
41+
virtual Real GetRandomValueReal( Real lo, Real hi, const char *file, Int line ) const = 0;
42+
};
43+
struct LogicRandomValueClass final : RandomValueClass
44+
{
45+
virtual Int GetRandomValueInt( Int lo, Int hi, const char *file, Int line ) const override;
46+
virtual Real GetRandomValueReal( Real lo, Real hi, const char *file, Int line ) const override;
47+
};
48+
struct ClientRandomValueClass final : RandomValueClass
49+
{
50+
virtual Int GetRandomValueInt( Int lo, Int hi, const char *file, Int line ) const override;
51+
virtual Real GetRandomValueReal( Real lo, Real hi, const char *file, Int line ) const override;
52+
};
53+
54+
// use these macros to access the random value functions
55+
#define RandomValueInt(randomValueClass, lo, hi) randomValueClass.GetRandomValueInt( lo, hi, __FILE__, __LINE__ )
56+
#define RandomValueReal(randomValueClass, lo, hi) randomValueClass.GetRandomValueReal( lo, hi, __FILE__, __LINE__ )
57+
3858
//--------------------------------------------------------------------------------------------------------------

Core/GameEngine/Source/Common/RandomValue.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,3 +465,24 @@ Real GameLogicRandomVariable::getValue() const
465465
return 0.0f;
466466
}
467467
}
468+
469+
470+
Int LogicRandomValueClass::GetRandomValueInt( Int lo, Int hi, const char *file, Int line ) const
471+
{
472+
return GetGameLogicRandomValue(lo, hi, file, line);
473+
}
474+
475+
Real LogicRandomValueClass::GetRandomValueReal( Real lo, Real hi, const char *file, Int line ) const
476+
{
477+
return GetGameLogicRandomValueReal(lo, hi, file, line);
478+
}
479+
480+
Int ClientRandomValueClass::GetRandomValueInt( Int lo, Int hi, const char *file, Int line ) const
481+
{
482+
return GetGameClientRandomValue(lo, hi, file, line);
483+
}
484+
485+
Real ClientRandomValueClass::GetRandomValueReal( Real lo, Real hi, const char *file, Int line ) const
486+
{
487+
return GetGameClientRandomValueReal(lo, hi, file, line);
488+
}

Generals/Code/GameEngine/Include/Common/Geometry.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131

3232
#include "Lib/BaseType.h"
3333
#include "Common/AsciiString.h"
34+
#include "Common/RandomValue.h"
3435
#include "Common/Snapshot.h"
3536

3637
class INI;
@@ -171,9 +172,8 @@ class GeometryInfo : public Snapshot
171172
/// get the 2d bounding box
172173
void get2DBounds(const Coord3D& geomCenter, Real angle, Region2D& bounds ) const;
173174

174-
/// note that the pt is generated using game logic random, not game client random!
175-
void makeRandomOffsetWithinFootprint(Coord3D& pt) const;
176-
void makeRandomOffsetOnPerimeter(Coord3D& pt) const; //Chooses a random point on the extent border.
175+
void makeRandomOffsetWithinFootprint(Coord3D& pt, const RandomValueClass& random = LogicRandomValueClass()) const;
176+
void makeRandomOffsetOnPerimeter(Coord3D& pt) const; ///< Chooses a random point on the extent border. Uses game logic random!
177177

178178
void clipPointToFootprint(const Coord3D& geomCenter, Coord3D& ptToClip) const;
179179

Generals/Code/GameEngine/Source/Common/System/Geometry.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ Bool GeometryInfo::isPointInFootprint(const Coord3D& geomCenter, const Coord3D&
376376
}
377377

378378
//=============================================================================
379-
void GeometryInfo::makeRandomOffsetWithinFootprint(Coord3D& pt) const
379+
void GeometryInfo::makeRandomOffsetWithinFootprint(Coord3D& pt, const RandomValueClass& random) const
380380
{
381381
switch(m_type)
382382
{
@@ -390,14 +390,14 @@ void GeometryInfo::makeRandomOffsetWithinFootprint(Coord3D& pt) const
390390
Real distSqr;
391391
do
392392
{
393-
pt.x = GameLogicRandomValueReal(-m_majorRadius, m_majorRadius);
394-
pt.y = GameLogicRandomValueReal(-m_majorRadius, m_majorRadius);
393+
pt.x = RandomValueReal(random, -m_majorRadius, m_majorRadius);
394+
pt.y = RandomValueReal(random, -m_majorRadius, m_majorRadius);
395395
pt.z = 0.0f;
396396
distSqr = sqr(pt.x) + sqr(pt.y);
397397
} while (distSqr > maxDistSqr);
398398
#else
399-
Real radius = GameLogicRandomValueReal(0.0f, m_boundingCircleRadius);
400-
Real angle = GameLogicRandomValueReal(-PI, PI);
399+
Real radius = RandomValueReal(random, 0.0f, m_boundingCircleRadius);
400+
Real angle = RandomValueReal(random, -PI, PI);
401401
pt.x = radius * Cos(angle);
402402
pt.y = radius * Sin(angle);
403403
pt.z = 0.0f;
@@ -407,8 +407,8 @@ void GeometryInfo::makeRandomOffsetWithinFootprint(Coord3D& pt) const
407407

408408
case GEOMETRY_BOX:
409409
{
410-
pt.x = GameLogicRandomValueReal(-m_majorRadius, m_majorRadius);
411-
pt.y = GameLogicRandomValueReal(-m_minorRadius, m_minorRadius);
410+
pt.x = RandomValueReal(random, -m_majorRadius, m_majorRadius);
411+
pt.y = RandomValueReal(random, -m_minorRadius, m_minorRadius);
412412
pt.z = 0.0f;
413413
break;
414414
}

Generals/Code/GameEngine/Source/GameLogic/Object/Damage/TransitionDamageFX.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ void TransitionDamageFX::onDelete()
253253
/** Given an FXLoc info struct, return the effect position that we are supposed to use.
254254
* The position is local to to the object */
255255
//-------------------------------------------------------------------------------------------------
256-
static Coord3D getLocalEffectPos( const FXLocInfo *locInfo, Drawable *draw )
256+
static Coord3D getLocalEffectPos( const FXLocInfo *locInfo, Drawable *draw, const RandomValueClass &random = LogicRandomValueClass() )
257257
{
258258

259259
DEBUG_ASSERTCRASH( locInfo, ("getLocalEffectPos: locInfo is null") );
@@ -290,7 +290,7 @@ static Coord3D getLocalEffectPos( const FXLocInfo *locInfo, Drawable *draw )
290290
return locInfo->loc;
291291

292292
// pick one of the bone positions
293-
Int pick = GameLogicRandomValue( 0, boneCount - 1 );
293+
Int pick = RandomValueInt( random, 0, boneCount - 1 );
294294
return positions[ pick ];
295295

296296
}
@@ -387,14 +387,19 @@ void TransitionDamageFX::onBodyDamageStateChange( const DamageInfo* damageInfo,
387387
if( lastDamageInfo == nullptr ||
388388
getDamageTypeFlag( modData->m_damageParticleTypes, lastDamageInfo->in.m_damageType ) )
389389
{
390+
#if RETAIL_COMPATIBLE_CRC
391+
// TheSuperHackers @fix The particle system is now decoupled from the logic crc
392+
// and the side effects on the logic random seed values are preserved for retail compatibility.
393+
getLocalEffectPos( &modData->m_particleSystem[ newState ][ i ].locInfo, draw, LogicRandomValueClass() );
394+
#endif
390395

391396
// create a new particle system based on the template provided
392397
ParticleSystem* pSystem = TheParticleSystemManager->createParticleSystem( pSystemT );
393398
if( pSystem )
394399
{
395400

396401
// get the what is the position we're going to played the effect at
397-
pos = getLocalEffectPos( &modData->m_particleSystem[ newState ][ i ].locInfo, draw );
402+
pos = getLocalEffectPos( &modData->m_particleSystem[ newState ][ i ].locInfo, draw, ClientRandomValueClass() );
398403

399404
//
400405
// set position on system given any bone position provided, the bone position is

Generals/Code/GameEngine/Source/GameLogic/Object/Update/EMPUpdate.cpp

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -235,14 +235,24 @@ void EMPUpdate::doDisableAttack()
235235

236236
for (UnsignedInt e = 0 ; e < emitterCount; ++e)
237237
{
238+
#if RETAIL_COMPATIBLE_CRC
239+
// TheSuperHackers @fix The particle system is now decoupled from the logic crc
240+
// and the side effects on the logic random seed values are preserved for retail compatibility.
241+
{
242+
Coord3D offs = {0,0,0};
243+
curVictim->getGeometryInfo().makeRandomOffsetWithinFootprint( offs, LogicRandomValueClass() );
244+
GameLogicRandomValue(3, victimHeight);
245+
GameLogicRandomValue(1, 100);
246+
}
247+
#endif
238248

239249
ParticleSystem *sys = TheParticleSystemManager->createParticleSystem(tmp);
240250

241251
if (sys)
242252
{
243253
Coord3D offs = {0,0,0};
244-
curVictim->getGeometryInfo().makeRandomOffsetWithinFootprint( offs );
245-
offs.z = GameLogicRandomValue(3, victimHeight);
254+
curVictim->getGeometryInfo().makeRandomOffsetWithinFootprint( offs, ClientRandomValueClass() );
255+
offs.z = GameClientRandomValue(3, victimHeight);
246256

247257
//This puts all the sparks within a quadrahemicycloid (rectangular dome) volume
248258
//The same shape as a four cornered camping dome tent, for those with less Greek
@@ -259,7 +269,7 @@ void EMPUpdate::doDisableAttack()
259269
sys->attachToObject(curVictim);
260270
sys->setPosition( &offs );
261271
sys->setSystemLifetime(MAX(0, data->m_disabledDuration - 30));
262-
sys->setInitialDelay(GameLogicRandomValue(1,100));
272+
sys->setInitialDelay(GameClientRandomValue(1,100));
263273
}
264274
}
265275
}

Generals/Code/GameEngine/Source/GameLogic/Object/Update/SpecialAbilityUpdate.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1263,11 +1263,20 @@ void SpecialAbilityUpdate::triggerAbilityEffect()
12631263
const ParticleSystemTemplate *tmp = data->m_disableFXParticleSystem;
12641264
if (tmp)
12651265
{
1266+
#if RETAIL_COMPATIBLE_CRC
1267+
// TheSuperHackers @fix The particle system is now decoupled from the logic crc
1268+
// and the side effects on the logic random seed values are preserved for retail compatibility.
1269+
{
1270+
Coord3D offs = {0,0,0};
1271+
target->getGeometryInfo().makeRandomOffsetWithinFootprint( offs, LogicRandomValueClass() );
1272+
}
1273+
#endif
1274+
12661275
ParticleSystem *sys = TheParticleSystemManager->createParticleSystem(tmp);
12671276
if (sys)
12681277
{
12691278
Coord3D offs = {0,0,0};
1270-
target->getGeometryInfo().makeRandomOffsetWithinFootprint( offs );
1279+
target->getGeometryInfo().makeRandomOffsetWithinFootprint( offs, ClientRandomValueClass() );
12711280

12721281
sys->attachToObject(target);
12731282
sys->setPosition( &offs );

GeneralsMD/Code/GameEngine/Include/Common/Geometry.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131

3232
#include "Lib/BaseType.h"
3333
#include "Common/AsciiString.h"
34+
#include "Common/RandomValue.h"
3435
#include "Common/Snapshot.h"
3536

3637
class INI;
@@ -171,9 +172,8 @@ class GeometryInfo : public Snapshot
171172
/// get the 2d bounding box
172173
void get2DBounds(const Coord3D& geomCenter, Real angle, Region2D& bounds ) const;
173174

174-
/// note that the pt is generated using game logic random, not game client random!
175-
void makeRandomOffsetWithinFootprint(Coord3D& pt) const;
176-
void makeRandomOffsetOnPerimeter(Coord3D& pt) const; //Chooses a random point on the extent border.
175+
void makeRandomOffsetWithinFootprint(Coord3D& pt, const RandomValueClass& random = LogicRandomValueClass()) const;
176+
void makeRandomOffsetOnPerimeter(Coord3D& pt) const; ///< Chooses a random point on the extent border. Uses game logic random!
177177

178178
void clipPointToFootprint(const Coord3D& geomCenter, Coord3D& ptToClip) const;
179179

GeneralsMD/Code/GameEngine/Source/Common/System/Geometry.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ Bool GeometryInfo::isPointInFootprint(const Coord3D& geomCenter, const Coord3D&
376376
}
377377

378378
//=============================================================================
379-
void GeometryInfo::makeRandomOffsetWithinFootprint(Coord3D& pt) const
379+
void GeometryInfo::makeRandomOffsetWithinFootprint(Coord3D& pt, const RandomValueClass& random) const
380380
{
381381
switch(m_type)
382382
{
@@ -390,14 +390,14 @@ void GeometryInfo::makeRandomOffsetWithinFootprint(Coord3D& pt) const
390390
Real distSqr;
391391
do
392392
{
393-
pt.x = GameLogicRandomValueReal(-m_majorRadius, m_majorRadius);
394-
pt.y = GameLogicRandomValueReal(-m_majorRadius, m_majorRadius);
393+
pt.x = RandomValueReal(random, -m_majorRadius, m_majorRadius);
394+
pt.y = RandomValueReal(random, -m_majorRadius, m_majorRadius);
395395
pt.z = 0.0f;
396396
distSqr = sqr(pt.x) + sqr(pt.y);
397397
} while (distSqr > maxDistSqr);
398398
#else
399-
Real radius = GameLogicRandomValueReal(0.0f, m_boundingCircleRadius);
400-
Real angle = GameLogicRandomValueReal(-PI, PI);
399+
Real radius = RandomValueReal(random, 0.0f, m_boundingCircleRadius);
400+
Real angle = RandomValueReal(random, -PI, PI);
401401
pt.x = radius * Cos(angle);
402402
pt.y = radius * Sin(angle);
403403
pt.z = 0.0f;
@@ -407,8 +407,8 @@ void GeometryInfo::makeRandomOffsetWithinFootprint(Coord3D& pt) const
407407

408408
case GEOMETRY_BOX:
409409
{
410-
pt.x = GameLogicRandomValueReal(-m_majorRadius, m_majorRadius);
411-
pt.y = GameLogicRandomValueReal(-m_minorRadius, m_minorRadius);
410+
pt.x = RandomValueReal(random, -m_majorRadius, m_majorRadius);
411+
pt.y = RandomValueReal(random, -m_minorRadius, m_minorRadius);
412412
pt.z = 0.0f;
413413
break;
414414
}

GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Damage/TransitionDamageFX.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ void TransitionDamageFX::onDelete()
253253
/** Given an FXLoc info struct, return the effect position that we are supposed to use.
254254
* The position is local to to the object */
255255
//-------------------------------------------------------------------------------------------------
256-
static Coord3D getLocalEffectPos( const FXLocInfo *locInfo, Drawable *draw )
256+
static Coord3D getLocalEffectPos( const FXLocInfo *locInfo, Drawable *draw, const RandomValueClass &random = LogicRandomValueClass() )
257257
{
258258

259259
DEBUG_ASSERTCRASH( locInfo, ("getLocalEffectPos: locInfo is null") );
@@ -290,7 +290,7 @@ static Coord3D getLocalEffectPos( const FXLocInfo *locInfo, Drawable *draw )
290290
return locInfo->loc;
291291

292292
// pick one of the bone positions
293-
Int pick = GameLogicRandomValue( 0, boneCount - 1 );
293+
Int pick = RandomValueInt( random, 0, boneCount - 1 );
294294
return positions[ pick ];
295295

296296
}
@@ -387,14 +387,19 @@ void TransitionDamageFX::onBodyDamageStateChange( const DamageInfo* damageInfo,
387387
if( lastDamageInfo == nullptr ||
388388
getDamageTypeFlag( modData->m_damageParticleTypes, lastDamageInfo->in.m_damageType ) )
389389
{
390+
#if RETAIL_COMPATIBLE_CRC
391+
// TheSuperHackers @fix The particle system is now decoupled from the logic crc
392+
// and the side effects on the logic random seed values are preserved for retail compatibility.
393+
getLocalEffectPos( &modData->m_particleSystem[ newState ][ i ].locInfo, draw, LogicRandomValueClass() );
394+
#endif
390395

391396
// create a new particle system based on the template provided
392397
ParticleSystem* pSystem = TheParticleSystemManager->createParticleSystem( pSystemT );
393398
if( pSystem )
394399
{
395400

396401
// get the what is the position we're going to played the effect at
397-
pos = getLocalEffectPos( &modData->m_particleSystem[ newState ][ i ].locInfo, draw );
402+
pos = getLocalEffectPos( &modData->m_particleSystem[ newState ][ i ].locInfo, draw, ClientRandomValueClass() );
398403

399404
//
400405
// set position on system given any bone position provided, the bone position is

0 commit comments

Comments
 (0)