Index: binaries/data/mods/public/simulation/components/Health.js =================================================================== --- binaries/data/mods/public/simulation/components/Health.js +++ binaries/data/mods/public/simulation/components/Health.js @@ -227,7 +227,7 @@ this.hitpoints = 0; this.RegisterHealthChanged(oldHitpoints); this.HandleDeath(); - return { "killed": true, "HPchange": -oldHitpoints }; + return { "killed": true, "HPchange": -oldHitpoints, "corpse": this.corpse }; } // If we are not marked as injured, do it now @@ -259,7 +259,7 @@ switch (this.template.DeathType) { case "corpse": - this.CreateCorpse(); + this.corpse = this.CreateCorpse(); break; case "remain": Index: binaries/data/mods/public/simulation/helpers/Attacking.js =================================================================== --- binaries/data/mods/public/simulation/helpers/Attacking.js +++ binaries/data/mods/public/simulation/helpers/Attacking.js @@ -195,6 +195,7 @@ let nearEnts = this.EntitiesNearPoint(data.origin, data.radius, this.GetPlayersToDamage(data.attackerOwner, data.friendlyFire)); let damageMultiplier = 1; + data.attackData.origin = data.origin; // Cycle through all the nearby entities and damage it appropriately based on its distance from the origin. for (let ent of nearEnts) @@ -257,6 +258,17 @@ if (targetState.killed) this.TargetKilled(attacker, target, attackerOwner); + if (targetState.corpse && attackType.search("Splash") !== -1) + { + let cmpDecay = Engine.QueryInterface(targetState.corpse, IID_Decay); + if (cmpDecay) + { + let cmpEPos = Engine.QueryInterface(target, IID_Position); + let pos = [cmpEPos.GetPosition().x - attackData.origin.x, cmpEPos.GetPosition().z - attackData.origin.y]; + cmpDecay.ApplyForce(pos[0] * 4.0, 10.0, pos[1] * 4.0); + } + } + Engine.PostMessage(target, MT_Attacked, { "type": attackType, "target": target, Index: binaries/data/mods/public/simulation/templates/special/target_marker.xml =================================================================== --- binaries/data/mods/public/simulation/templates/special/target_marker.xml +++ binaries/data/mods/public/simulation/templates/special/target_marker.xml @@ -2,7 +2,6 @@ true - false 0.5 10000 0 Index: binaries/data/mods/public/simulation/templates/template_structure.xml =================================================================== --- binaries/data/mods/public/simulation/templates/template_structure.xml +++ binaries/data/mods/public/simulation/templates/template_structure.xml @@ -38,7 +38,6 @@ false - false 0.0 3.0 9.8 Index: binaries/data/mods/public/simulation/templates/template_unit.xml =================================================================== --- binaries/data/mods/public/simulation/templates/template_unit.xml +++ binaries/data/mods/public/simulation/templates/template_unit.xml @@ -19,10 +19,15 @@ false - false 30.0 0.01 0.0 + physics + + 0.1 + 0.2 + 50.0 + Index: source/simulation2/components/CCmpDecay.cpp =================================================================== --- source/simulation2/components/CCmpDecay.cpp +++ source/simulation2/components/CCmpDecay.cpp @@ -25,6 +25,7 @@ #include "ICmpPosition.h" #include "ICmpTerrain.h" #include "ICmpVisual.h" +#include "ICmpWaterManager.h" #include "ps/Profile.h" @@ -53,12 +54,29 @@ DEFAULT_COMPONENT_ALLOCATOR(Decay) + enum { + DECAY_CLASSIC = 0, + DECAY_SHIP = 1, + DECAY_PHYSICS = 2 + }; + bool m_Active; - bool m_ShipSink; + bool m_ReallyInited; + uint8_t m_DecayType; + float m_DelayTime; float m_SinkRate; float m_SinkAccel; + // for physics decay only + CVector3D m_Force; + float m_GroundFriction; // a fake friction, so that a round object might roll down a hill but a human probably won't. + float m_AirFriction; // note that values range [0..1] for both frictions where 1 means none and 0 means total stop. + float m_Weight; + CFixedVector3D m_PositionOneSecAgo; // to check if we should go into basic decay. + float m_LastPositionCheck; + + // for ship-like sinking only entity_pos_t m_InitialXRotation; entity_pos_t m_InitialZRotation; @@ -75,9 +93,30 @@ "" "" "" - "" - "" - "" + "" + "" + "" + "classic" + "ship" + "physics" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" "" "" "" @@ -92,13 +131,34 @@ virtual void Init(const CParamNode& paramNode) { m_Active = paramNode.GetChild("Active").ToBool(); - m_ShipSink = paramNode.GetChild("SinkingAnim").ToBool(); + + if (!paramNode.GetChild("DecayType").IsOk() || paramNode.GetChild("DecayType").ToString() == L"classic") + m_DecayType = DECAY_CLASSIC; + else if(paramNode.GetChild("DecayType").ToString() == L"ship") + m_DecayType = DECAY_SHIP; + else if(paramNode.GetChild("DecayType").ToString() == L"physics") + { + m_DecayType = DECAY_PHYSICS; + if (!paramNode.GetChild("PhysicsParameters").IsOk()) + { + debug_warn(L"CCmpDecay in physics mode without physics parameters"); + m_Active = false; + } + else + { + m_Force = CVector3D(); + m_GroundFriction = paramNode.GetChild("PhysicsParameters").GetChild("GroundFriction").ToFloat(); + m_AirFriction = paramNode.GetChild("PhysicsParameters").GetChild("AirFriction").ToFloat(); + m_Weight = paramNode.GetChild("PhysicsParameters").GetChild("Weight").ToFloat(); + } + } + m_DelayTime = paramNode.GetChild("DelayTime").ToFixed().ToFloat(); m_SinkRate = paramNode.GetChild("SinkRate").ToFixed().ToFloat(); m_SinkAccel = paramNode.GetChild("SinkAccel").ToFixed().ToFloat(); m_CurrentTime = 0.f; - m_TotalSinkDepth = -1.f; + m_ReallyInited = false; // Detect unsafe misconfiguration if (m_Active && !ENTITY_IS_LOCAL(GetEntityId())) @@ -146,11 +206,14 @@ break; } + CFixedVector3D pos = cmpPosition->GetPosition(); + // Compute the depth the first time this is called // (This is a bit of an ugly place to do it but at least we'll be sure // the actor component was loaded already) - if (m_TotalSinkDepth < 0.f) + if (!m_ReallyInited) { + m_ReallyInited = true; m_TotalSinkDepth = 1.f; // minimum so we always sink at least a little CmpPtr cmpVisual(GetEntityHandle()); @@ -163,8 +226,6 @@ // If this is a floating unit, we want it to sink all the way under the terrain, // so find the difference between its current position and the terrain - CFixedVector3D pos = cmpPosition->GetPosition(); - CmpPtr cmpTerrain(GetSystemEntity()); if (cmpTerrain) { @@ -173,26 +234,91 @@ } // Sink it further down if it sinks like a ship, as it will rotate. - if (m_ShipSink) + if (m_DecayType == DECAY_SHIP) { // lacking randomness we'll trick m_SinkingAngleX = (pos.X.ToInt_RoundToNearest() % 30 - 15) / 15.0; m_SinkingAngleZ = (pos.Z.ToInt_RoundToNearest() % 30) / 40.0; m_TotalSinkDepth += 10.f; + + // probably 0 in both cases but we'll remember it anyway. + m_InitialXRotation = cmpPosition->GetRotation().X; + m_InitialZRotation = cmpPosition->GetRotation().Z; } - // probably 0 in both cases but we'll remember it anyway. - m_InitialXRotation = cmpPosition->GetRotation().X; - m_InitialZRotation = cmpPosition->GetRotation().Z; } m_CurrentTime += msgData.deltaSimTime; - if (m_CurrentTime >= m_DelayTime) + if (m_DecayType == DECAY_PHYSICS) + { + // decay our force and apply it + CmpPtr cmpTerrain(GetSystemEntity()); + CmpPtr cmpWaterMgr(GetSystemEntity()); + + pos.X += fixed::FromFloat(m_Force.X*msgData.deltaSimTime); + pos.Y += fixed::FromFloat(m_Force.Y*msgData.deltaSimTime); + pos.Z += fixed::FromFloat(m_Force.Z*msgData.deltaSimTime); + + float groundLevel = cmpTerrain->GetExactGroundLevel(pos.X.ToFloat(),pos.Z.ToFloat()); + float waterLevel = cmpWaterMgr->GetExactWaterLevel(pos.X.ToFloat(),pos.Z.ToFloat()); + + m_Force.Y -= m_Weight * 0.2f * msgData.deltaSimTime; + + if (pos.Y.ToFloat() < waterLevel) + { + // just die. + m_DecayType = DECAY_CLASSIC; + m_ReallyInited = false; + + cmpPosition->SetFloating(true); + m_DelayTime = m_CurrentTime-1; + m_SinkRate = 0.1; + m_SinkAccel = 0.1; + m_LastPositionCheck = m_CurrentTime; // avoids an update. + } + else if (pos.Y.ToFloat() > groundLevel) + { + // just fly + m_Force.X -= m_Force.X * m_AirFriction*msgData.deltaSimTime; + m_Force.Y -= m_Force.Y * m_AirFriction*msgData.deltaSimTime; + m_Force.Z -= m_Force.Z * m_AirFriction*msgData.deltaSimTime; + } + else + { + // we've hit the ground + CVector3D normal = cmpTerrain->CalcExactNormal(pos.X.ToFloat(),pos.Z.ToFloat()); + float length = m_Force.Length(); + m_Force *= 1.0f/length; + // reflect the force, losing some speed in the process. + float dot = m_Force.Dot(normal); + float dampen = (1.0f-(m_GroundFriction-dot)); + if (dampen < 0.0f) + dampen = 0.0f; + m_Force = (normal * -2 * dot + m_Force) * dampen * length; + + pos.Y = fixed::FromFloat(groundLevel); + } + // checked if we stopped moving. + if (m_CurrentTime - m_LastPositionCheck > 1) + { + if ((m_PositionOneSecAgo-pos).Length().ToFloat() < 0.5f) + { + m_DecayType = DECAY_CLASSIC; + m_DelayTime += m_CurrentTime; + m_ReallyInited = false; + } + m_PositionOneSecAgo = pos; + m_LastPositionCheck = m_CurrentTime; + } + cmpPosition->MoveTo(pos.X,pos.Z); + cmpPosition->SetHeightFixed(pos.Y); + } + else if (m_CurrentTime >= m_DelayTime) { float t = m_CurrentTime - m_DelayTime; float depth = (m_SinkRate * t) + (m_SinkAccel * t * t); - if (m_ShipSink) + if (m_DecayType == DECAY_SHIP) { // exponential sinking with tilting float tilt_time = t > 5.f ? 5.f : t; @@ -216,6 +342,12 @@ } } } + virtual void ApplyForce(float x, float y, float z) + { + m_Force.X += x; + m_Force.Y += y; + m_Force.Z += z; + } }; REGISTER_COMPONENT_TYPE(Decay) Index: source/simulation2/components/ICmpDecay.h =================================================================== --- source/simulation2/components/ICmpDecay.h +++ source/simulation2/components/ICmpDecay.h @@ -26,6 +26,9 @@ class ICmpDecay : public IComponent { public: + // adds directly to the fake physics "force". + virtual void ApplyForce(float x, float y, float z) = 0; + DECLARE_INTERFACE_TYPE(Decay) }; Index: source/simulation2/components/ICmpDecay.cpp =================================================================== --- source/simulation2/components/ICmpDecay.cpp +++ source/simulation2/components/ICmpDecay.cpp @@ -22,4 +22,5 @@ #include "simulation2/system/InterfaceScripted.h" BEGIN_INTERFACE_WRAPPER(Decay) + DEFINE_INTERFACE_METHOD_3("ApplyForce", void, ICmpDecay, ApplyForce, float, float, float) END_INTERFACE_WRAPPER(Decay)