From 0064b8ad6537319d498d01a93c8ed7cec6ca8294 Mon Sep 17 00:00:00 2001 From: wh1ter0se <62149665+wh1ter0se69@users.noreply.github.com> Date: Fri, 7 Aug 2026 18:33:26 +0300 Subject: [PATCH 1/3] bugfix(fx): Destroy slave particle systems with their master instead of orphaning them ~ParticleSystem() cleared the slave's master pointer but left the slave alive. A slave is never positioned or attached itself - the master merges positions into it on every burst - and it is kept from emitting on its own only by the m_masterSystem == NULL check in update(). Orphaning it therefore produces a live system with no transform at all, which emits its particles at raw local coordinates, i.e. the world origin. destroy() already propagates to the slave for this reason; the destructor did not. Verified on a VC6 release build against Golden Replay 1: emissions at the world origin drop from 59 to 0 over a full playthrough, with no CRC mismatch before or after. --- .../Source/GameClient/System/ParticleSys.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Core/GameEngine/Source/GameClient/System/ParticleSys.cpp b/Core/GameEngine/Source/GameClient/System/ParticleSys.cpp index 1a79b3c2b6d..31d59eb55b4 100644 --- a/Core/GameEngine/Source/GameClient/System/ParticleSys.cpp +++ b/Core/GameEngine/Source/GameClient/System/ParticleSys.cpp @@ -1235,6 +1235,16 @@ ParticleSystem::~ParticleSystem() { DEBUG_ASSERTCRASH( m_slaveSystem->getMaster() == this, ("~ParticleSystem: Our slave doesn't have us as a master!") ); + + // TheSuperHackers @bugfix Destroy the slave instead of orphaning it alive. + // A slave system is never positioned or attached by anything - its master merges + // positions into it on every burst - and it is kept from emitting on its own only by the + // m_masterSystem == NULL check in update(). Clearing the master without destroying the + // slave therefore leaves a live system with no transform at all, which emits its + // particles at raw local coordinates, i.e. the world origin. destroy() already + // propagates to the slave for exactly this reason; the destructor did not. + m_slaveSystem->destroy(); + m_slaveSystem->setMaster( nullptr ); setSlave( nullptr ); From f63e1e029ef2eeb154d89f15a3cbd0132a701528 Mon Sep 17 00:00:00 2001 From: wh1ter0se <62149665+wh1ter0se69@users.noreply.github.com> Date: Sat, 8 Aug 2026 01:03:01 +0300 Subject: [PATCH 2/3] Shorten the source comment and move the explanation to the PR Per review: the long rationale belongs in the pull request description, not inline. --- .../Source/GameClient/System/ParticleSys.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/Core/GameEngine/Source/GameClient/System/ParticleSys.cpp b/Core/GameEngine/Source/GameClient/System/ParticleSys.cpp index 31d59eb55b4..60f7a4bb8ad 100644 --- a/Core/GameEngine/Source/GameClient/System/ParticleSys.cpp +++ b/Core/GameEngine/Source/GameClient/System/ParticleSys.cpp @@ -1236,13 +1236,9 @@ ParticleSystem::~ParticleSystem() DEBUG_ASSERTCRASH( m_slaveSystem->getMaster() == this, ("~ParticleSystem: Our slave doesn't have us as a master!") ); - // TheSuperHackers @bugfix Destroy the slave instead of orphaning it alive. - // A slave system is never positioned or attached by anything - its master merges - // positions into it on every burst - and it is kept from emitting on its own only by the - // m_masterSystem == NULL check in update(). Clearing the master without destroying the - // slave therefore leaves a live system with no transform at all, which emits its - // particles at raw local coordinates, i.e. the world origin. destroy() already - // propagates to the slave for exactly this reason; the destructor did not. + // TheSuperHackers @bugfix Destroy the slave instead of orphaning it alive. A masterless + // slave has no transform of its own and starts self-emitting at the world origin. + // See the pull request for the full explanation. m_slaveSystem->destroy(); m_slaveSystem->setMaster( nullptr ); From b48912563eb55b202a6dbc5dbb88dff8dead6e9c Mon Sep 17 00:00:00 2001 From: wh1ter0se <62149665+wh1ter0se69@users.noreply.github.com> Date: Mon, 10 Aug 2026 03:27:33 +0300 Subject: [PATCH 3/3] Address review: drop the PR reference, say position instead of transform The slave has no position of its own, not no transform - particle systems carry a Coord3D, and the wording was confusing on that point. Also adds the author/date fields the annotation convention asks for. --- Core/GameEngine/Source/GameClient/System/ParticleSys.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Core/GameEngine/Source/GameClient/System/ParticleSys.cpp b/Core/GameEngine/Source/GameClient/System/ParticleSys.cpp index 60f7a4bb8ad..76f9e629685 100644 --- a/Core/GameEngine/Source/GameClient/System/ParticleSys.cpp +++ b/Core/GameEngine/Source/GameClient/System/ParticleSys.cpp @@ -1236,9 +1236,9 @@ ParticleSystem::~ParticleSystem() DEBUG_ASSERTCRASH( m_slaveSystem->getMaster() == this, ("~ParticleSystem: Our slave doesn't have us as a master!") ); - // TheSuperHackers @bugfix Destroy the slave instead of orphaning it alive. A masterless - // slave has no transform of its own and starts self-emitting at the world origin. - // See the pull request for the full explanation. + // TheSuperHackers @bugfix wh1ter0se69 10/08/2026 Destroy the slave instead of orphaning it + // alive. A slave has no position of its own - its master merges one in on every burst - and + // nothing stops a masterless slave from emitting, so it starts bursting at the world origin. m_slaveSystem->destroy(); m_slaveSystem->setMaster( nullptr );