From a0ac74b51cf6ca07b6ccd212a7a2dc10bf04a007 Mon Sep 17 00:00:00 2001 From: Tommy March Date: Fri, 2 Jan 2026 13:25:03 +0530 Subject: [PATCH 1/4] Bunch of hopeful fixes to emitters --- Source/Entities/AEmitter.cpp | 34 ++++++++++++++-------------- Source/Entities/PEmitter.cpp | 43 ++++++++++++++++++++++-------------- 2 files changed, 42 insertions(+), 35 deletions(-) diff --git a/Source/Entities/AEmitter.cpp b/Source/Entities/AEmitter.cpp index e407c1e403..2329a8d45a 100644 --- a/Source/Entities/AEmitter.cpp +++ b/Source/Entities/AEmitter.cpp @@ -276,28 +276,25 @@ float AEmitter::EstimateImpulse(bool burst) { for (Emission* emission: m_EmissionList) { // Only check emissions that push the emitter if (emission->PushesEmitter()) { - // TODO: we're not checking emission start/stop times here, so this will always calculate the impulse as if the emission was active. + // Todo... we're not checking emission start/stop times here, so this will always calculate the impulse as if the emission was active. // There's not really an easy way to do this, since the emission rate is not necessarily constant over time. + float emissionsPerFrame = (emission->GetRate() / 60.0f) * g_TimerMan.GetDeltaTimeSecs(); + float scale = 1.0F; - // TODO: burst emissions shouldn't be affected by delta time, but they were. - // However our values were tuned for 60hz, so hack in constant 60Hz deltatime in milliseconds. - float deltaTimeSecs = burst ? 1.0f / 60.0f : g_TimerMan.GetDeltaTimeSecs(); + // Get all the particles emitted this frame + emissionsPerFrame *= emission->GetParticleCount(); - float emissions = (emission->GetRate() / 60.0f) * deltaTimeSecs; - float scale = 1.0F; + // When bursting, add on all the bursted emissions + // We also use m_BurstScale on ALL emissions, not just the extra bursted ones + // This is a bit funky but consistent with the code that applies the impulse if (burst) { - emissions *= emission->GetBurstSize(); + emissionsPerFrame += emission->GetBurstSize(); scale = m_BurstScale; } - if (emissions > 0) { - int extraEmissions = emission->GetParticleCount() - 1; - emissions += extraEmissions; - } - float velMin = emission->GetMinVelocity() * scale; - float velRange = (emission->GetMaxVelocity() - emission->GetMinVelocity()) * scale * 0.5f; - float spread = (std::max(static_cast(c_PI) - (emission->GetSpread() * scale), 0.0F) / c_PI); // A large spread will cause the forces to cancel eachother out + float velRange = (emission->GetMaxVelocity() - emission->GetMinVelocity()) * scale * 0.5f; + float spread = (std::max(static_cast(c_PI) - (emission->GetSpread() * scale), 0.0F) / c_PI); // A large spread will cause the forces to cancel eachother out // Add to accumulative recoil impulse generated, F = m * a. impulse += (velMin + velRange) * spread * emission->m_pEmission->GetMass() * emissions; @@ -458,17 +455,18 @@ void AEmitter::Update() { } else { emission->m_Accumulator = 0; } + float scale = 1.0F; // Add extra emissions if bursting. if (m_BurstTriggered) { emissionCount += emission->GetBurstSize(); scale = m_BurstScale; } + + // We don't consider extra particles for our emission count, so add prior to multiply emissionCountTotal += emissionCount; - if (emissionCount > 0) { - int extraEmissions = emission->GetParticleCount() - 1; - emissionCount += extraEmissions; - } + emissionCount *= emission->GetParticleCount(); + pParticle = 0; emitVel.Reset(); parentVel = pRootParent->GetVel() * emission->InheritsVelocity(); diff --git a/Source/Entities/PEmitter.cpp b/Source/Entities/PEmitter.cpp index 1734c56744..1a8296e53b 100644 --- a/Source/Entities/PEmitter.cpp +++ b/Source/Entities/PEmitter.cpp @@ -234,18 +234,20 @@ float PEmitter::EstimateImpulse(bool burst) { if (emission->PushesEmitter()) { // Todo... we're not checking emission start/stop times here, so this will always calculate the impulse as if the emission was active. // There's not really an easy way to do this, since the emission rate is not necessarily constant over time. - float emissions = (emission->GetRate() / 60.0f) * g_TimerMan.GetDeltaTimeSecs(); + float emissionsPerFrame = (emission->GetRate() / 60.0f) * g_TimerMan.GetDeltaTimeSecs(); float scale = 1.0F; + + // Get all the particles emitted this frame + emissionsPerFrame *= emission->GetParticleCount(); + + // When bursting, add on all the bursted emissions + // We also use m_BurstScale on ALL emissions, not just the extra bursted ones + // This is a bit funky but consistent with the code that applies the impulse if (burst) { - emissions *= emission->GetBurstSize(); + emissionsPerFrame += emission->GetBurstSize(); scale = m_BurstScale; } - if (emissions > 0) { - int extraEmissions = emission->GetParticleCount() - 1; - emissions += extraEmissions; - } - float velMin = emission->GetMinVelocity() * scale; float velRange = (emission->GetMaxVelocity() - emission->GetMinVelocity()) * scale * 0.5f; float spread = (std::max(static_cast(c_PI) - (emission->GetSpread() * scale), 0.0F) / c_PI); // A large spread will cause the forces to cancel eachother out @@ -320,7 +322,7 @@ void PEmitter::Update() { else m_BurstTriggered = false; - int emissions = 0; + int emissionCountTotal = 0; float velMin, velRange, spread; double currentPPM, SPE; MovableObject* pParticle = 0; @@ -331,7 +333,7 @@ void PEmitter::Update() { if (emission->IsEmissionTime()) { // Apply the throttle factor to the emission rate currentPPM = emission->GetRate() * throttleFactor; - emissions = 0; + int emissionCount = 0; // Only do all this if the PPM is acutally above zero if (currentPPM > 0) { @@ -342,25 +344,32 @@ void PEmitter::Update() { emission->m_Accumulator += m_LastEmitTmr.GetElapsedSimTimeS(); // Now figure how many full emissions can fit in the current accumulator - emissions = std::floor(emission->m_Accumulator / SPE); + emissionCount = std::floor(emission->m_Accumulator / SPE); // Deduct the about to be emitted emissions from the accumulator emission->m_Accumulator -= emissions * SPE; RTEAssert(emission->m_Accumulator >= 0, "Emission accumulator negative!"); } + float scale = 1.0F; // Add extra emissions if bursting. - if (m_BurstTriggered) - emissions += emission->GetBurstSize() * std::floor(throttleFactor); + if (m_BurstTriggered) { + emissionCount += emission->GetBurstSize(); + scale = m_BurstScale; + } + + // We don't consider extra particles for our emission count, so add prior to multiply + emissionCountTotal += emissionCount; + emissionCount *= emission->GetParticleCount(); pParticle = 0; emitVel.Reset(); parentVel = pRootParent->GetVel() * emission->InheritsVelocity(); - for (int i = 0; i < emissions; ++i) { - velMin = emission->GetMinVelocity() * (m_BurstTriggered ? m_BurstScale : 1.0); - velRange = emission->GetMaxVelocity() - emission->GetMinVelocity() * (m_BurstTriggered ? m_BurstScale : 1.0); - spread = emission->GetSpread() * (m_BurstTriggered ? m_BurstScale : 1.0); + for (int i = 0; i < emissionCount; ++i) { + velMin = emission->GetMinVelocity() * scale; + velRange = emission->GetMaxVelocity() - emission->GetMinVelocity() * scale; + spread = emission->GetSpread() * scale; // Make a copy after the reference particle pParticle = dynamic_cast(emission->GetEmissionParticlePreset()->Clone()); // Set up its position and velocity according to the parameters of this. @@ -405,7 +414,7 @@ void PEmitter::Update() { m_LastEmitTmr.Reset(); // Count the total emissions since enabling, and stop emitting if beyong limit (and limit is also enabled) - m_EmitCount += emissions; + m_EmitCount += emissionCountTotal; if (m_EmitCountLimit > 0 && m_EmitCount > m_EmitCountLimit) EnableEmission(false); From d6d3866b72be6c0a72915795dd3281f82e378970 Mon Sep 17 00:00:00 2001 From: Tommy March Date: Fri, 2 Jan 2026 13:31:10 +0530 Subject: [PATCH 2/4] Whoops --- Source/Entities/AEmitter.cpp | 2 +- Source/Entities/PEmitter.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Entities/AEmitter.cpp b/Source/Entities/AEmitter.cpp index 2329a8d45a..2147c8d119 100644 --- a/Source/Entities/AEmitter.cpp +++ b/Source/Entities/AEmitter.cpp @@ -297,7 +297,7 @@ float AEmitter::EstimateImpulse(bool burst) { float spread = (std::max(static_cast(c_PI) - (emission->GetSpread() * scale), 0.0F) / c_PI); // A large spread will cause the forces to cancel eachother out // Add to accumulative recoil impulse generated, F = m * a. - impulse += (velMin + velRange) * spread * emission->m_pEmission->GetMass() * emissions; + impulse += (velMin + velRange) * spread * emission->m_pEmission->GetMass() * emissionsPerFrame; } } diff --git a/Source/Entities/PEmitter.cpp b/Source/Entities/PEmitter.cpp index 1a8296e53b..4b89d08c89 100644 --- a/Source/Entities/PEmitter.cpp +++ b/Source/Entities/PEmitter.cpp @@ -253,7 +253,7 @@ float PEmitter::EstimateImpulse(bool burst) { float spread = (std::max(static_cast(c_PI) - (emission->GetSpread() * scale), 0.0F) / c_PI); // A large spread will cause the forces to cancel eachother out // Add to accumulative recoil impulse generated, F = m * a. - impulse += (velMin + velRange) * spread * emission->m_pEmission->GetMass() * emissions; + impulse += (velMin + velRange) * spread * emission->m_pEmission->GetMass() * emissionsPerFrame; } } @@ -346,7 +346,7 @@ void PEmitter::Update() { // Now figure how many full emissions can fit in the current accumulator emissionCount = std::floor(emission->m_Accumulator / SPE); // Deduct the about to be emitted emissions from the accumulator - emission->m_Accumulator -= emissions * SPE; + emission->m_Accumulator -= emissionCount * SPE; RTEAssert(emission->m_Accumulator >= 0, "Emission accumulator negative!"); } From 311e8a4410721b0a388341b8c178dd70deddf19b Mon Sep 17 00:00:00 2001 From: Tommy March Date: Fri, 2 Jan 2026 13:46:01 +0530 Subject: [PATCH 3/4] Adjusted estimate-jump-height to work a little better * return FLT_MAX for infinite jump height * don't reduce final height- it doesn't actually improve behaviour in a lot of cases, it's more consistent to let the AI try to take a big vertical climb than many smaller ones --- Source/Entities/AHuman.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Source/Entities/AHuman.cpp b/Source/Entities/AHuman.cpp index a21ef1dbe7..e032cd8a02 100644 --- a/Source/Entities/AHuman.cpp +++ b/Source/Entities/AHuman.cpp @@ -1007,8 +1007,7 @@ float AHuman::EstimateJumpHeight() const { if (currentYVelocity + yGravity >= currentYVelocity) { // Velocity is too big or gravity is too small. Either way, this will loop forever now. // Just assume that we can reach the stars. - totalHeight = g_SceneMan.GetSceneHeight() * c_MPP; - break; + return FLT_MAX; } currentYVelocity += yGravity; @@ -1022,9 +1021,7 @@ float AHuman::EstimateJumpHeight() const { } } - float finalHeightMultipler = 0.6f; // Make us think we can do less because AI path following is shit - - return totalHeight * c_MPP * finalHeightMultipler; + return totalHeight * c_MPP; } bool AHuman::EquipShield() { From 3ccb0f9da726829e36734c64a778a47e3c88c6b6 Mon Sep 17 00:00:00 2001 From: Tommy March Date: Fri, 2 Jan 2026 15:07:26 +0530 Subject: [PATCH 4/4] for architector --- Source/Entities/AEmitter.cpp | 2 +- Source/Entities/PEmitter.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Entities/AEmitter.cpp b/Source/Entities/AEmitter.cpp index 2147c8d119..5803b963ab 100644 --- a/Source/Entities/AEmitter.cpp +++ b/Source/Entities/AEmitter.cpp @@ -276,7 +276,7 @@ float AEmitter::EstimateImpulse(bool burst) { for (Emission* emission: m_EmissionList) { // Only check emissions that push the emitter if (emission->PushesEmitter()) { - // Todo... we're not checking emission start/stop times here, so this will always calculate the impulse as if the emission was active. + // TODO: we're not checking emission start/stop times here, so this will always calculate the impulse as if the emission was active. // There's not really an easy way to do this, since the emission rate is not necessarily constant over time. float emissionsPerFrame = (emission->GetRate() / 60.0f) * g_TimerMan.GetDeltaTimeSecs(); float scale = 1.0F; diff --git a/Source/Entities/PEmitter.cpp b/Source/Entities/PEmitter.cpp index 4b89d08c89..13141a80b1 100644 --- a/Source/Entities/PEmitter.cpp +++ b/Source/Entities/PEmitter.cpp @@ -232,7 +232,7 @@ float PEmitter::EstimateImpulse(bool burst) { for (Emission* emission: m_EmissionList) { // Only check emissions that push the emitter if (emission->PushesEmitter()) { - // Todo... we're not checking emission start/stop times here, so this will always calculate the impulse as if the emission was active. + // TODO: we're not checking emission start/stop times here, so this will always calculate the impulse as if the emission was active. // There's not really an easy way to do this, since the emission rate is not necessarily constant over time. float emissionsPerFrame = (emission->GetRate() / 60.0f) * g_TimerMan.GetDeltaTimeSecs(); float scale = 1.0F;