From 1d09adb2b8244fa77e5bcca714c2fb43e5d46137 Mon Sep 17 00:00:00 2001 From: stm <14291421+stephanmeesters@users.noreply.github.com> Date: Fri, 20 Feb 2026 21:41:04 +0100 Subject: [PATCH 1/7] bugfix(particlesys): Delay creation of particle emitters until postProcess --- .../GameClient/Drawable/Draw/W3DTankDraw.cpp | 10 ++++-- .../Drawable/Draw/W3DTankTruckDraw.cpp | 10 ++++-- .../GameLogic/Module/AutoHealBehavior.h | 5 ++- .../GameLogic/Module/GrantStealthBehavior.h | 2 ++ .../Object/Behavior/AutoHealBehavior.cpp | 35 ++++++++++++------- .../Object/Behavior/GrantStealthBehavior.cpp | 35 +++++++++++-------- 6 files changed, 62 insertions(+), 35 deletions(-) diff --git a/Core/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DTankDraw.cpp b/Core/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DTankDraw.cpp index 31b3d4c8c27..c6c3ed50532 100644 --- a/Core/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DTankDraw.cpp +++ b/Core/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DTankDraw.cpp @@ -34,6 +34,7 @@ #include "Common/Thing.h" #include "Common/ThingFactory.h" #include "Common/GameAudio.h" +#include "Common/GameState.h" #include "Common/ThingTemplate.h" #include "Common/Xfer.h" #include "GameLogic/Weapon.h" @@ -106,7 +107,12 @@ W3DTankDraw::W3DTankDraw( Thing *thing, const ModuleData* moduleData ) m_lastDirection.y=0.0f; m_lastDirection.z=0.0f; - createTreadEmitters(); + // TheSuperHackers @bugfix stephanmeesters 20/02/2026 + // If loading from savegame, delay non-saveable emitter creation until postProcess. + if (TheGameState == nullptr || TheGameState->isInLoadGame() == FALSE) + { + createTreadEmitters(); + } } //------------------------------------------------------------------------------------------------- @@ -435,8 +441,6 @@ void W3DTankDraw::loadPostProcess() // extend base class W3DModelDraw::loadPostProcess(); - // toss any existing tread emitters and re-create 'em (since this module expects 'em to always be around) - tossTreadEmitters(); createTreadEmitters(); } diff --git a/Core/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DTankTruckDraw.cpp b/Core/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DTankTruckDraw.cpp index eecbbdc975b..30ea63bc6e9 100644 --- a/Core/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DTankTruckDraw.cpp +++ b/Core/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DTankTruckDraw.cpp @@ -32,6 +32,7 @@ #include "Common/Thing.h" #include "Common/ThingFactory.h" #include "Common/GameAudio.h" +#include "Common/GameState.h" #include "Common/GlobalData.h" #include "Common/ThingTemplate.h" #include "Common/Xfer.h" @@ -119,7 +120,12 @@ m_prevRenderObj(nullptr) m_treadCount=0; - createTreadEmitters(); + // TheSuperHackers @bugfix stephanmeesters 20/02/2026 + // If loading from savegame, delay non-saveable emitter creation until postProcess. + if (TheGameState == nullptr || TheGameState->isInLoadGame() == FALSE) + { + createTreadEmitters(); + } } //------------------------------------------------------------------------------------------------- @@ -761,8 +767,6 @@ void W3DTankTruckDraw::loadPostProcess() // toss any existing wheel emitters (no need to re-create; we'll do that on demand) tossWheelEmitters(); - // toss any existing tread emitters and re-create 'em (since this module expects 'em to always be around) - tossTreadEmitters(); createTreadEmitters(); } diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/AutoHealBehavior.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/AutoHealBehavior.h index d9a2db32ee3..cdd0ab94b76 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/AutoHealBehavior.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/AutoHealBehavior.h @@ -108,9 +108,7 @@ class AutoHealBehaviorModuleData : public UpdateModuleData //------------------------------------------------------------------------------------------------- class AutoHealBehavior : public UpdateModule, public UpgradeMux, - public DamageModuleInterface -{ - + public DamageModuleInterface { MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE( AutoHealBehavior, "AutoHealBehavior" ) MAKE_STANDARD_MODULE_MACRO_WITH_MODULE_DATA( AutoHealBehavior, AutoHealBehaviorModuleData ) @@ -174,6 +172,7 @@ class AutoHealBehavior : public UpdateModule, private: void pulseHealObject( Object *obj ); + void createEmitters(); ParticleSystemID m_radiusParticleSystemID; diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/GrantStealthBehavior.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/GrantStealthBehavior.h index e431caaedbe..19858cf8b77 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/GrantStealthBehavior.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/GrantStealthBehavior.h @@ -101,6 +101,8 @@ class GrantStealthBehavior : public UpdateModule private: void grantStealthToObject( Object *obj ); + void createEmitters(); + ParticleSystemID m_radiusParticleSystemID; Real m_currentScanRadius; }; diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/AutoHealBehavior.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/AutoHealBehavior.cpp index b58e8b88592..f8360976554 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/AutoHealBehavior.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/AutoHealBehavior.cpp @@ -34,6 +34,7 @@ #include "Common/ThingTemplate.h" #include "Common/INI.h" #include "Common/Player.h" +#include "Common/GameState.h" #include "Common/Xfer.h" #include "GameClient/ParticleSys.h" #include "GameClient/Anim2D.h" @@ -94,20 +95,12 @@ AutoHealBehavior::AutoHealBehavior( Thing *thing, const ModuleData* moduleData ) m_radiusParticleSystemID = INVALID_PARTICLE_SYSTEM_ID; m_soonestHealFrame = 0; m_stopped = false; - Object *obj = getObject(); + // TheSuperHackers @bugfix stephanmeesters 20/02/2026 + // If loading from savegame, delay non-saveable emitter creation until postProcess. + if (TheGameState == nullptr || TheGameState->isInLoadGame() == FALSE) { - if( d->m_radiusParticleSystemTmpl ) - { - ParticleSystem *particleSystem; - - particleSystem = TheParticleSystemManager->createParticleSystem( d->m_radiusParticleSystemTmpl ); - if( particleSystem ) - { - particleSystem->setPosition( obj->getPosition() ); - m_radiusParticleSystemID = particleSystem->getSystemID(); - } - } + createEmitters(); } if (d->m_initiallyActive) @@ -373,4 +366,22 @@ void AutoHealBehavior::loadPostProcess() // extend base class UpgradeMux::upgradeMuxLoadPostProcess(); + createEmitters(); + +} + +// ------------------------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------------------------ +void AutoHealBehavior::createEmitters( void ) +{ + const AutoHealBehaviorModuleData *d = getAutoHealBehaviorModuleData(); + if( d->m_radiusParticleSystemTmpl ) + { + ParticleSystem *particleSystem = TheParticleSystemManager->createParticleSystem(d->m_radiusParticleSystemTmpl); + if( particleSystem ) + { + particleSystem->setPosition( getObject()->getPosition() ); + m_radiusParticleSystemID = particleSystem->getSystemID(); + } + } } diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/GrantStealthBehavior.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/GrantStealthBehavior.cpp index a638406ddff..16b764d32b3 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/GrantStealthBehavior.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/GrantStealthBehavior.cpp @@ -33,6 +33,7 @@ #include "Common/ThingTemplate.h" #include "Common/INI.h" #include "Common/Player.h" +#include "Common/GameState.h" #include "Common/Xfer.h" #include "GameClient/ParticleSys.h" #include "GameClient/Anim2D.h" @@ -96,21 +97,11 @@ GrantStealthBehavior::GrantStealthBehavior( Thing *thing, const ModuleData* modu m_currentScanRadius = d->m_startRadius; - - Object *obj = getObject(); - + // TheSuperHackers @bugfix stephanmeesters 20/02/2026 + // If loading from savegame, delay non-saveable emitter creation until postProcess. + if ( TheGameState == nullptr || TheGameState->isInLoadGame() == FALSE ) { - if( d->m_radiusParticleSystemTmpl ) - { - ParticleSystem *particleSystem; - - particleSystem = TheParticleSystemManager->createParticleSystem( d->m_radiusParticleSystemTmpl ); - if( particleSystem ) - { - particleSystem->setPosition( obj->getPosition() ); - m_radiusParticleSystemID = particleSystem->getSystemID(); - } - } + createEmitters(); } setWakeFrame( getObject(), UPDATE_SLEEP_NONE ); @@ -246,5 +237,21 @@ void GrantStealthBehavior::loadPostProcess() // extend base class UpdateModule::loadPostProcess(); + createEmitters(); +} +// ------------------------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------------------------ +void GrantStealthBehavior::createEmitters( void ) +{ + const GrantStealthBehaviorModuleData *d = getGrantStealthBehaviorModuleData(); + if( d->m_radiusParticleSystemTmpl ) + { + ParticleSystem *particleSystem = TheParticleSystemManager->createParticleSystem(d->m_radiusParticleSystemTmpl); + if( particleSystem ) + { + particleSystem->setPosition( getObject()->getPosition() ); + m_radiusParticleSystemID = particleSystem->getSystemID(); + } + } } From ce85d682c197ee9dac30166c6b275d139723b566 Mon Sep 17 00:00:00 2001 From: stm <14291421+stephanmeesters@users.noreply.github.com> Date: Sun, 22 Feb 2026 22:14:27 +0100 Subject: [PATCH 2/7] Apply suggestions from code review Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- .../Source/GameLogic/Object/Behavior/AutoHealBehavior.cpp | 2 +- .../Source/GameLogic/Object/Behavior/GrantStealthBehavior.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/AutoHealBehavior.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/AutoHealBehavior.cpp index f8360976554..c85d7cd9a70 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/AutoHealBehavior.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/AutoHealBehavior.cpp @@ -375,7 +375,7 @@ void AutoHealBehavior::loadPostProcess() void AutoHealBehavior::createEmitters( void ) { const AutoHealBehaviorModuleData *d = getAutoHealBehaviorModuleData(); - if( d->m_radiusParticleSystemTmpl ) + if( m_radiusParticleSystemID == INVALID_PARTICLE_SYSTEM_ID && d->m_radiusParticleSystemTmpl ) { ParticleSystem *particleSystem = TheParticleSystemManager->createParticleSystem(d->m_radiusParticleSystemTmpl); if( particleSystem ) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/GrantStealthBehavior.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/GrantStealthBehavior.cpp index 16b764d32b3..6770fec51c5 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/GrantStealthBehavior.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/GrantStealthBehavior.cpp @@ -245,7 +245,7 @@ void GrantStealthBehavior::loadPostProcess() void GrantStealthBehavior::createEmitters( void ) { const GrantStealthBehaviorModuleData *d = getGrantStealthBehaviorModuleData(); - if( d->m_radiusParticleSystemTmpl ) + if( m_radiusParticleSystemID == INVALID_PARTICLE_SYSTEM_ID && d->m_radiusParticleSystemTmpl ) { ParticleSystem *particleSystem = TheParticleSystemManager->createParticleSystem(d->m_radiusParticleSystemTmpl); if( particleSystem ) From d58f3a0655fca3979f75255fc841943af859a92c Mon Sep 17 00:00:00 2001 From: stm <14291421+stephanmeesters@users.noreply.github.com> Date: Sun, 22 Feb 2026 22:23:19 +0100 Subject: [PATCH 3/7] Update GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/AutoHealBehavior.cpp Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- .../Source/GameLogic/Object/Behavior/AutoHealBehavior.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/AutoHealBehavior.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/AutoHealBehavior.cpp index c85d7cd9a70..378d2655e4d 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/AutoHealBehavior.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/AutoHealBehavior.cpp @@ -374,7 +374,7 @@ void AutoHealBehavior::loadPostProcess() // ------------------------------------------------------------------------------------------------ void AutoHealBehavior::createEmitters( void ) { - const AutoHealBehaviorModuleData *d = getAutoHealBehaviorModuleData(); + const AutoHealBehaviorModuleData *d = getAutoHealBehaviorModuleData(); if( m_radiusParticleSystemID == INVALID_PARTICLE_SYSTEM_ID && d->m_radiusParticleSystemTmpl ) { ParticleSystem *particleSystem = TheParticleSystemManager->createParticleSystem(d->m_radiusParticleSystemTmpl); From 2fe0d63414d7d26cf20de639c9609530b0a1978f Mon Sep 17 00:00:00 2001 From: stm <14291421+stephanmeesters@users.noreply.github.com> Date: Sat, 14 Mar 2026 22:29:11 +0100 Subject: [PATCH 4/7] Delay emitters created in constructor to update/render loop --- .../GameClient/Drawable/Draw/W3DTankDraw.cpp | 10 +++------- .../GameClient/Drawable/Draw/W3DTankTruckDraw.cpp | 10 +++------- .../GameLogic/Object/Behavior/AutoHealBehavior.cpp | 11 +++-------- .../Object/Behavior/GrantStealthBehavior.cpp | 13 ++++--------- 4 files changed, 13 insertions(+), 31 deletions(-) diff --git a/Core/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DTankDraw.cpp b/Core/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DTankDraw.cpp index c6c3ed50532..956d56a0968 100644 --- a/Core/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DTankDraw.cpp +++ b/Core/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DTankDraw.cpp @@ -34,7 +34,6 @@ #include "Common/Thing.h" #include "Common/ThingFactory.h" #include "Common/GameAudio.h" -#include "Common/GameState.h" #include "Common/ThingTemplate.h" #include "Common/Xfer.h" #include "GameLogic/Weapon.h" @@ -107,12 +106,6 @@ W3DTankDraw::W3DTankDraw( Thing *thing, const ModuleData* moduleData ) m_lastDirection.y=0.0f; m_lastDirection.z=0.0f; - // TheSuperHackers @bugfix stephanmeesters 20/02/2026 - // If loading from savegame, delay non-saveable emitter creation until postProcess. - if (TheGameState == nullptr || TheGameState->isInLoadGame() == FALSE) - { - createTreadEmitters(); - } } //------------------------------------------------------------------------------------------------- @@ -316,6 +309,9 @@ void W3DTankDraw::doDrawModule(const Matrix3D* transformMtx) if (obj == nullptr) return; + // TheSuperHackers @bugfix stephanmeesters 14/03/2026 Delay emitter creation until draw + createTreadEmitters(); + // get object physics state PhysicsBehavior *physics = obj->getPhysics(); if (physics == nullptr) diff --git a/Core/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DTankTruckDraw.cpp b/Core/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DTankTruckDraw.cpp index 30ea63bc6e9..280fc0f0f18 100644 --- a/Core/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DTankTruckDraw.cpp +++ b/Core/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DTankTruckDraw.cpp @@ -32,7 +32,6 @@ #include "Common/Thing.h" #include "Common/ThingFactory.h" #include "Common/GameAudio.h" -#include "Common/GameState.h" #include "Common/GlobalData.h" #include "Common/ThingTemplate.h" #include "Common/Xfer.h" @@ -120,12 +119,6 @@ m_prevRenderObj(nullptr) m_treadCount=0; - // TheSuperHackers @bugfix stephanmeesters 20/02/2026 - // If loading from savegame, delay non-saveable emitter creation until postProcess. - if (TheGameState == nullptr || TheGameState->isInLoadGame() == FALSE) - { - createTreadEmitters(); - } } //------------------------------------------------------------------------------------------------- @@ -510,6 +503,9 @@ void W3DTankTruckDraw::doDrawModule(const Matrix3D* transformMtx) if (obj == nullptr) return; + // TheSuperHackers @bugfix stephanmeesters 14/03/2026 Delay emitter creation until draw + createTreadEmitters(); + if (getRenderObject()==nullptr) return; if (getRenderObject() != m_prevRenderObj) { updateBones(); diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/AutoHealBehavior.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/AutoHealBehavior.cpp index 378d2655e4d..12e559b254a 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/AutoHealBehavior.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/AutoHealBehavior.cpp @@ -34,7 +34,6 @@ #include "Common/ThingTemplate.h" #include "Common/INI.h" #include "Common/Player.h" -#include "Common/GameState.h" #include "Common/Xfer.h" #include "GameClient/ParticleSys.h" #include "GameClient/Anim2D.h" @@ -96,13 +95,6 @@ AutoHealBehavior::AutoHealBehavior( Thing *thing, const ModuleData* moduleData ) m_soonestHealFrame = 0; m_stopped = false; - // TheSuperHackers @bugfix stephanmeesters 20/02/2026 - // If loading from savegame, delay non-saveable emitter creation until postProcess. - if (TheGameState == nullptr || TheGameState->isInLoadGame() == FALSE) - { - createEmitters(); - } - if (d->m_initiallyActive) { giveSelfUpgrade(); @@ -176,6 +168,9 @@ UpdateSleepTime AutoHealBehavior::update() if (m_stopped) return UPDATE_SLEEP_FOREVER; + // TheSuperHackers @bugfix stephanmeesters 14/03/2026 Delay emitter creation until update + createEmitters(); + Object *obj = getObject(); const AutoHealBehaviorModuleData *d = getAutoHealBehaviorModuleData(); diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/GrantStealthBehavior.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/GrantStealthBehavior.cpp index 6770fec51c5..f24e4858372 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/GrantStealthBehavior.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/GrantStealthBehavior.cpp @@ -33,7 +33,6 @@ #include "Common/ThingTemplate.h" #include "Common/INI.h" #include "Common/Player.h" -#include "Common/GameState.h" #include "Common/Xfer.h" #include "GameClient/ParticleSys.h" #include "GameClient/Anim2D.h" @@ -97,14 +96,7 @@ GrantStealthBehavior::GrantStealthBehavior( Thing *thing, const ModuleData* modu m_currentScanRadius = d->m_startRadius; - // TheSuperHackers @bugfix stephanmeesters 20/02/2026 - // If loading from savegame, delay non-saveable emitter creation until postProcess. - if ( TheGameState == nullptr || TheGameState->isInLoadGame() == FALSE ) - { - createEmitters(); - } - - setWakeFrame( getObject(), UPDATE_SLEEP_NONE ); + setWakeFrame( getObject(), UPDATE_SLEEP_NONE ); } //------------------------------------------------------------------------------------------------- @@ -130,6 +122,9 @@ UpdateSleepTime GrantStealthBehavior::update() if ( self->isEffectivelyDead()) return UPDATE_SLEEP_FOREVER; + // TheSuperHackers @bugfix stephanmeesters 14/03/2026 Delay emitter creation until update + createEmitters(); + const GrantStealthBehaviorModuleData *d = getGrantStealthBehaviorModuleData(); // setup scan filters PartitionFilterRelationship relationship( self, PartitionFilterRelationship::ALLOW_ALLIES ); From 1f55acd4784ebd97ce8d6a7e911b89e105c2051d Mon Sep 17 00:00:00 2001 From: stm <14291421+stephanmeesters@users.noreply.github.com> Date: Sun, 15 Mar 2026 19:16:27 +0100 Subject: [PATCH 5/7] refactor: Improve emitter functions perf and syntax --- .../GameClient/Drawable/Draw/W3DTankDraw.cpp | 40 ++++++++++-------- .../Drawable/Draw/W3DTankTruckDraw.cpp | 41 ++++++++++--------- .../Object/Behavior/AutoHealBehavior.cpp | 10 ++--- .../Object/Behavior/GrantStealthBehavior.cpp | 4 +- 4 files changed, 52 insertions(+), 43 deletions(-) diff --git a/Core/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DTankDraw.cpp b/Core/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DTankDraw.cpp index 956d56a0968..826071d34b9 100644 --- a/Core/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DTankDraw.cpp +++ b/Core/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DTankDraw.cpp @@ -125,27 +125,33 @@ void W3DTankDraw::tossTreadEmitters() //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- -void W3DTankDraw::createTreadEmitters() +static ParticleSystemID createParticleSystem( const AsciiString &name, const Drawable *drawable ) { - const AsciiString *treadDebrisNames[2]; - static_assert(ARRAY_SIZE(treadDebrisNames) == ARRAY_SIZE(m_treadDebrisIDs), "Array size must match"); - treadDebrisNames[0] = &getW3DTankDrawModuleData()->m_treadDebrisNameLeft; - treadDebrisNames[1] = &getW3DTankDrawModuleData()->m_treadDebrisNameRight; + const ParticleSystemTemplate *sysTemplate = TheParticleSystemManager->findTemplate(name); + ParticleSystem *particleSys = TheParticleSystemManager->createParticleSystem( sysTemplate ); + if (!particleSys) + return INVALID_PARTICLE_SYSTEM_ID; + + particleSys->attachToDrawable(drawable); + // important: mark it as do-not-save, since we'll just re-create it when we reload. + particleSys->setSaveable(FALSE); + // they come into being stopped. + particleSys->stop(); + + return particleSys->getSystemID(); +} - for (size_t i = 0; i < ARRAY_SIZE(m_treadDebrisIDs); ++i) +void W3DTankDraw::createTreadEmitters() +{ + if (getW3DTankDrawModuleData()) { - if (m_treadDebrisIDs[i] == INVALID_PARTICLE_SYSTEM_ID) + if (m_treadDebrisIDs[0] == INVALID_PARTICLE_SYSTEM_ID) { - if (const ParticleSystemTemplate *sysTemplate = TheParticleSystemManager->findTemplate(*treadDebrisNames[i])) - { - ParticleSystem *particleSys = TheParticleSystemManager->createParticleSystem( sysTemplate ); - particleSys->attachToDrawable(getDrawable()); - // important: mark it as do-not-save, since we'll just re-create it when we reload. - particleSys->setSaveable(FALSE); - // they come into being stopped. - particleSys->stop(); - m_treadDebrisIDs[i] = particleSys->getSystemID(); - } + m_treadDebrisIDs[0] = createParticleSystem(getW3DTankDrawModuleData()->m_treadDebrisNameLeft, getDrawable()); + } + if (m_treadDebrisIDs[1] == INVALID_PARTICLE_SYSTEM_ID) + { + m_treadDebrisIDs[1] = createParticleSystem(getW3DTankDrawModuleData()->m_treadDebrisNameRight, getDrawable()); } } } diff --git a/Core/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DTankTruckDraw.cpp b/Core/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DTankTruckDraw.cpp index 280fc0f0f18..501bbf061e4 100644 --- a/Core/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DTankTruckDraw.cpp +++ b/Core/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DTankTruckDraw.cpp @@ -181,30 +181,33 @@ void W3DTankTruckDraw::setFullyObscuredByShroud(Bool fullyObscured) //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- +static ParticleSystemID createParticleSystem( const AsciiString &name, const Drawable *drawable ) +{ + const ParticleSystemTemplate *sysTemplate = TheParticleSystemManager->findTemplate(name); + ParticleSystem *particleSys = TheParticleSystemManager->createParticleSystem( sysTemplate ); + if (!particleSys) + return INVALID_PARTICLE_SYSTEM_ID; + + particleSys->attachToDrawable(drawable); + // important: mark it as do-not-save, since we'll just re-create it when we reload. + particleSys->setSaveable(FALSE); + // they come into being stopped. + particleSys->stop(); + + return particleSys->getSystemID(); +} + void W3DTankTruckDraw::createTreadEmitters() { if (getW3DTankTruckDrawModuleData()) { - const AsciiString *treadDebrisNames[2]; - static_assert(ARRAY_SIZE(treadDebrisNames) == ARRAY_SIZE(m_treadDebrisIDs), "Array size must match"); - treadDebrisNames[0] = &getW3DTankTruckDrawModuleData()->m_treadDebrisNameLeft; - treadDebrisNames[1] = &getW3DTankTruckDrawModuleData()->m_treadDebrisNameRight; - - for (size_t i = 0; i < ARRAY_SIZE(m_treadDebrisIDs); ++i) + if (m_treadDebrisIDs[0] == INVALID_PARTICLE_SYSTEM_ID) { - if (m_treadDebrisIDs[i] == INVALID_PARTICLE_SYSTEM_ID) - { - if (const ParticleSystemTemplate *sysTemplate = TheParticleSystemManager->findTemplate(*treadDebrisNames[i])) - { - ParticleSystem *particleSys = TheParticleSystemManager->createParticleSystem( sysTemplate ); - particleSys->attachToDrawable(getDrawable()); - // important: mark it as do-not-save, since we'll just re-create it when we reload. - particleSys->setSaveable(FALSE); - // they come into being stopped. - particleSys->stop(); - m_treadDebrisIDs[i] = particleSys->getSystemID(); - } - } + m_treadDebrisIDs[0] = createParticleSystem(getW3DTankTruckDrawModuleData()->m_treadDebrisNameLeft, getDrawable()); + } + if (m_treadDebrisIDs[1] == INVALID_PARTICLE_SYSTEM_ID) + { + m_treadDebrisIDs[1] = createParticleSystem(getW3DTankTruckDrawModuleData()->m_treadDebrisNameRight, getDrawable()); } } } diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/AutoHealBehavior.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/AutoHealBehavior.cpp index 12e559b254a..dd859243be9 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/AutoHealBehavior.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/AutoHealBehavior.cpp @@ -168,9 +168,6 @@ UpdateSleepTime AutoHealBehavior::update() if (m_stopped) return UPDATE_SLEEP_FOREVER; - // TheSuperHackers @bugfix stephanmeesters 14/03/2026 Delay emitter creation until update - createEmitters(); - Object *obj = getObject(); const AutoHealBehaviorModuleData *d = getAutoHealBehaviorModuleData(); @@ -182,6 +179,9 @@ UpdateSleepTime AutoHealBehavior::update() return UPDATE_SLEEP_FOREVER; } + // TheSuperHackers @bugfix stephanmeesters 14/03/2026 Delay emitter creation until update + createEmitters(); + //DEBUG_LOG(("doing auto heal %d",TheGameLogic->getFrame())); if( d->m_affectsWholePlayer ) @@ -369,9 +369,9 @@ void AutoHealBehavior::loadPostProcess() // ------------------------------------------------------------------------------------------------ void AutoHealBehavior::createEmitters( void ) { - const AutoHealBehaviorModuleData *d = getAutoHealBehaviorModuleData(); - if( m_radiusParticleSystemID == INVALID_PARTICLE_SYSTEM_ID && d->m_radiusParticleSystemTmpl ) + if( m_radiusParticleSystemID == INVALID_PARTICLE_SYSTEM_ID ) { + const AutoHealBehaviorModuleData *d = getAutoHealBehaviorModuleData(); ParticleSystem *particleSystem = TheParticleSystemManager->createParticleSystem(d->m_radiusParticleSystemTmpl); if( particleSystem ) { diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/GrantStealthBehavior.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/GrantStealthBehavior.cpp index f24e4858372..ca245d2a564 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/GrantStealthBehavior.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/GrantStealthBehavior.cpp @@ -239,9 +239,9 @@ void GrantStealthBehavior::loadPostProcess() // ------------------------------------------------------------------------------------------------ void GrantStealthBehavior::createEmitters( void ) { - const GrantStealthBehaviorModuleData *d = getGrantStealthBehaviorModuleData(); - if( m_radiusParticleSystemID == INVALID_PARTICLE_SYSTEM_ID && d->m_radiusParticleSystemTmpl ) + if( m_radiusParticleSystemID == INVALID_PARTICLE_SYSTEM_ID ) { + const GrantStealthBehaviorModuleData *d = getGrantStealthBehaviorModuleData(); ParticleSystem *particleSystem = TheParticleSystemManager->createParticleSystem(d->m_radiusParticleSystemTmpl); if( particleSystem ) { From 3605bfea3a0e221f7fc48a2ad9db9fd2ebc2849a Mon Sep 17 00:00:00 2001 From: stm <14291421+stephanmeesters@users.noreply.github.com> Date: Sun, 15 Mar 2026 19:54:30 +0100 Subject: [PATCH 6/7] Remove void --- .../Source/GameLogic/Object/Behavior/AutoHealBehavior.cpp | 2 +- .../Source/GameLogic/Object/Behavior/GrantStealthBehavior.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/AutoHealBehavior.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/AutoHealBehavior.cpp index dd859243be9..6f193b5f148 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/AutoHealBehavior.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/AutoHealBehavior.cpp @@ -367,7 +367,7 @@ void AutoHealBehavior::loadPostProcess() // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ -void AutoHealBehavior::createEmitters( void ) +void AutoHealBehavior::createEmitters() { if( m_radiusParticleSystemID == INVALID_PARTICLE_SYSTEM_ID ) { diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/GrantStealthBehavior.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/GrantStealthBehavior.cpp index ca245d2a564..3c733c1a6d9 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/GrantStealthBehavior.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/GrantStealthBehavior.cpp @@ -237,7 +237,7 @@ void GrantStealthBehavior::loadPostProcess() // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ -void GrantStealthBehavior::createEmitters( void ) +void GrantStealthBehavior::createEmitters() { if( m_radiusParticleSystemID == INVALID_PARTICLE_SYSTEM_ID ) { From 751e97881efbb8dc8d40bc1d9fa3ba07486ddd29 Mon Sep 17 00:00:00 2001 From: stm <14291421+stephanmeesters@users.noreply.github.com> Date: Fri, 27 Mar 2026 23:16:32 +0100 Subject: [PATCH 7/7] Add assert in ParticleSystemManager --- Core/GameEngine/Source/GameClient/System/ParticleSys.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Core/GameEngine/Source/GameClient/System/ParticleSys.cpp b/Core/GameEngine/Source/GameClient/System/ParticleSys.cpp index c11bfefb2fa..5cbf3470c3c 100644 --- a/Core/GameEngine/Source/GameClient/System/ParticleSys.cpp +++ b/Core/GameEngine/Source/GameClient/System/ParticleSys.cpp @@ -3346,6 +3346,8 @@ void ParticleSystemManager::xfer( Xfer *xfer ) } else { + DEBUG_ASSERTCRASH(m_allParticleSystemList.size()==0, ("ParticleSystemManager: particle systems list must be empty at start of xfer-load.")); + const ParticleSystemTemplate *systemTemplate; // read each particle system