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
@@ -899,17 +899,22 @@
{
let maxRadius = 0;
let minSpeed = Infinity;
+ let minAcceleration = Infinity;
for (let ent of this.members)
{
let cmpUnitMotion = Engine.QueryInterface(ent, IID_UnitMotion);
if (cmpUnitMotion)
+ {
minSpeed = Math.min(minSpeed, cmpUnitMotion.GetWalkSpeed());
+ minAcceleration = Math.min(minAcceleration, cmpUnitMotion.GetAcceleration());
+ }
}
minSpeed *= this.GetSpeedMultiplier();
let cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
cmpUnitMotion.SetSpeedMultiplier(minSpeed / cmpUnitMotion.GetWalkSpeed());
+ cmpUnitMotion.SetAcceleration(minAcceleration);
};
Formation.prototype.ShapeUpdate = function()
Index: binaries/data/mods/public/simulation/components/tests/test_UnitAI.js
===================================================================
--- binaries/data/mods/public/simulation/components/tests/test_UnitAI.js
+++ binaries/data/mods/public/simulation/components/tests/test_UnitAI.js
@@ -92,6 +92,7 @@
"MoveToTargetRange": () => true,
"GetRunMultiplier": () => 1,
"SetSpeedMultiplier": () => {},
+ "GetAcceleration": () => 1,
"StopMoving": () => {}
});
@@ -171,6 +172,7 @@
AddMock(unit, IID_UnitMotion, {
"GetWalkSpeed": () => 1,
+ "GetAcceleration": () => 1,
"MoveToFormationOffset": (target, x, z) => {},
"MoveToTargetRange": (target, min, max) => true,
"StopMoving": () => {},
@@ -242,6 +244,7 @@
"GetWalkSpeed": () => 1,
"StopMoving": () => {},
"SetSpeedMultiplier": () => {},
+ "SetAcceleration": (accel) => {},
"MoveToPointRange": () => true,
"SetFacePointAfterMove": () => {},
"GetFacePointAfterMove": () => true,
@@ -346,6 +349,7 @@
AddMock(unit + i, IID_UnitMotion, {
"GetWalkSpeed": () => 1,
+ "GetAcceleration": () => 1,
"MoveToFormationOffset": (target, x, z) => {},
"MoveToTargetRange": (target, min, max) => true,
"StopMoving": () => {},
@@ -408,6 +412,7 @@
AddMock(controller, IID_UnitMotion, {
"GetWalkSpeed": () => 1,
"SetSpeedMultiplier": (speed) => {},
+ "SetAcceleration": (accel) => {},
"MoveToPointRange": (x, z, minRange, maxRange) => {},
"StopMoving": () => {},
"SetFacePointAfterMove": () => {},
Index: binaries/data/mods/public/simulation/templates/template_formation.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_formation.xml
+++ binaries/data/mods/public/simulation/templates/template_formation.xml
@@ -71,5 +71,6 @@
1.0
100.0
large
+ 6
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
@@ -132,6 +132,7 @@
default
9.0
1.67
+ 4
false
Index: source/simulation2/components/CCmpUnitMotion.cpp
===================================================================
--- source/simulation2/components/CCmpUnitMotion.cpp
+++ source/simulation2/components/CCmpUnitMotion.cpp
@@ -128,7 +128,7 @@
bool m_FormationController;
- fixed m_TemplateWalkSpeed, m_TemplateRunMultiplier;
+ fixed m_TemplateWalkSpeed, m_TemplateRunMultiplier, m_TemplateAcceleration;
pass_class_t m_PassClass;
std::string m_PassClassName;
@@ -190,8 +190,12 @@
// This caches the resulting speed from m_WalkSpeed * m_SpeedMultiplier for convenience.
fixed m_Speed;
- // Current mean speed (over the last turn).
- fixed m_CurSpeed;
+ // Current mean speed over the last turn.
+ fixed m_CurMeanSpeed;
+ // The highest speed achieved on the last turn. Usually at the end of the last turn. Only zero if did't move last and this turn.
+ fixed m_CurTopSpeed;
+
+ fixed m_Acceleration;
// Currently active paths (storing waypoints in reverse order).
// The last item in each path is the point we're currently heading towards.
@@ -217,6 +221,9 @@
""
""
""
+ ""
+ ""
+ ""
""
""
"";
@@ -230,12 +237,15 @@
m_WalkSpeed = m_TemplateWalkSpeed = m_Speed = paramNode.GetChild("WalkSpeed").ToFixed();
m_SpeedMultiplier = fixed::FromInt(1);
- m_CurSpeed = fixed::Zero();
+ m_CurMeanSpeed = fixed::Zero();
+ m_CurTopSpeed = fixed::Zero();
m_RunMultiplier = m_TemplateRunMultiplier = fixed::FromInt(1);
if (paramNode.GetChild("RunMultiplier").IsOk())
m_RunMultiplier = m_TemplateRunMultiplier = paramNode.GetChild("RunMultiplier").ToFixed();
+ m_Acceleration = m_TemplateAcceleration = paramNode.GetChild("Acceleration").ToFixed();
+
CmpPtr cmpPathfinder(GetSystemEntity());
if (cmpPathfinder)
{
@@ -275,7 +285,10 @@
serialize.NumberFixed_Unbounded("speed multiplier", m_SpeedMultiplier);
- serialize.NumberFixed_Unbounded("current speed", m_CurSpeed);
+ serialize.NumberFixed_Unbounded("current mean speed", m_CurMeanSpeed);
+ serialize.NumberFixed_Unbounded("current top speed", m_CurTopSpeed);
+
+ serialize.NumberFixed_Unbounded("acceleration", m_Acceleration);
serialize.Bool("facePointAfterMove", m_FacePointAfterMove);
@@ -339,6 +352,7 @@
const CMessageValueModification& msgData = static_cast (msg);
if (msgData.component != L"UnitMotion")
break;
+
FALLTHROUGH;
}
case MT_OwnershipChanged:
@@ -398,6 +412,16 @@
return m_RunMultiplier;
}
+ virtual fixed GetAcceleration() const
+ {
+ return m_Acceleration;
+ }
+
+ virtual void SetAcceleration(fixed acceleration)
+ {
+ m_Acceleration = acceleration;
+ }
+
virtual pass_class_t GetPassabilityClass() const
{
return m_PassClass;
@@ -418,7 +442,7 @@
virtual fixed GetCurrentSpeed() const
{
- return m_CurSpeed;
+ return m_CurMeanSpeed;
}
virtual void SetFacePointAfterMove(bool facePointAfterMove)
@@ -616,7 +640,7 @@
* This does not send actually change the position.
* @returns true if the move was obstructed.
*/
- bool PerformMove(fixed dt, WaypointPath& shortPath, WaypointPath& longPath, CFixedVector2D& pos) const;
+ bool PerformMove(fixed dt, WaypointPath& shortPath, WaypointPath& longPath, CFixedVector2D& pos, fixed& speed,entity_angle_t angle) const;
/**
* Update other components on our speed.
@@ -849,7 +873,7 @@
// If we were idle and will still be, we can return.
// TODO: this will need to be removed if pushing is implemented.
- if (m_CurSpeed == fixed::Zero() && m_MoveRequest.m_Type == MoveRequest::NONE)
+ if (m_CurTopSpeed == fixed::Zero() && m_CurMeanSpeed == fixed::Zero() && m_MoveRequest.m_Type == MoveRequest::NONE)
return;
if (PossiblyAtDestination())
@@ -881,7 +905,7 @@
// to it, then throw away our current path and go straight to it
bool wentStraight = TryGoingStraightToTarget(initialPos);
- bool wasObstructed = PerformMove(dt, m_ShortPath, m_LongPath, pos);
+ bool wasObstructed = PerformMove(dt, m_ShortPath, m_LongPath, pos, m_CurTopSpeed, cmpPosition->GetRotation().Y);
// Update our speed over this turn so that the visual actor shows the correct animation.
if (pos == initialPos)
@@ -942,7 +966,7 @@
return false;
}
-bool CCmpUnitMotion::PerformMove(fixed dt, WaypointPath& shortPath, WaypointPath& longPath, CFixedVector2D& pos) const
+bool CCmpUnitMotion::PerformMove(fixed dt, WaypointPath& shortPath, WaypointPath& longPath, CFixedVector2D& pos, fixed& speed, entity_angle_t angle) const
{
// If there are no waypoint, behave as though we were obstructed and let HandleObstructedMove handle it.
if (shortPath.m_Waypoints.empty() && longPath.m_Waypoints.empty())
@@ -969,11 +993,16 @@
fixed maxSpeed = basicSpeed.Multiply(terrainSpeed);
- // We want to move (at most) maxSpeed*dt units from pos towards the next waypoint
+ // We want to move (at most) std::min(maxSpeed * dt, nowSpeed * dt + 1/2 * acceleration * dt^2) units from pos towards the next waypoint
fixed timeLeft = dt;
fixed zero = fixed::Zero();
+ // Take the current rotation as the previous direction, slight TODO, might need to be the previous angle.
+ fixed sin, cos;
+ sincos_approx(angle, sin, cos);
+ CFixedVector2D prevOffset(sin, cos);
+
while (timeLeft > zero)
{
// If we ran out of path, we have to stop
@@ -988,20 +1017,27 @@
CFixedVector2D offset = target - pos;
- // Work out how far we can travel in timeLeft
- fixed maxdist = maxSpeed.Multiply(timeLeft);
+ // Modify the speed depending on the angle difference between previous and next offset.
+ sincos_approx(atan2_approx(offset.X, offset.Y) / 2 - atan2_approx(prevOffset.X, prevOffset.Y) / 2, sin, cos);
+ speed = speed.Multiply(cos);
+
+ // Work out how far we can travel in timeLeft.
+ fixed maxdist = std::min(maxSpeed, speed + m_Acceleration.Multiply(dt) / 2).Multiply(timeLeft);
- // If the target is close, we can move there directly
+ // If the target is close, we can move there directly.
fixed offsetLength = offset.Length();
if (offsetLength <= maxdist)
{
if (cmpPathfinder->CheckMovement(GetObstructionFilter(), pos.X, pos.Y, target.X, target.Y, m_Clearance, m_PassClass))
{
+ prevOffset = offset;
pos = target;
// Spend the rest of the time heading towards the next waypoint
timeLeft = (maxdist - offsetLength) / maxSpeed;
+ speed = std::min(maxSpeed, speed + m_Acceleration.Multiply(dt - timeLeft));
+
if (shortPath.m_Waypoints.empty())
longPath.m_Waypoints.pop_back();
else
@@ -1021,6 +1057,8 @@
offset.Normalize(maxdist);
target = pos + offset;
+ speed = std::min(maxSpeed, speed + m_Acceleration.Multiply(dt));
+
if (cmpPathfinder->CheckMovement(GetObstructionFilter(), pos.X, pos.Y, target.X, target.Y, m_Clearance, m_PassClass))
pos = target;
else
@@ -1036,8 +1074,11 @@
{
CmpPtr cmpObstruction(GetEntityHandle());
CmpPtr cmpVisual(GetEntityHandle());
+ // Not moving this and last turn.
+ if (speed == fixed::Zero() && m_CurMeanSpeed == fixed::Zero())
+ m_CurTopSpeed = fixed::Zero();
// Moved last turn, didn't this turn.
- if (speed == fixed::Zero() && m_CurSpeed > fixed::Zero())
+ else if (speed == fixed::Zero() && m_CurMeanSpeed > fixed::Zero())
{
if (cmpObstruction)
cmpObstruction->SetMovingFlag(false);
@@ -1045,18 +1086,18 @@
cmpVisual->SelectMovementAnimation("idle", fixed::FromInt(1));
}
// Moved this turn, didn't last turn
- else if (speed > fixed::Zero() && m_CurSpeed == fixed::Zero())
+ else if (speed > fixed::Zero() && m_CurMeanSpeed == fixed::Zero())
{
if (cmpObstruction)
cmpObstruction->SetMovingFlag(true);
if (cmpVisual)
- cmpVisual->SelectMovementAnimation(m_Speed > m_WalkSpeed ? "run" : "walk", m_Speed);
+ cmpVisual->SelectMovementAnimation(m_Speed > m_WalkSpeed ? "run" : "walk", speed);
}
// Speed change, update the visual actor if necessary.
- else if (speed != m_CurSpeed && cmpVisual)
- cmpVisual->SelectMovementAnimation(m_Speed > m_WalkSpeed ? "run" : "walk", m_Speed);
+ else if (speed != m_CurMeanSpeed && cmpVisual)
+ cmpVisual->SelectMovementAnimation(m_Speed > m_WalkSpeed ? "run" : "walk", speed);
- m_CurSpeed = speed;
+ m_CurMeanSpeed = speed;
}
bool CCmpUnitMotion::HandleObstructedMove()
Index: source/simulation2/components/ICmpUnitMotion.h
===================================================================
--- source/simulation2/components/ICmpUnitMotion.h
+++ source/simulation2/components/ICmpUnitMotion.h
@@ -117,6 +117,17 @@
virtual fixed GetSpeed() const = 0;
/**
+ * Get the current acceleration.
+ */
+ virtual fixed GetAcceleration() const = 0;
+
+ /**
+ * Set the current acceleration.
+ * @param acceleration The acceleration.
+ */
+ virtual void SetAcceleration(fixed acceleration) = 0;
+
+ /**
* Set whether the unit will turn to face the target point after finishing moving.
*/
virtual void SetFacePointAfterMove(bool facePointAfterMove) = 0;
Index: source/simulation2/components/ICmpUnitMotion.cpp
===================================================================
--- source/simulation2/components/ICmpUnitMotion.cpp
+++ source/simulation2/components/ICmpUnitMotion.cpp
@@ -35,6 +35,8 @@
DEFINE_INTERFACE_METHOD_CONST_0("GetWalkSpeed", fixed, ICmpUnitMotion, GetWalkSpeed)
DEFINE_INTERFACE_METHOD_CONST_0("GetRunMultiplier", fixed, ICmpUnitMotion, GetRunMultiplier)
DEFINE_INTERFACE_METHOD_1("SetSpeedMultiplier", void, ICmpUnitMotion, SetSpeedMultiplier, fixed)
+DEFINE_INTERFACE_METHOD_CONST_0("GetAcceleration", fixed, ICmpUnitMotion, GetAcceleration)
+DEFINE_INTERFACE_METHOD_1("SetAcceleration", void, ICmpUnitMotion, SetAcceleration, fixed)
DEFINE_INTERFACE_METHOD_CONST_0("GetPassabilityClassName", std::string, ICmpUnitMotion, GetPassabilityClassName)
DEFINE_INTERFACE_METHOD_CONST_0("GetUnitClearance", entity_pos_t, ICmpUnitMotion, GetUnitClearance)
DEFINE_INTERFACE_METHOD_1("SetFacePointAfterMove", void, ICmpUnitMotion, SetFacePointAfterMove, bool)
@@ -112,6 +114,16 @@
return m_Script.Call("GetSpeedMultiplier");
}
+ virtual fixed GetAcceleration() const
+ {
+ return m_Script.Call("GetAcceleration");
+ }
+
+ virtual void SetAcceleration(fixed acceleration)
+ {
+ m_Script.CallVoid("SetAcceleration", acceleration);
+ }
+
virtual void SetFacePointAfterMove(bool facePointAfterMove)
{
m_Script.CallVoid("SetFacePointAfterMove", facePointAfterMove);