Add a random chance for a decay sinking event to occur.

Adds a 1/125 chance for a corpse to undergo a decay movement in a given frame.
This allows a probability passed from the template to determine the sinking behavior of the entity.
Fixes #7548
This commit is contained in:
real_tabasco_sauce 2025-09-14 10:01:37 -07:00 committed by real_tabasco_sauce
parent 90505c29ee
commit f8e0a78d6d
8 changed files with 55 additions and 18 deletions

View file

@ -3,6 +3,7 @@
<Decay>
<Active>true</Active>
<SinkingAnim>false</SinkingAnim>
<SinkProb>1.0</SinkProb>
<DelayTime>6</DelayTime>
<SinkRate>10000</SinkRate>
<SinkAccel>0</SinkAccel>

View file

@ -3,6 +3,7 @@
<Decay>
<Active>true</Active>
<SinkingAnim>false</SinkingAnim>
<SinkProb>1.0</SinkProb>
<DelayTime>0.5</DelayTime>
<SinkRate>10000</SinkRate>
<SinkAccel>0</SinkAccel>

View file

@ -3,6 +3,7 @@
<Decay>
<Active>false</Active>
<SinkingAnim>false</SinkingAnim>
<SinkProb>0.25</SinkProb>
<DelayTime>15.0</DelayTime>
<SinkRate>0.2</SinkRate>
<SinkAccel>0</SinkAccel>

View file

@ -24,6 +24,7 @@
<Decay>
<Active>false</Active>
<SinkingAnim>false</SinkingAnim>
<SinkProb>1.0</SinkProb>
<DelayTime>0.0</DelayTime>
<SinkRate>3.0</SinkRate>
<SinkAccel>9.8</SinkAccel>

View file

@ -14,6 +14,7 @@
<Decay>
<Active>false</Active>
<SinkingAnim>false</SinkingAnim>
<SinkProb>0.01</SinkProb>
<DelayTime>30.0</DelayTime>
<SinkRate>0.01</SinkRate>
<SinkAccel>0.0</SinkAccel>

View file

@ -2,6 +2,7 @@
<Entity parent="template_unit">
<Decay>
<SinkingAnim>true</SinkingAnim>
<SinkProb>1.0</SinkProb>
<DelayTime>0.0</DelayTime>
<SinkRate>0.5</SinkRate>
<SinkAccel>7.0</SinkAccel>

View file

@ -5,6 +5,7 @@
</Cost>
<Decay>
<DelayTime>0.0</DelayTime>
<SinkProb>1.0</SinkProb>
<SinkRate>0.0</SinkRate>
<SinkAccel>2.0</SinkAccel>
</Decay>

View file

@ -36,6 +36,8 @@
#include <algorithm>
#include <cmath>
#include <optional>
#include <random>
#include <string>
/**
@ -65,6 +67,13 @@ public:
bool m_Active;
bool m_ShipSink;
bool m_Stochastic;
std::optional<std::minstd_rand> m_Generator;
std::negative_binomial_distribution<> m_Distribution{ 6, 0.032 };
std::geometric_distribution<>::result_type m_NextSink;
float m_SinkProb;
float m_DelayTime;
float m_SinkRate;
float m_SinkAccel;
@ -88,6 +97,9 @@ public:
"<element name='SinkingAnim' a:help='If true, the entity will decay in a ship-like manner'>"
"<data type='boolean'/>"
"</element>"
"<element name='SinkProb' a:help='The entity decays according to the supplied probability each frame.'>"
"<ref name='nonNegativeDecimal'/>"
"</element>"
"<element name='DelayTime' a:help='Time to wait before starting to sink, in seconds'>"
"<ref name='nonNegativeDecimal'/>"
"</element>"
@ -103,6 +115,7 @@ public:
{
m_Active = paramNode.GetChild("Active").ToBool();
m_ShipSink = paramNode.GetChild("SinkingAnim").ToBool();
m_SinkProb = paramNode.GetChild("SinkProb").ToFixed().ToFloat();
m_DelayTime = paramNode.GetChild("DelayTime").ToFixed().ToFloat();
m_SinkRate = paramNode.GetChild("SinkRate").ToFixed().ToFloat();
m_SinkAccel = paramNode.GetChild("SinkAccel").ToFixed().ToFloat();
@ -110,6 +123,11 @@ public:
m_CurrentTime = 0.f;
m_TotalSinkDepth = -1.f;
m_SinkProb < 1.f ? m_Stochastic = true : m_Stochastic = false;
std::negative_binomial_distribution<int>::param_type new_params(6, m_SinkProb*4);
m_Distribution.param(new_params);
// Detect unsafe misconfiguration
if (m_Active && !ENTITY_IS_LOCAL(GetEntityId()))
{
@ -175,6 +193,13 @@ public:
CFixedVector3D pos = cmpPosition->GetPosition();
//Use the entities position upon decay start to vary the pseudorandom seed.
if (!m_Generator.has_value())
{
m_Generator.emplace(pos.X.ToInt_RoundToNearest() * pos.Z.ToInt_RoundToNearest());
m_NextSink = m_Distribution(m_Generator.value());
}
CmpPtr<ICmpTerrain> cmpTerrain(GetSystemEntity());
if (cmpTerrain)
{
@ -199,27 +224,32 @@ public:
if (m_CurrentTime >= m_DelayTime)
{
float t = m_CurrentTime - m_DelayTime;
float depth = (m_SinkRate * t) + (m_SinkAccel * t * t);
if (m_ShipSink)
//If the entity should sink stochastically, use the negative binomial distribution to see if it should sink each frame.
if ((m_Stochastic && (m_NextSink-- == 0)) || !m_Stochastic)
{
// exponential sinking with tilting
float tilt_time = t > 5.f ? 5.f : t;
float tiltSink = tilt_time * tilt_time / 5.f;
entity_pos_t RotX = entity_pos_t::FromFloat(((m_InitialXRotation.ToFloat() * (5.f - tiltSink)) + (m_SinkingAngleX * tiltSink)) / 5.f);
entity_pos_t RotZ = entity_pos_t::FromFloat(((m_InitialZRotation.ToFloat() * (3.f - tilt_time)) + (m_SinkingAngleZ * tilt_time)) / 3.f);
cmpPosition->SetXZRotation(RotX,RotZ);
m_NextSink = m_Distribution(m_Generator.value());
float t = m_CurrentTime - m_DelayTime;
float depth = (m_SinkRate * t) + (m_SinkAccel * t * t);
depth = m_SinkRate * (exp(t - 1.f) - 0.54881163609f) + (m_SinkAccel * exp(t - 4.f) - 0.01831563888f);
if (depth < 0.f)
depth = 0.f;
if (m_ShipSink)
{
// exponential sinking with tilting
float tilt_time = t > 5.f ? 5.f : t;
float tiltSink = tilt_time * tilt_time / 5.f;
entity_pos_t RotX = entity_pos_t::FromFloat(((m_InitialXRotation.ToFloat() * (5.f - tiltSink)) + (m_SinkingAngleX * tiltSink)) / 5.f);
entity_pos_t RotZ = entity_pos_t::FromFloat(((m_InitialZRotation.ToFloat() * (3.f - tilt_time)) + (m_SinkingAngleZ * tilt_time)) / 3.f);
cmpPosition->SetXZRotation(RotX,RotZ);
depth = m_SinkRate * (exp(t - 1.f) - 0.54881163609f) + (m_SinkAccel * exp(t - 4.f) - 0.01831563888f);
if (depth < 0.f)
depth = 0.f;
}
cmpPosition->SetHeightOffset(entity_pos_t::FromFloat(-depth));
if (depth > m_TotalSinkDepth)
GetSimContext().GetComponentManager().DestroyComponentsSoon(GetEntityId());
}
cmpPosition->SetHeightOffset(entity_pos_t::FromFloat(-depth));
if (depth > m_TotalSinkDepth)
GetSimContext().GetComponentManager().DestroyComponentsSoon(GetEntityId());
}
break;