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
@@ -349,7 +349,7 @@
this.MoveToMembersCenter();
// Compute the speed etc. of the formation.
- this.ComputeMotionParameters();
+ this.ComputeSpeed();
};
/**
@@ -391,13 +391,11 @@
return;
}
- this.ComputeMotionParameters();
-
- if (!this.rearrange)
- return;
+ this.ComputeSpeed();
// Rearrange the remaining members.
- this.MoveMembersIntoFormation(true, true, this.lastOrderVariant);
+ if (this.rearrange)
+ this.MoveMembersIntoFormation(true, true, this.lastOrderVariant);
};
Formation.prototype.AddMembers = function(ents)
@@ -427,12 +425,10 @@
}
}
- this.ComputeMotionParameters();
-
- if (!this.rearrange)
- return;
+ this.ComputeSpeed();
- this.MoveMembersIntoFormation(true, true, this.lastOrderVariant);
+ if (this.rearrange)
+ this.MoveMembersIntoFormation(true, true, this.lastOrderVariant);
};
/**
@@ -555,6 +551,10 @@
}
this.width = xMax - xMin;
this.depth = yMax - yMin;
+
+ // The formation turn rate depends on the positioning of the members.
+ if (offsetsChanged)
+ this.ComputeTurnRate();
};
Formation.prototype.MoveToMembersCenter = function()
@@ -873,23 +873,42 @@
/**
* Set formation controller's speed based on its current members.
*/
-Formation.prototype.ComputeMotionParameters = function()
+Formation.prototype.ComputeSpeed = function()
{
- let maxRadius = 0;
let minSpeed = Infinity;
- for (let ent of this.members)
+ for (const ent of this.members)
{
- let cmpUnitMotion = Engine.QueryInterface(ent, IID_UnitMotion);
+ const cmpUnitMotion = Engine.QueryInterface(ent, IID_UnitMotion);
if (cmpUnitMotion)
minSpeed = Math.min(minSpeed, cmpUnitMotion.GetWalkSpeed());
}
minSpeed *= this.GetSpeedMultiplier();
-
- let cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
+ const cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
cmpUnitMotion.SetSpeedMultiplier(minSpeed / cmpUnitMotion.GetWalkSpeed());
};
+/**
+ * Set formation controller's turn rate based on its current shape.
+ */
+Formation.prototype.ComputeTurnRate = function()
+{
+ if (!this.offsets || !this.offsets.length)
+ return;
+
+ let minTurnRateSq = Infinity;
+ const speedMultiplier = this.GetSpeedMultiplier();
+ for (const offset of this.offsets)
+ {
+ const cmpUnitMotion = Engine.QueryInterface(offset.ent, IID_UnitMotion);
+ if (cmpUnitMotion)
+ minTurnRateSq = Math.min(
+ minTurnRateSq,
+ Math.square(cmpUnitMotion.GetWalkSpeed() * cmpUnitMotion.GetRunMultiplier() * speedMultiplier) / (offset.x * offset.x + offset.y * offset.y));
+ }
+ Engine.QueryInterface(this.entity, IID_Position).SetTurnRate(Math.sqrt(minTurnRateSq));
+};
+
Formation.prototype.ShapeUpdate = function()
{
if (!this.rearrange)
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
@@ -168,6 +168,7 @@
AddMock(unit, IID_UnitMotion, {
"GetWalkSpeed": () => 1,
+ "GetRunMultiplier": () => 1,
"MoveToFormationOffset": (target, x, z) => {},
"MoveToTargetRange": (target, min, max) => true,
"SetMemberOfFormation": () => {},
@@ -235,6 +236,7 @@
"GetPosition": function() { return new Vector3D(this.x, 0, this.z); },
"GetPosition2D": function() { return new Vector2D(this.x, this.z); },
"GetRotation": function() { return { "y": 0 }; },
+ "SetTurnRate": () => {},
"IsInWorld": function() { return true; },
"MoveOutOfWorld": () => {}
});
@@ -348,6 +350,7 @@
AddMock(unit + i, IID_UnitMotion, {
"GetWalkSpeed": () => 1,
+ "GetRunMultiplier": () => 1,
"MoveToFormationOffset": (target, x, z) => {},
"MoveToTargetRange": (target, min, max) => true,
"SetMemberOfFormation": () => {},
@@ -407,6 +410,7 @@
"GetPosition": function(){ return new Vector3D(this.x, 0, this.z); },
"GetPosition2D": function(){ return new Vector2D(this.x, this.z); },
"GetRotation": () => ({ "y": 0 }),
+ "SetTurnRate": () => {},
"IsInWorld": () => true,
"MoveOutOfWorld": () => {},
});
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
@@ -54,7 +54,7 @@
upright
false
0
- 10
+ 0
0.75
Index: source/simulation2/components/CCmpPosition.cpp
===================================================================
--- source/simulation2/components/CCmpPosition.cpp
+++ source/simulation2/components/CCmpPosition.cpp
@@ -132,7 +132,7 @@
""
""
""
- ""
+ ""
"";
}
@@ -552,6 +552,11 @@
return m_RotYSpeed;
}
+ virtual void SetTurnRate(fixed turnRate)
+ {
+ m_RotYSpeed = turnRate;
+ }
+
virtual void TurnTo(entity_angle_t y)
{
if (m_TurretParent != INVALID_ENTITY)
Index: source/simulation2/components/ICmpPosition.h
===================================================================
--- source/simulation2/components/ICmpPosition.h
+++ source/simulation2/components/ICmpPosition.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2020 Wildfire Games.
+/* Copyright (C) 2021 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -71,6 +71,11 @@
virtual entity_id_t GetTurretParent() const = 0;
/**
+ * Set the turn rate in radians per second.
+ */
+ virtual void SetTurnRate(fixed turnRate) = 0;
+
+ /**
* Has to be called to update the simulation position of the turret
*/
virtual void UpdateTurretPosition() = 0;
Index: source/simulation2/components/ICmpPosition.cpp
===================================================================
--- source/simulation2/components/ICmpPosition.cpp
+++ source/simulation2/components/ICmpPosition.cpp
@@ -43,6 +43,7 @@
DEFINE_INTERFACE_METHOD("GetPreviousPosition", ICmpPosition, GetPreviousPosition)
DEFINE_INTERFACE_METHOD("GetPreviousPosition2D", ICmpPosition, GetPreviousPosition2D)
DEFINE_INTERFACE_METHOD("GetTurnRate", ICmpPosition, GetTurnRate)
+DEFINE_INTERFACE_METHOD("SetTurnRate", ICmpPosition, SetTurnRate)
DEFINE_INTERFACE_METHOD("TurnTo", ICmpPosition, TurnTo)
DEFINE_INTERFACE_METHOD("SetYRotation", ICmpPosition, SetYRotation)
DEFINE_INTERFACE_METHOD("SetXZRotation", ICmpPosition, SetXZRotation)
Index: source/simulation2/components/tests/test_RangeManager.h
===================================================================
--- source/simulation2/components/tests/test_RangeManager.h
+++ source/simulation2/components/tests/test_RangeManager.h
@@ -64,6 +64,7 @@
virtual CFixedVector3D GetPreviousPosition() const { return CFixedVector3D(); }
virtual CFixedVector2D GetPreviousPosition2D() const { return CFixedVector2D(); }
virtual fixed GetTurnRate() const { return fixed::Zero(); }
+ virtual void SetTurnRate(fixed UNUSED(turnRate)) { }
virtual void TurnTo(entity_angle_t UNUSED(y)) { }
virtual void SetYRotation(entity_angle_t UNUSED(y)) { }
virtual void SetXZRotation(entity_angle_t UNUSED(x), entity_angle_t UNUSED(z)) { }