Skip to content
4 changes: 4 additions & 0 deletions Core/GameEngine/Include/Common/GameDefines.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@
#define PRESERVE_RETAIL_SCRIPTED_CAMERA (1) // Retain scripted camera behavior present in retail Generals 1.08 and Zero Hour 1.04
#endif

#ifndef PRESERVE_RETAIL_PARTICLES
#define PRESERVE_RETAIL_PARTICLES (1) // Preserve original look of particles present in retail Generals 1.08 and Zero Hour 1.04
#endif

#ifndef RETAIL_COMPATIBLE_CRC
#define RETAIL_COMPATIBLE_CRC (1) // Game is expected to be CRC compatible with retail Generals 1.08, Zero Hour 1.04
#endif
Expand Down
49 changes: 40 additions & 9 deletions Core/GameEngine/Include/GameClient/ParticleSys.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,10 @@ class Particle : public MemoryPoolObject,

Particle( ParticleSystem *system, const ParticleInfo *data );

Bool update(); ///< update this particle's behavior - return false if dead
void doWindMotion(); ///< do wind motion (if present) from particle system
Bool update(); ///< update this particle's behavior - return false if dead

void draw( Real timeScale ); ///< render update
void doWindMotion( Real timeScale ); ///< do wind motion (if present) from particle system

void applyForce( const Coord3D *force ); ///< add the given acceleration

Expand Down Expand Up @@ -226,7 +228,6 @@ class Particle : public MemoryPoolObject,
// most of the particle data is derived from ParticleInfo

Coord3D m_accel; ///< current acceleration
Coord3D m_lastPos; ///< previous position
UnsignedInt m_lifetimeLeft; ///< lifetime remaining, if zero -> destroy
UnsignedInt m_createTimestamp; ///< frame this particle was created

Expand Down Expand Up @@ -577,7 +578,9 @@ class ParticleSystem : public MemoryPoolObject,
void attachToObject( const Object *obj ); ///< attach this particle system to an Object

virtual Bool update( Int localPlayerIndex ); ///< update this particle system, return false if dead
void updateWindMotion(); ///< update wind motion

void draw( Real timeScale ); ///< render update
void updateWindMotion( Real timeScale ); ///< update wind motion

void setControlParticle( Particle *p ); ///< set control particle

Expand Down Expand Up @@ -661,6 +664,12 @@ class ParticleSystem : public MemoryPoolObject,

protected:

struct VisibilityState
{
VisibilityState() : isShrouded(false) {}
Bool isShrouded;
};

// snapshot methods
virtual void crc( Xfer *xfer ) override;
virtual void xfer( Xfer *xfer ) override;
Expand All @@ -670,6 +679,14 @@ class ParticleSystem : public MemoryPoolObject,
ParticlePriorityType priority,
Bool forceCreate = FALSE ); ///< factory method for particles

void updateTransform();
void updateParentTransform(const Matrix3D &parentXfrm);
void updateLocalTransform();

void updateLogicalPos();
void updateLastLogicalPos();

VisibilityState updateVisibility( Int localPlayerIndex );

const ParticleInfo *generateParticleInfo( Int particleNum, Int particleCount ); ///< generate a new, random set of ParticleInfo
const Coord3D *computeParticlePosition(); ///< compute a position based on emission properties
Expand Down Expand Up @@ -704,8 +721,10 @@ class ParticleSystem : public MemoryPoolObject,
Real m_delayCoeff; ///< scalar value multiplied by burst delay
Real m_sizeCoeff; ///< scalar value multiplied by initial size

Coord3D m_pos; ///< this is the position to emit at.
Coord3D m_lastPos; ///< this is the previous position we emitted at.
Coord3D m_logicalPos; ///< this is the current logic position to emit at.
///< Can be different from the actual emitter transform
///< if the render update is faster than the logic step.
Coord3D m_lastLogicalPos; ///< this is the previous logic position we emitted at.

ParticleSystem * m_slaveSystem; ///< if non-null, another system this one has control of
ParticleSystemID m_slaveSystemID; ///< id of slave system (if present)
Expand Down Expand Up @@ -735,6 +754,9 @@ class ParticleSystem : public MemoryPoolObject,
/**
* The particle system manager, responsible for maintaining all ParticleSystems
*/
// TheSuperHacker @tweak The particle render update is now decoupled from the logic step.
// The lifetime management remains coupled to the logic step.
//
class ParticleSystemManager : public SubsystemInterface,
public Snapshot
{
Expand All @@ -751,11 +773,14 @@ class ParticleSystemManager : public SubsystemInterface,

virtual void init() override; ///< initialize the manager
virtual void reset() override; ///< reset the manager and all particle systems
virtual void update() override; ///< update all particle systems
virtual void update() override; ///< logic update for all particle systems
virtual void draw() override; ///< render update for all particle systems

virtual Int getOnScreenParticleCount() = 0; ///< returns the number of particles on screen
virtual void setOnScreenParticleCount(int count);

virtual Bool isDummy() const { return false; }

ParticleSystemTemplate *findTemplate( const AsciiString &name ) const;
ParticleSystemTemplate *findParentTemplate( const AsciiString &name, int parentNum ) const;
ParticleSystemTemplate *newTemplate( const AsciiString &name );
Expand Down Expand Up @@ -827,7 +852,6 @@ class ParticleSystemManager : public SubsystemInterface,
UnsignedInt m_fieldParticleCount; ///< this does not need to be xfered, since it is evaluated every frame
UnsignedInt m_particleSystemCount;
Int m_onScreenParticleCount; ///< number of particles displayed on screen per frame
UnsignedInt m_lastLogicFrameUpdate;
Int m_localPlayerIndex; ///<used to tell particle systems which particles can be skipped due to player shroud status

private:
Expand All @@ -836,14 +860,21 @@ class ParticleSystemManager : public SubsystemInterface,
};

// TheSuperHackers @feature bobtista 31/01/2026
// ParticleSystemManager that does nothing. Used for Headless Mode.
// ParticleSystemManager that does nothing. Cannot create particle systems and templates. Used for Headless Mode.
class ParticleSystemManagerDummy : public ParticleSystemManager
{
public:
virtual void init() override {}
virtual void reset() override {}
virtual void update() override {}
virtual void draw() override {}

virtual Int getOnScreenParticleCount() override { return 0; }
virtual void doParticles(RenderInfoClass &rinfo) override {}
virtual void queueParticleRender() override {}

virtual Bool isDummy() const override { return true; }

protected:
virtual void crc( Xfer *xfer ) override {}
virtual void xfer( Xfer *xfer ) override {}
Expand Down
2 changes: 0 additions & 2 deletions Core/GameEngine/Source/Common/ReplaySimulation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,6 @@ int ReplaySimulation::simulateReplaysInThisProcess(const std::vector<AsciiString
UnsignedInt totalTimeSec = TheRecorder->getPlaybackFrameCount() / LOGICFRAMES_PER_SECOND;
while (TheRecorder->isPlaybackInProgress())
{
TheGameClient->updateHeadless();

const int progressFrameInterval = 10*60*LOGICFRAMES_PER_SECOND;
if (TheGameLogic->getFrame() != 0 && TheGameLogic->getFrame() % progressFrameInterval == 0)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,7 @@ static ParticleSystem* createParticleSystem( Drawable *draw )
AsciiString templateName;
templateName.format("BeaconSmoke%6.6X", (0xffffff & obj->getIndicatorColor()));
const ParticleSystemTemplate *particleTemplate = TheParticleSystemManager->findTemplate( templateName );

DEBUG_ASSERTCRASH(particleTemplate, ("Could not find particle system %s", templateName.str()));

DEBUG_ASSERTCRASH(TheParticleSystemManager->isDummy() || particleTemplate, ("Could not find particle system %s", templateName.str()));
if (particleTemplate)
{
system = TheParticleSystemManager->createParticleSystem( particleTemplate );
Expand All @@ -107,7 +105,7 @@ static ParticleSystem* createParticleSystem( Drawable *draw )
{// THis this will whip up a new particle system to match the house color provided
templateName.format("BeaconSmokeFFFFFF");
const ParticleSystemTemplate *failsafeTemplate = TheParticleSystemManager->findTemplate( templateName );
DEBUG_ASSERTCRASH(failsafeTemplate, ("Doh, this is bad \n I Could not even find the white particle system to make a failsafe system out of."));
DEBUG_ASSERTCRASH(TheParticleSystemManager->isDummy() || failsafeTemplate, ("Doh, this is bad \n I Could not even find the white particle system to make a failsafe system out of."));
if (failsafeTemplate)
{
system = TheParticleSystemManager->createParticleSystem( failsafeTemplate );
Expand Down
2 changes: 1 addition & 1 deletion Core/GameEngine/Source/GameClient/FXList.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -600,7 +600,7 @@ class ParticleSystemFXNugget : public FXNugget
}

const ParticleSystemTemplate *tmp = TheParticleSystemManager->findTemplate(m_name);
DEBUG_ASSERTCRASH(tmp, ("ParticleSystem %s not found",m_name.str()));
DEBUG_ASSERTCRASH(TheParticleSystemManager->isDummy() || tmp, ("ParticleSystem %s not found",m_name.str()));
if (tmp)
{
for (Int i = 0; i < m_count; i++ )
Expand Down
Loading
Loading