Index: binaries/data/mods/public/simulation/components/Formation.js =================================================================== --- binaries/data/mods/public/simulation/components/Formation.js +++ binaries/data/mods/public/simulation/components/Formation.js @@ -378,6 +378,10 @@ } this.ComputeMotionParameters(); + + if (!this.rearrange) + return; + this.MoveMembersIntoFormation(true, true); }; Index: binaries/data/mods/public/simulation/components/UnitAI.js =================================================================== --- binaries/data/mods/public/simulation/components/UnitAI.js +++ binaries/data/mods/public/simulation/components/UnitAI.js @@ -240,7 +240,15 @@ this.FinishOrder(); return; } - +let cmpPhysics = Engine.QueryInterface(this.entity, IID_Physics); +if (cmpPhysics) +{ + cmpPhysics.SetActive(true); + cmpPhysics.ApplyForce(0, 750, 0); + //let cmpPos = Engine.QueryInterface(this.entity, IID_Position); + //if (cmpPos) + // cmpPos.SetHeightFixed(50); +} // Stop moving immediately. this.StopMoving(); this.FinishOrder(); 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 @@ -71,6 +71,15 @@ + + false + 0.5 + 9.81 + 0.5 + 75 + 1000 + 0 + 0 upright Index: source/simulation2/TypeList.h =================================================================== --- source/simulation2/TypeList.h +++ source/simulation2/TypeList.h @@ -132,6 +132,9 @@ INTERFACE(Pathfinder) COMPONENT(Pathfinder) +INTERFACE(Physics) +COMPONENT(Physics) + INTERFACE(Player) COMPONENT(PlayerScripted) Index: source/simulation2/components/CCmpPhysics.cpp =================================================================== --- /dev/null +++ source/simulation2/components/CCmpPhysics.cpp @@ -0,0 +1,259 @@ +/* Copyright (C) 2020 Wildfire Games. + * This file is part of 0 A.D. + * + * 0 A.D. is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 0 A.D. is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 0 A.D. If not, see . + */ + +#include "precompiled.h" + +#include "simulation2/system/Component.h" +#include "ICmpPhysics.h" + +#include "simulation2/MessageTypes.h" + +#include "ICmpPosition.h" +#include "ICmpTerrain.h" +#include "ICmpWaterManager.h" + +#include "ps/Profile.h" + +/** + * Fairly basic physics implementation, for units and buildings etc. + */ +class CCmpPhysics : public ICmpPhysics +{ +public: + static void ClassInit(CComponentManager& UNUSED(componentManager)) + { + } + + DEFAULT_COMPONENT_ALLOCATOR(Physics) + + bool m_Active; + + entity_pos_t m_AirFriction; + entity_pos_t m_GroundFriction; + entity_pos_t m_Gravity; + entity_pos_t m_Mass; + entity_pos_t m_MaxForce; + entity_pos_t m_MomentOfInertia; + + CVector3D m_Velocity; + CVector3D m_Acceleration; + + static std::string GetSchema() + { + return + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + ""; + } + + virtual void Init(const CParamNode& paramNode) + { + m_Active = paramNode.GetChild("Active").ToBool(); + + m_AirFriction = paramNode.GetChild("AirFriction").ToFixed(); + m_GroundFriction = paramNode.GetChild("GroundFriction").ToFixed(); + m_Gravity = paramNode.GetChild("Gravity").ToFixed(); + m_Mass = paramNode.GetChild("Mass").ToFixed(); + m_MaxForce = paramNode.GetChild("MaxForce").ToFixed(); + m_MomentOfInertia = paramNode.GetChild("MomentOfInertia").ToFixed(); + + if (m_Active) + GetSimContext().GetComponentManager().DynamicSubscriptionNonsync(MT_Update, this, true); + } + + virtual void Deinit() + { + } + + virtual void Serialize(ISerializer& serialize) + { + serialize.Bool("active", m_Active); + + serialize.NumberFixed_Unbounded("frictionair", m_AirFriction); + serialize.NumberFixed_Unbounded("gravity", m_Gravity); + serialize.NumberFixed_Unbounded("frictionground", m_GroundFriction); + serialize.NumberFixed_Unbounded("mass", m_Mass); + serialize.NumberFixed_Unbounded("maxforce", m_MaxForce); + serialize.NumberFixed_Unbounded("momentofinertia", m_MomentOfInertia); + + serialize.NumberFloat_Unbounded("velocityx", m_Velocity.X); + serialize.NumberFloat_Unbounded("velocityy", m_Velocity.Y); + serialize.NumberFloat_Unbounded("velocityz", m_Velocity.Z); + + serialize.NumberFloat_Unbounded("accelerationx", m_Acceleration.X); + serialize.NumberFloat_Unbounded("accelerationy", m_Acceleration.Y); + serialize.NumberFloat_Unbounded("accelerationz", m_Acceleration.Z); + } + + virtual void Deserialize(const CParamNode& paramNode, IDeserializer& deserialize) + { + Init(paramNode); + + deserialize.Bool("active", m_Active); + + deserialize.NumberFixed_Unbounded("frictionair", m_AirFriction); + deserialize.NumberFixed_Unbounded("gravity", m_Gravity); + deserialize.NumberFixed_Unbounded("frictionground", m_GroundFriction); + deserialize.NumberFixed_Unbounded("mass", m_Mass); + deserialize.NumberFixed_Unbounded("maxforce", m_MaxForce); + deserialize.NumberFixed_Unbounded("momentofinertia", m_MomentOfInertia); + + deserialize.NumberFloat_Unbounded("velocityx", m_Velocity.X); + deserialize.NumberFloat_Unbounded("velocityy", m_Velocity.Y); + deserialize.NumberFloat_Unbounded("velocityz", m_Velocity.Z); + + deserialize.NumberFloat_Unbounded("accelerationx", m_Acceleration.X); + deserialize.NumberFloat_Unbounded("accelerationy", m_Acceleration.Y); + deserialize.NumberFloat_Unbounded("accelerationz", m_Acceleration.Z); + } + + virtual void HandleMessage(const CMessage& msg, bool UNUSED(global)) + { + switch (msg.GetType()) + { + case MT_Update: + { + PROFILE("Physics::Update"); + + if (!m_Active) + break; + + Update(static_cast (msg).turnLength); + + break; + } + } + } + + void Update(fixed turnLength) + { + // When there is no net energy, there is nothing to do. + //if (m_Energy.X == 0 && m_Energy.Y == 0 && m_Energy.Z == 0) + // return; + + // If there's no position don't do anything. + CmpPtr cmpPosition(GetEntityHandle()); + if (!cmpPosition || !cmpPosition->IsInWorld()) + return; + CVector3D initialPosition = cmpPosition->GetPosition(); + + float dt = turnLength.ToFloat(); + + CVector3D newPosition; + + m_Velocity += m_Acceleration * dt; + newPosition = initialPosition + m_Velocity * dt; + + CmpPtr cmpTerrain(GetSystemEntity()); + CmpPtr cmpWaterMgr(GetSystemEntity()); + + float groundLevel = cmpTerrain->GetExactGroundLevel(newPosition.X, newPosition.Z); + float waterLevel = cmpWaterMgr->GetExactWaterLevel(newPosition.X, newPosition.Z); + +LOGERROR("%s", (fixed::FromFloat(m_Velocity.Y)).ToString()); + + // ToDo: Fix entities landing on water. + // They should just try to stand on the ground actually, + // unless they are dead, then they should float. So that should + // be done in CCmpDecay probably. + // However, when going too deep a unit should die. + if (newPosition.Y < waterLevel && waterLevel > groundLevel && m_Velocity.Y <= 0) + { + cmpPosition->SetFloating(true); + + newPosition.Y = waterLevel; + + // Drain all energy when landing on water. + m_Velocity *= 0; + m_Acceleration *= 0; + } + else if (newPosition.Y > groundLevel) + { + // Simplified Stokes' drag. + m_Acceleration.X -= abs(m_Velocity.X) * (m_AirFriction.ToFloat() / m_Mass.ToFloat()); + m_Acceleration.Y -= abs(m_Velocity.Y) * (m_AirFriction.ToFloat() / m_Mass.ToFloat()); + m_Acceleration.Z -= abs(m_Velocity.Z) * (m_AirFriction.ToFloat() / m_Mass.ToFloat()); + + // Gravity pull. + m_Velocity.Y -= m_Gravity.ToFloat() * dt; + } + else if (newPosition.Y <= groundLevel) + { + CVector3D normal = cmpTerrain->CalcExactNormal(newPosition.X, newPosition.Z); + float normalForce = m_Mass.ToFloat() * m_Gravity.ToFloat(); + + float length = m_Velocity.Length(); + m_Velocity *= 1.0f / length; + + // Reflect the force, losing some speed in the process. + float dotProduct = m_Velocity.Dot(normal); + float dampen = 1.0f - (m_GroundFriction.ToFloat() - dotProduct); + if (dampen < 0.0f) + dampen = 0.0f; + + m_Velocity = (normal * -2 * dotProduct + m_Velocity) * dampen * length; + + newPosition.Y = groundLevel; + } + else + { + // Nothing? + } + + // We should check for collisions. + + cmpPosition->MoveTo(entity_pos_t::FromFloat(newPosition.X), entity_pos_t::FromFloat(newPosition.Z)); + cmpPosition->SetHeightFixed(entity_pos_t::FromFloat(newPosition.Y)); + } + + virtual void ApplyForce(float x, float y, float z) + { + // Correct for time force applies? What are nice values? + m_Acceleration.X += x / m_Mass.ToFloat(); + m_Acceleration.Y += y / m_Mass.ToFloat(); + m_Acceleration.Z += z / m_Mass.ToFloat(); + } + + virtual void SetActive(bool active) + { + if (active != m_Active) + { + GetSimContext().GetComponentManager().DynamicSubscriptionNonsync(MT_Update, this, active); + m_Active = active; + } + } +}; + +REGISTER_COMPONENT_TYPE(Physics) Index: source/simulation2/components/ICmpPhysics.h =================================================================== --- /dev/null +++ source/simulation2/components/ICmpPhysics.h @@ -0,0 +1,39 @@ +/* Copyright (C) 2020 Wildfire Games. + * This file is part of 0 A.D. + * + * 0 A.D. is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 0 A.D. is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 0 A.D. If not, see . + */ + +#ifndef INCLUDED_ICMPPHYSICS +#define INCLUDED_ICMPPHYSICS + +#include "simulation2/system/Interface.h" +#include "simulation2/components/ICmpPosition.h" // for entity_pos_t + +/** + * Simple physics. + */ +class ICmpPhysics : public IComponent +{ +public: + // Adds some force to the fake physics of this entity. + virtual void ApplyForce(float x, float y, float z) = 0; + + // Set whether physics is active for this entity. + virtual void SetActive(bool active) = 0; + + DECLARE_INTERFACE_TYPE(Physics) +}; + +#endif // INCLUDED_ICMPPHYSICS Index: source/simulation2/components/ICmpPhysics.cpp =================================================================== --- /dev/null +++ source/simulation2/components/ICmpPhysics.cpp @@ -0,0 +1,27 @@ +/* Copyright (C) 2020 Wildfire Games. + * This file is part of 0 A.D. + * + * 0 A.D. is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 0 A.D. is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 0 A.D. If not, see . + */ + +#include "precompiled.h" + +#include "ICmpPhysics.h" + +#include "simulation2/system/InterfaceScripted.h" + +BEGIN_INTERFACE_WRAPPER(Physics) +DEFINE_INTERFACE_METHOD_3("ApplyForce", void, ICmpPhysics, ApplyForce, float, float, float) +DEFINE_INTERFACE_METHOD_1("SetActive", void, ICmpPhysics, SetActive, bool) +END_INTERFACE_WRAPPER(Physics)