Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions GeneralsMD/Code/GameEngine/Include/Common/Geometry.h
Original file line number Diff line number Diff line change
Expand Up @@ -171,8 +171,8 @@ class GeometryInfo : public Snapshot
/// get the 2d bounding box
void get2DBounds(const Coord3D& geomCenter, Real angle, Region2D& bounds ) const;

/// note that the pt is generated using game logic random, not game client random!
void makeRandomOffsetWithinFootprint(Coord3D& pt) const;
void makeGameLogicRandomOffsetWithinFootprint(Coord3D& pt) const;
void makeGameClientRandomOffsetWithinFootprint(Coord3D& pt) const;
void makeRandomOffsetOnPerimeter(Coord3D& pt) const; //Chooses a random point on the extent border.

void clipPointToFootprint(const Coord3D& geomCenter, Coord3D& ptToClip) const;
Expand Down
47 changes: 38 additions & 9 deletions GeneralsMD/Code/GameEngine/Source/Common/System/Geometry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -376,28 +376,37 @@ Bool GeometryInfo::isPointInFootprint(const Coord3D& geomCenter, const Coord3D&
}

//=============================================================================
void GeometryInfo::makeRandomOffsetWithinFootprint(Coord3D& pt) const
static void makeRandomOffsetWithinFootprint(
Real (*randomReal)(Real lo, Real hi),
GeometryType type,
Real majorRadius,
Real minorRadius,
Real boundingCircleRadius,
Coord3D& pt)
{
switch(m_type)
#if 1
(void)boundingCircleRadius;
#endif
switch(type)
{
case GEOMETRY_SPHERE:
case GEOMETRY_CYLINDER:
{
#if 1
// this is a better technique than the more obvious radius-and-angle
// one, below, because the latter tends to clump more towards the center.
Real maxDistSqr = sqr(m_majorRadius);
Real maxDistSqr = sqr(majorRadius);
Real distSqr;
do
{
pt.x = GameLogicRandomValueReal(-m_majorRadius, m_majorRadius);
pt.y = GameLogicRandomValueReal(-m_majorRadius, m_majorRadius);
pt.x = randomReal(-majorRadius, majorRadius);
pt.y = randomReal(-majorRadius, majorRadius);
pt.z = 0.0f;
distSqr = sqr(pt.x) + sqr(pt.y);
} while (distSqr > maxDistSqr);
#else
Real radius = GameLogicRandomValueReal(0.0f, m_boundingCircleRadius);
Real angle = GameLogicRandomValueReal(-PI, PI);
Real radius = randomReal(0.0f, boundingCircleRadius);
Real angle = randomReal(-PI, PI);
pt.x = radius * Cos(angle);
pt.y = radius * Sin(angle);
pt.z = 0.0f;
Expand All @@ -407,14 +416,34 @@ void GeometryInfo::makeRandomOffsetWithinFootprint(Coord3D& pt) const

case GEOMETRY_BOX:
{
pt.x = GameLogicRandomValueReal(-m_majorRadius, m_majorRadius);
pt.y = GameLogicRandomValueReal(-m_minorRadius, m_minorRadius);
pt.x = randomReal(-majorRadius, majorRadius);
pt.y = randomReal(-minorRadius, minorRadius);
pt.z = 0.0f;
break;
}
};
}

static Real GameLogicGeometryRandomReal(Real lo, Real hi)
{
return GameLogicRandomValueReal(lo, hi);
}

static Real GameClientGeometryRandomReal(Real lo, Real hi)
{
return GameClientRandomValueReal(lo, hi);
}

void GeometryInfo::makeGameLogicRandomOffsetWithinFootprint(Coord3D& pt) const
{
makeRandomOffsetWithinFootprint(GameLogicGeometryRandomReal, m_type, m_majorRadius, m_minorRadius, m_boundingCircleRadius, pt);
}

void GeometryInfo::makeGameClientRandomOffsetWithinFootprint(Coord3D& pt) const
{
makeRandomOffsetWithinFootprint(GameClientGeometryRandomReal, m_type, m_majorRadius, m_minorRadius, m_boundingCircleRadius, pt);
}

//=============================================================================
void GeometryInfo::makeRandomOffsetOnPerimeter(Coord3D& pt) const
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ void GenerateMinefieldBehavior::placeMinesInFootprint(const GeometryInfo& geom,
Int maxRetry = 100;
do
{
geom.makeRandomOffsetWithinFootprint(pt);
geom.makeGameLogicRandomOffsetWithinFootprint(pt);
pt.x += target->x;
pt.y += target->y;
pt.z += target->z;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ void TransitionDamageFX::onDelete()
/** Given an FXLoc info struct, return the effect position that we are supposed to use.
* The position is local to to the object */
//-------------------------------------------------------------------------------------------------
static Coord3D getLocalEffectPos( const FXLocInfo *locInfo, Drawable *draw )
static Coord3D getLocalEffectPos( const FXLocInfo *locInfo, Drawable *draw, Bool useGameClientRandom = FALSE )
{

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

// pick one of the bone positions
Int pick = GameLogicRandomValue( 0, boneCount - 1 );
Int pick = useGameClientRandom ? GameClientRandomValue( 0, boneCount - 1 ) : GameLogicRandomValue( 0, boneCount - 1 );
return positions[ pick ];

}
Expand Down Expand Up @@ -394,7 +394,7 @@ void TransitionDamageFX::onBodyDamageStateChange( const DamageInfo* damageInfo,
{

// get the what is the position we're going to played the effect at
pos = getLocalEffectPos( &modData->m_particleSystem[ newState ][ i ].locInfo, draw );
pos = getLocalEffectPos( &modData->m_particleSystem[ newState ][ i ].locInfo, draw, TRUE );
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to make this more elegant, by accepting generally structs from FXDamageFXListInfo, FXDamageOCLInfo or FXDamageParticleSystemInfo and deciding based on that whether to use game logic or client logic RNG, but we can't touch the structs really without causing mismatching.


//
// set position on system given any bone position provided, the bone position is
Expand All @@ -412,10 +412,29 @@ void TransitionDamageFX::onBodyDamageStateChange( const DamageInfo* damageInfo,

}

#if RETAIL_COMPATIBLE_CRC
// TheSuperHackers @fix stephanmeesters 18/05/2026 Fix issue where the creation of a certain particle system
// would influence game logic CRC due to the incorrect usage of game logic RNG. This code block is required to
// forward the game logic RNG and keep things consistent.
if ( pSystem )
{
const FXLocInfo* locInfo = &modData->m_particleSystem[newState][i].locInfo;
if( locInfo->locType == FX_DAMAGE_LOC_TYPE_BONE && locInfo->randomBone && draw )
{
const Int MAX_BONES = 32;
Coord3D positions[ MAX_BONES ];
Int boneCount = draw->getPristineBonePositions( locInfo->boneName.str(), 1, positions, nullptr, MAX_BONES );
if( boneCount > 0 )
{
static_cast<void>(GameLogicRandomValue( 0, boneCount - 1 ));
}
}
}
#endif

}

}

}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -310,8 +310,8 @@ void EMPUpdate::doDisableAttack()
if (sys)
{
Coord3D offs = {0,0,0};
curVictim->getGeometryInfo().makeRandomOffsetWithinFootprint( offs );
offs.z = GameLogicRandomValue(3, victimHeight);
curVictim->getGeometryInfo().makeGameClientRandomOffsetWithinFootprint( offs );
offs.z = GameClientRandomValue(3, victimHeight);

//This puts all the sparks within a quadrahemicycloid (rectangular dome) volume
//The same shape as a four cornered camping dome tent, for those with less Greek
Expand All @@ -328,12 +328,25 @@ void EMPUpdate::doDisableAttack()
sys->attachToObject(curVictim);
sys->setPosition( &offs );
sys->setSystemLifetime(MAX(0, data->m_disabledDuration - 30));
sys->setInitialDelay(GameLogicRandomValue(1,100));
sys->setInitialDelay(GameClientRandomValue(1,100));
}

#if RETAIL_COMPATIBLE_CRC
// TheSuperHackers @fix stephanmeesters 18/05/2026 Fix issue where the creation of a certain particle system
// would influence game logic CRC due to the incorrect usage of game logic RNG. This code block is required to
// forward the game logic RNG and keep things consistent.
if (sys)
{
Coord3D offs = { 0,0,0 };
curVictim->getGeometryInfo().makeGameLogicRandomOffsetWithinFootprint(offs);
static_cast<void>(GameLogicRandomValue(0, 1));
static_cast<void>(GameLogicRandomValue(0, 1));
}
#endif
}
}
Comment thread
stephanmeesters marked this conversation as resolved.
}

}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1404,13 +1404,24 @@ void SpecialAbilityUpdate::triggerAbilityEffect()
if (sys)
{
Coord3D offs = {0,0,0};
target->getGeometryInfo().makeRandomOffsetWithinFootprint( offs );
target->getGeometryInfo().makeGameClientRandomOffsetWithinFootprint( offs );

sys->attachToObject(target);
sys->setPosition( &offs );
sys->setSystemLifetime( data->m_effectDuration * durationInterleaveFactor ); //lifetime of the system, not the particles

}

#if RETAIL_COMPATIBLE_CRC
// TheSuperHackers @fix stephanmeesters 18/05/2026 Fix issue where the creation of a certain particle system
// would influence game logic CRC due to the incorrect usage of game logic RNG. This code block is required to
// forward the game logic RNG and keep things consistent.
if (sys)
{
Coord3D offs = { 0,0,0 };
target->getGeometryInfo().makeGameLogicRandomOffsetWithinFootprint(offs);
}
#endif
}
}
break;
Expand Down
Loading