Add accelerations in unit movement.

This helps preventing arrow dodging.

Differential Revision: D3200
Reviewed By: Freagarach
Comments By: wraitii, vladislav, Palaxin, Stan
refs: #5106

This was SVN commit r25953.
This commit is contained in:
bb 2021-10-09 21:31:11 +00:00
parent 4640a1fd36
commit acc780bcbb
79 changed files with 226 additions and 43 deletions

View file

@ -437,10 +437,12 @@ function GetTemplateDataHelper(template, player, auraTemplates, modifiers = {})
if (template.UnitMotion)
{
const walkSpeed = getEntityValue("UnitMotion/WalkSpeed");
ret.speed = {
"walk": getEntityValue("UnitMotion/WalkSpeed"),
"walk": walkSpeed,
"run": walkSpeed,
"acceleration": getEntityValue("UnitMotion/Acceleration")
};
ret.speed.run = getEntityValue("UnitMotion/WalkSpeed");
if (template.UnitMotion.RunMultiplier)
ret.speed.run *= getEntityValue("UnitMotion/RunMultiplier");
}

View file

@ -1005,12 +1005,13 @@ function getSpeedTooltip(template)
if (!template.speed)
return "";
let walk = template.speed.walk.toFixed(1);
let run = template.speed.run.toFixed(1);
const walk = template.speed.walk.toFixed(1);
const run = template.speed.run.toFixed(1);
if (walk == 0 && run == 0)
return "";
const acceleration = template.speed.acceleration.toFixed(1);
return sprintf(translate("%(label)s %(speeds)s"), {
"label": headerFont(translate("Speed:")),
"speeds":
@ -1022,6 +1023,11 @@ function getSpeedTooltip(template)
sprintf(translate("%(speed)s %(movementType)s"), {
"speed": run,
"movementType": unitFont(translate("Run"))
}) +
commaFont(translate(", ")) +
sprintf(translate("%(speed)s %(movementType)s"), {
"speed": acceleration,
"movementType": unitFont(translate("Acceleration"))
})
});
}

View file

@ -895,17 +895,22 @@ Formation.prototype.ComputeMotionParameters = function()
{
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()

View file

@ -585,7 +585,8 @@ GuiInterface.prototype.GetEntityState = function(player, ent)
if (cmpUnitMotion)
ret.speed = {
"walk": cmpUnitMotion.GetWalkSpeed(),
"run": cmpUnitMotion.GetWalkSpeed() * cmpUnitMotion.GetRunMultiplier()
"run": cmpUnitMotion.GetWalkSpeed() * cmpUnitMotion.GetRunMultiplier(),
"acceleration": cmpUnitMotion.GetAcceleration()
};
let cmpUpkeep = Engine.QueryInterface(ent, IID_Upkeep);

View file

@ -345,6 +345,16 @@ UnitMotionFlying.prototype.GetSpeedMultiplier = function()
return this.speed / +this.template.MaxSpeed;
};
UnitMotionFlying.prototype.GetAcceleration = function()
{
return +this.template.AccelRate;
};
UnitMotionFlying.prototype.SetAcceleration = function()
{
// Acceleration is set by the template. Ignore.
};
UnitMotionFlying.prototype.GetPassabilityClassName = function()
{
return this.template.PassabilityClass;

View file

@ -94,6 +94,7 @@ TestTargetEntityRenaming(
"MoveToTargetRange": () => true,
"GetRunMultiplier": () => 1,
"SetSpeedMultiplier": () => {},
"GetAcceleration": () => 1,
"StopMoving": () => {}
});
@ -168,6 +169,7 @@ function TestFormationExiting(mode)
AddMock(unit, IID_UnitMotion, {
"GetWalkSpeed": () => 1,
"GetAcceleration": () => 1,
"MoveToFormationOffset": (target, x, z) => {},
"MoveToTargetRange": (target, min, max) => true,
"SetMemberOfFormation": () => {},
@ -243,6 +245,7 @@ function TestFormationExiting(mode)
"GetWalkSpeed": () => 1,
"StopMoving": () => {},
"SetSpeedMultiplier": () => {},
"SetAcceleration": (accel) => {},
"MoveToPointRange": () => true,
"SetFacePointAfterMove": () => {},
"GetFacePointAfterMove": () => true,
@ -348,6 +351,7 @@ function TestMoveIntoFormationWhileAttacking()
AddMock(unit + i, IID_UnitMotion, {
"GetWalkSpeed": () => 1,
"GetAcceleration": () => 1,
"MoveToFormationOffset": (target, x, z) => {},
"MoveToTargetRange": (target, min, max) => true,
"SetMemberOfFormation": () => {},
@ -414,6 +418,7 @@ function TestMoveIntoFormationWhileAttacking()
AddMock(controller, IID_UnitMotion, {
"GetWalkSpeed": () => 1,
"SetSpeedMultiplier": (speed) => {},
"SetAcceleration": (accel) => {},
"MoveToPointRange": (x, z, minRange, maxRange) => {},
"StopMoving": () => {},
"SetFacePointAfterMove": () => {},

View file

@ -27,6 +27,7 @@
</StatusBars>
<UnitMotion>
<WalkSpeed op="mul">0.45</WalkSpeed>
<Acceleration op="mul">0.45</Acceleration>
</UnitMotion>
<VisualActor>
<Actor>fauna/camel.xml</Actor>

View file

@ -42,6 +42,7 @@
</UnitAI>
<UnitMotion>
<WalkSpeed op="mul">0.15</WalkSpeed>
<Acceleration op="mul">0.15</Acceleration>
</UnitMotion>
<VisualActor>
<Actor>fauna/chicken.xml</Actor>

View file

@ -47,6 +47,7 @@
</StatusBars>
<UnitMotion>
<WalkSpeed op="mul">0.3</WalkSpeed>
<Acceleration op="mul">0.3</Acceleration>
</UnitMotion>
<VisualActor>
<Actor>fauna/crocodile.xml</Actor>

View file

@ -28,6 +28,7 @@
</StatusBars>
<UnitMotion>
<WalkSpeed op="mul">0.8</WalkSpeed>
<Acceleration op="mul">0.8</Acceleration>
</UnitMotion>
<VisualActor>
<Actor>fauna/donkey.xml</Actor>

View file

@ -20,6 +20,7 @@
</StatusBars>
<UnitMotion>
<WalkSpeed op="mul">0.6</WalkSpeed>
<Acceleration op="mul">0.6</Acceleration>
</UnitMotion>
<VisualActor>
<Actor>fauna/giraffe_adult.xml</Actor>

View file

@ -20,6 +20,7 @@
</StatusBars>
<UnitMotion>
<WalkSpeed op="mul">0.6</WalkSpeed>
<Acceleration op="mul">0.6</Acceleration>
</UnitMotion>
<VisualActor>
<Actor>fauna/giraffe_baby.xml</Actor>

View file

@ -35,6 +35,7 @@
</StatusBars>
<UnitMotion>
<WalkSpeed op="mul">0.45</WalkSpeed>
<Acceleration op="mul">0.45</Acceleration>
</UnitMotion>
<VisualActor>
<Actor>fauna/goat.xml</Actor>

View file

@ -28,6 +28,7 @@
</StatusBars>
<UnitMotion>
<WalkSpeed op="mul">0.8</WalkSpeed>
<Acceleration op="mul">0.8</Acceleration>
</UnitMotion>
<VisualActor>
<Actor>fauna/horse.xml</Actor>

View file

@ -36,6 +36,7 @@
</StatusBars>
<UnitMotion>
<WalkSpeed op="mul">0.45</WalkSpeed>
<Acceleration op="mul">0.45</Acceleration>
</UnitMotion>
<VisualActor>
<Actor>fauna/lion.xml</Actor>

View file

@ -40,6 +40,7 @@
</UnitAI>
<UnitMotion>
<WalkSpeed op="mul">0.3</WalkSpeed>
<Acceleration op="mul">0.3</Acceleration>
</UnitMotion>
<VisualActor>
<Actor>fauna/peacock.xml</Actor>

View file

@ -35,6 +35,7 @@
</StatusBars>
<UnitMotion>
<WalkSpeed op="mul">0.45</WalkSpeed>
<Acceleration op="mul">0.45</Acceleration>
</UnitMotion>
<VisualActor>
<Actor>fauna/pig1.xml</Actor>

View file

@ -29,6 +29,7 @@
</Sound>
<UnitMotion>
<WalkSpeed op="mul">3.5</WalkSpeed>
<Acceleration op="mul">3.5</Acceleration>
</UnitMotion>
<Upgrade disable=""/>
<VisualActor>

View file

@ -19,6 +19,7 @@
</StatusBars>
<UnitMotion>
<WalkSpeed op="mul">0.25</WalkSpeed>
<Acceleration op="mul">0.25</Acceleration>
</UnitMotion>
<VisualActor>
<Actor>fauna/piglet.xml</Actor>

View file

@ -45,6 +45,7 @@
<UnitMotion>
<PassabilityClass>ship-small</PassabilityClass>
<WalkSpeed op="mul">0.6</WalkSpeed>
<Acceleration op="mul">0.6</Acceleration>
</UnitMotion>
<Visibility>
<RetainInFog>false</RetainInFog>

View file

@ -35,6 +35,7 @@
</StatusBars>
<UnitMotion>
<WalkSpeed op="mul">0.45</WalkSpeed>
<Acceleration op="mul">0.45</Acceleration>
</UnitMotion>
<VisualActor>
<Actor>fauna/sheep3.xml</Actor>

View file

@ -20,6 +20,7 @@
</StatusBars>
<UnitMotion>
<WalkSpeed op="mul">0.9</WalkSpeed>
<Acceleration op="mul">0.9</Acceleration>
</UnitMotion>
<VisualActor>
<Actor>fauna/wildebeest.xml</Actor>

View file

@ -20,6 +20,7 @@
</StatusBars>
<UnitMotion>
<WalkSpeed op="mul">0.9</WalkSpeed>
<Acceleration op="mul">0.9</Acceleration>
</UnitMotion>
<VisualActor>
<Actor>fauna/zebra.xml</Actor>

View file

@ -69,8 +69,10 @@
</UnitAI>
<UnitMotion>
<FormationController>true</FormationController>
<WalkSpeed>1.0</WalkSpeed>
<RunMultiplier>100.0</RunMultiplier> <!-- = Max formation speed -->
<WalkSpeed>1</WalkSpeed>
<RunMultiplier>100</RunMultiplier> <!-- = Max formation speed -->
<InstantTurnAngle>0.1</InstantTurnAngle>
<Acceleration>100</Acceleration> <!-- Unused -->
<PassabilityClass>large</PassabilityClass>
</UnitMotion>
</Entity>

View file

@ -123,8 +123,10 @@
<UnitMotion>
<FormationController>false</FormationController>
<PassabilityClass>default</PassabilityClass>
<WalkSpeed>9.0</WalkSpeed>
<WalkSpeed>9</WalkSpeed>
<RunMultiplier>1.67</RunMultiplier>
<InstantTurnAngle>1.5</InstantTurnAngle>
<Acceleration>18</Acceleration>
</UnitMotion>
<Visibility>
<RetainInFog>false</RetainInFog>

View file

@ -52,6 +52,7 @@
<UnitMotion>
<PassabilityClass>large</PassabilityClass>
<WalkSpeed op="mul">0.55</WalkSpeed>
<Acceleration op="mul">0.275</Acceleration>
</UnitMotion>
<VisualActor>
<Actor>units/global/catafalque.xml</Actor>

View file

@ -36,5 +36,6 @@
</Sound>
<UnitMotion>
<WalkSpeed op="mul">0.85</WalkSpeed>
<Acceleration op="mul">0.85</Acceleration>
</UnitMotion>
</Entity>

View file

@ -41,5 +41,6 @@
</Sound>
<UnitMotion>
<WalkSpeed op="mul">0.8</WalkSpeed>
<Acceleration op="mul">0.8</Acceleration>
</UnitMotion>
</Entity>

View file

@ -36,5 +36,6 @@
</Sound>
<UnitMotion>
<WalkSpeed op="mul">0.9</WalkSpeed>
<Acceleration op="mul">0.9</Acceleration>
</UnitMotion>
</Entity>

View file

@ -42,5 +42,6 @@
</Sound>
<UnitMotion>
<WalkSpeed op="mul">0.85</WalkSpeed>
<Acceleration op="mul">0.85</Acceleration>
</UnitMotion>
</Entity>

View file

@ -42,5 +42,6 @@
</Sound>
<UnitMotion>
<WalkSpeed op="mul">0.8</WalkSpeed>
<Acceleration op="mul">0.8</Acceleration>
</UnitMotion>
</Entity>

View file

@ -42,5 +42,6 @@
</Sound>
<UnitMotion>
<WalkSpeed op="mul">0.9</WalkSpeed>
<Acceleration op="mul">0.9</Acceleration>
</UnitMotion>
</Entity>

View file

@ -55,6 +55,7 @@
</StatusBars>
<UnitMotion>
<PassabilityClass>large</PassabilityClass>
<Acceleration op="mul">0.5</Acceleration>
</UnitMotion>
<Vision>
<Range>100</Range>

View file

@ -46,5 +46,6 @@
<Turretable/>
<UnitMotion>
<WalkSpeed op="add">1.2</WalkSpeed>
<Acceleration op="add">2.4</Acceleration>
</UnitMotion>
</Entity>

View file

@ -46,5 +46,6 @@
<Turretable/>
<UnitMotion>
<WalkSpeed op="add">0.6</WalkSpeed>
<Acceleration op="add">1.2</Acceleration>
</UnitMotion>
</Entity>

View file

@ -46,5 +46,6 @@
<Turretable/>
<UnitMotion>
<WalkSpeed op="mul">1.2</WalkSpeed>
<Acceleration op="mul">1.2</Acceleration>
</UnitMotion>
</Entity>

View file

@ -46,5 +46,6 @@
</Sound>
<UnitMotion>
<WalkSpeed op="mul">0.9</WalkSpeed>
<Acceleration op="mul">0.9</Acceleration>
</UnitMotion>
</Entity>

View file

@ -67,6 +67,7 @@
<UnitMotion>
<WalkSpeed op="mul">1.5</WalkSpeed>
<RunMultiplier>2</RunMultiplier>
<Acceleration op="mul">1.5</Acceleration>
</UnitMotion>
<Vision>
<Range>30</Range>

View file

@ -54,6 +54,7 @@
</StatusBars>
<UnitMotion>
<PassabilityClass>large</PassabilityClass>
<Acceleration op="mul">0.5</Acceleration>
</UnitMotion>
<Vision>
<Range>100</Range>

View file

@ -46,6 +46,7 @@
</UnitAI>
<UnitMotion>
<WalkSpeed op="mul">0.7</WalkSpeed>
<Acceleration op="mul">0.7</Acceleration>
</UnitMotion>
<Visibility>
<RetainInFog>true</RetainInFog>

View file

@ -11,5 +11,6 @@
<UnitMotion>
<WalkSpeed op="mul">0.4</WalkSpeed>
<RunMultiplier>1.4</RunMultiplier>
<Acceleration op="mul">0.4</Acceleration>
</UnitMotion>
</Entity>

View file

@ -16,5 +16,6 @@
<PassabilityClass>large</PassabilityClass>
<WalkSpeed op="mul">0.4</WalkSpeed>
<RunMultiplier>2.0</RunMultiplier>
<Acceleration op="mul">0.4</Acceleration>
</UnitMotion>
</Entity>

View file

@ -21,5 +21,6 @@
<UnitMotion>
<PassabilityClass>large</PassabilityClass>
<WalkSpeed op="mul">0.5</WalkSpeed>
<Acceleration op="mul">0.25</Acceleration>
</UnitMotion>
</Entity>

View file

@ -55,5 +55,6 @@
<PassabilityClass>ship-small</PassabilityClass>
<WalkSpeed op="mul">1.8</WalkSpeed>
<RunMultiplier>1</RunMultiplier>
<Acceleration op="mul">1.8</Acceleration>
</UnitMotion>
</Entity>

View file

@ -14,5 +14,6 @@
</Sound>
<UnitMotion>
<WalkSpeed op="mul">1.6</WalkSpeed>
<Acceleration op="mul">1.6</Acceleration>
</UnitMotion>
</Entity>

View file

@ -42,5 +42,6 @@
</Sound>
<UnitMotion>
<WalkSpeed op="mul">0.85</WalkSpeed>
<Acceleration op="mul">0.85</Acceleration>
</UnitMotion>
</Entity>

View file

@ -42,5 +42,6 @@
</Sound>
<UnitMotion>
<WalkSpeed op="mul">0.8</WalkSpeed>
<Acceleration op="mul">0.8</Acceleration>
</UnitMotion>
</Entity>

View file

@ -42,5 +42,6 @@
</Sound>
<UnitMotion>
<WalkSpeed op="mul">0.9</WalkSpeed>
<Acceleration op="mul">0.9</Acceleration>
</UnitMotion>
</Entity>

View file

@ -73,5 +73,6 @@
</StatusBars>
<UnitMotion>
<PassabilityClass>large</PassabilityClass>
<Acceleration op="mul">0.5</Acceleration>
</UnitMotion>
</Entity>

View file

@ -50,5 +50,6 @@
</Sound>
<UnitMotion>
<WalkSpeed op="mul">0.9</WalkSpeed>
<Acceleration op="mul">0.9</Acceleration>
</UnitMotion>
</Entity>

View file

@ -48,5 +48,6 @@
</Sound>
<UnitMotion>
<WalkSpeed op="add">1.2</WalkSpeed>
<Acceleration op="add">2.4</Acceleration>
</UnitMotion>
</Entity>

View file

@ -49,5 +49,6 @@
</Sound>
<UnitMotion>
<WalkSpeed op="add">0.6</WalkSpeed>
<Acceleration op="add">1.2</Acceleration>
</UnitMotion>
</Entity>

View file

@ -47,5 +47,6 @@
</Sound>
<UnitMotion>
<WalkSpeed op="add">2.4</WalkSpeed>
<Acceleration op="add">4.8</Acceleration>
</UnitMotion>
</Entity>

View file

@ -49,5 +49,6 @@
</Sound>
<UnitMotion>
<WalkSpeed op="mul">1.2</WalkSpeed>
<Acceleration op="mul">1.2</Acceleration>
</UnitMotion>
</Entity>

View file

@ -73,5 +73,7 @@
<TreasureCollector disable=""/>
<UnitMotion>
<PassabilityClass>ship</PassabilityClass>
<InstantTurnAngle>0.5</InstantTurnAngle>
<Acceleration op="mul">0.25</Acceleration>
</UnitMotion>
</Entity>

View file

@ -59,6 +59,7 @@
</Sound>
<UnitMotion>
<WalkSpeed op="mul">1.55</WalkSpeed>
<Acceleration op="mul">1.55</Acceleration>
</UnitMotion>
<Vision>
<Range>90</Range>

View file

@ -47,6 +47,7 @@
<UnitMotion>
<PassabilityClass>ship-small</PassabilityClass>
<WalkSpeed op="mul">1.6</WalkSpeed>
<Acceleration op="mul">1.6</Acceleration>
</UnitMotion>
<Vision>
<Range>60</Range>

View file

@ -71,6 +71,7 @@
<UnitMotion>
<PassabilityClass>ship-small</PassabilityClass>
<WalkSpeed op="mul">1.1</WalkSpeed>
<Acceleration op="mul">1.1</Acceleration>
</UnitMotion>
<Vision>
<Range>30</Range>

View file

@ -39,6 +39,7 @@
<UnitMotion>
<PassabilityClass>ship-small</PassabilityClass>
<WalkSpeed op="mul">1.35</WalkSpeed>
<Acceleration op="mul">1.6</Acceleration>
</UnitMotion>
<Vision>
<Range>50</Range>

View file

@ -67,6 +67,7 @@
</Sound>
<UnitMotion>
<WalkSpeed op="mul">1.8</WalkSpeed>
<Acceleration op="mul">1.8</Acceleration>
</UnitMotion>
<Vision>
<Range>110</Range>

View file

@ -62,6 +62,7 @@
</Sound>
<UnitMotion>
<WalkSpeed op="mul">1.8</WalkSpeed>
<Acceleration op="mul">1.8</Acceleration>
</UnitMotion>
<Vision>
<Range>90</Range>

View file

@ -60,5 +60,7 @@
<UnitMotion>
<PassabilityClass>large</PassabilityClass>
<RunMultiplier>1</RunMultiplier>
<InstantTurnAngle>0.75</InstantTurnAngle>
<Acceleration op="mul">0.25</Acceleration>
</UnitMotion>
</Entity>

View file

@ -71,6 +71,7 @@
</UnitAI>
<UnitMotion>
<WalkSpeed op="mul">0.9</WalkSpeed>
<Acceleration op="mul">0.9</Acceleration>
</UnitMotion>
<Vision>
<Range>100</Range>

View file

@ -59,6 +59,7 @@
</Sound>
<UnitMotion>
<WalkSpeed op="mul">0.8</WalkSpeed>
<Acceleration op="mul">0.8</Acceleration>
</UnitMotion>
<Vision>
<Range>80</Range>

View file

@ -64,6 +64,7 @@
</UnitAI>
<UnitMotion>
<WalkSpeed op="mul">0.8</WalkSpeed>
<Acceleration op="mul">0.8</Acceleration>
</UnitMotion>
<Vision>
<Range>100</Range>

View file

@ -95,6 +95,7 @@
</Resistance>
<UnitMotion>
<WalkSpeed op="mul">0.7</WalkSpeed>
<Acceleration op="mul">0.7</Acceleration>
</UnitMotion>
<Vision>
<Range>80</Range>

View file

@ -70,6 +70,7 @@
<UnitMotion>
<PassabilityClass>large</PassabilityClass>
<WalkSpeed op="mul">0.6</WalkSpeed>
<Acceleration op="mul">0.3</Acceleration>
</UnitMotion>
<Vision>
<Range>50</Range>

View file

@ -23,6 +23,7 @@
</Loot>
<UnitMotion>
<WalkSpeed op="mul">0.9</WalkSpeed>
<Acceleration op="mul">0.9</Acceleration>
</UnitMotion>
<VisualActor>
<Actor>structures/celts/warship.xml</Actor>

View file

@ -17,6 +17,7 @@
</Resistance>
<UnitMotion>
<WalkSpeed op="mul">1.4</WalkSpeed>
<Acceleration op="mul">1.4</Acceleration>
</UnitMotion>
<VisualActor>
<Actor>units/gauls/infantry_spearman_c.xml</Actor>

View file

@ -23,6 +23,7 @@
</Loot>
<UnitMotion>
<WalkSpeed op="mul">0.9</WalkSpeed>
<Acceleration op="mul">0.9</Acceleration>
</UnitMotion>
<VisualActor>
<Actor>structures/celts/warship.xml</Actor>

View file

@ -24,6 +24,7 @@
</Loot>
<UnitMotion>
<WalkSpeed op="mul">0.9</WalkSpeed>
<Acceleration op="mul">0.9</Acceleration>
</UnitMotion>
<VisualActor>
<Actor>structures/iberians/warship.xml</Actor>

View file

@ -23,6 +23,7 @@
</Loot>
<UnitMotion>
<WalkSpeed op="mul">0.9</WalkSpeed>
<Acceleration op="mul">0.9</Acceleration>
</UnitMotion>
<VisualActor>
<Actor>structures/mauryas/trireme.xml</Actor>

View file

@ -36,6 +36,7 @@
</TrainingRestrictions>
<UnitMotion>
<WalkSpeed op="mul">1.5</WalkSpeed>
<Acceleration op="mul">1.5</Acceleration>
</UnitMotion>
<Vision>
<Range op="mul">0.5</Range>

View file

@ -37,6 +37,7 @@
</TrainingRestrictions>
<UnitMotion>
<WalkSpeed op="mul">1.4</WalkSpeed>
<Acceleration op="mul">1.4</Acceleration>
</UnitMotion>
<Vision>
<Range op="mul">0.5</Range>

View file

@ -153,7 +153,7 @@ public:
bool m_IsFormationController;
fixed m_TemplateWalkSpeed, m_TemplateRunMultiplier;
fixed m_TemplateWalkSpeed, m_TemplateRunMultiplier, m_TemplateAcceleration;
pass_class_t m_PassClass;
std::string m_PassClassName;
@ -225,8 +225,12 @@ public:
// 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;
// The speed achieved at the end of the current turn.
fixed m_CurrentSpeed;
fixed m_InstantTurnAngle;
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.
@ -244,15 +248,21 @@ public:
"<element name='FormationController'>"
"<data type='boolean'/>"
"</element>"
"<element name='WalkSpeed' a:help='Basic movement speed (in metres per second)'>"
"<element name='WalkSpeed' a:help='Basic movement speed (in metres per second).'>"
"<ref name='positiveDecimal'/>"
"</element>"
"<optional>"
"<element name='RunMultiplier' a:help='How much faster the unit goes when running (as a multiple of walk speed)'>"
"<element name='RunMultiplier' a:help='How much faster the unit goes when running (as a multiple of walk speed).'>"
"<ref name='positiveDecimal'/>"
"</element>"
"</optional>"
"<element name='PassabilityClass' a:help='Identifies the terrain passability class (values are defined in special/pathfinder.xml)'>"
"<element name='InstantTurnAngle' a:help='Angle we can turn instantly. Any value greater than pi will disable turning times. Avoid zero since it stops the entity every turn.'>"
"<ref name='positiveDecimal'/>"
"</element>"
"<element name='Acceleration' a:help='Acceleration (in metres per second^2).'>"
"<ref name='positiveDecimal'/>"
"</element>"
"<element name='PassabilityClass' a:help='Identifies the terrain passability class (values are defined in special/pathfinder.xml).'>"
"<text/>"
"</element>"
"<optional>"
@ -270,12 +280,16 @@ public:
m_WalkSpeed = m_TemplateWalkSpeed = m_Speed = paramNode.GetChild("WalkSpeed").ToFixed();
m_SpeedMultiplier = fixed::FromInt(1);
m_CurSpeed = fixed::Zero();
m_CurrentSpeed = fixed::Zero();
m_RunMultiplier = m_TemplateRunMultiplier = fixed::FromInt(1);
if (paramNode.GetChild("RunMultiplier").IsOk())
m_RunMultiplier = m_TemplateRunMultiplier = paramNode.GetChild("RunMultiplier").ToFixed();
m_InstantTurnAngle = paramNode.GetChild("InstantTurnAngle").ToFixed();
m_Acceleration = m_TemplateAcceleration = paramNode.GetChild("Acceleration").ToFixed();
CmpPtr<ICmpPathfinder> cmpPathfinder(GetSystemEntity());
if (cmpPathfinder)
{
@ -322,7 +336,11 @@ public:
serialize.NumberFixed_Unbounded("speed multiplier", m_SpeedMultiplier);
serialize.NumberFixed_Unbounded("current speed", m_CurSpeed);
serialize.NumberFixed_Unbounded("current speed", m_CurrentSpeed);
serialize.NumberFixed_Unbounded("instant turn angle", m_InstantTurnAngle);
serialize.NumberFixed_Unbounded("acceleration", m_Acceleration);
serialize.Bool("facePointAfterMove", m_FacePointAfterMove);
serialize.Bool("pushing", m_Pushing);
@ -456,15 +474,25 @@ public:
CFixedVector2D pos = cmpPosition->GetPosition2D();
entity_angle_t angle = cmpPosition->GetRotation().Y;
fixed speed = m_CurrentSpeed;
// Copy the path so we don't change it.
WaypointPath shortPath = m_ShortPath;
WaypointPath longPath = m_LongPath;
PerformMove(dt, cmpPosition->GetTurnRate(), shortPath, longPath, pos, angle);
PerformMove(dt, cmpPosition->GetTurnRate(), shortPath, longPath, pos, speed, angle);
return pos;
}
virtual fixed GetAcceleration() const
{
return m_Acceleration;
}
virtual void SetAcceleration(fixed acceleration)
{
m_Acceleration = acceleration;
}
virtual pass_class_t GetPassabilityClass() const
{
return m_PassClass;
@ -485,7 +513,7 @@ public:
virtual fixed GetCurrentSpeed() const
{
return m_CurSpeed;
return m_CurrentSpeed;
}
virtual void SetFacePointAfterMove(bool facePointAfterMove)
@ -721,13 +749,13 @@ private:
* This does not send actually change the position.
* @returns true if the move was obstructed.
*/
bool PerformMove(fixed dt, const fixed& turnRate, WaypointPath& shortPath, WaypointPath& longPath, CFixedVector2D& pos, entity_angle_t& angle) const;
bool PerformMove(fixed dt, const fixed& turnRate, WaypointPath& shortPath, WaypointPath& longPath, CFixedVector2D& pos, fixed& speed, entity_angle_t& angle) const;
/**
* Update other components on our speed.
* (For performance, this should try to avoid sending messages).
*/
void UpdateMovementState(entity_pos_t speed);
void UpdateMovementState(entity_pos_t speed, entity_pos_t meanSpeed);
/**
* React if our move was obstructed.
@ -993,7 +1021,7 @@ void CCmpUnitMotion::PreMove(CCmpUnitMotionManager::MotionState& state)
// If we were idle and will still be, no need for an update.
state.needUpdate = state.cmpPosition->IsInWorld() &&
(m_CurSpeed != fixed::Zero() || m_MoveRequest.m_Type != MoveRequest::NONE);
(m_CurrentSpeed != fixed::Zero() || m_MoveRequest.m_Type != MoveRequest::NONE);
if (!m_BlockMovement)
return;
@ -1017,7 +1045,7 @@ void CCmpUnitMotion::Move(CCmpUnitMotionManager::MotionState& state, fixed dt)
// to it, then throw away our current path and go straight to it.
state.wentStraight = TryGoingStraightToTarget(state.initialPos, true);
state.wasObstructed = PerformMove(dt, state.cmpPosition->GetTurnRate(), m_ShortPath, m_LongPath, state.pos, state.angle);
state.wasObstructed = PerformMove(dt, state.cmpPosition->GetTurnRate(), m_ShortPath, m_LongPath, state.pos, state.speed, state.angle);
}
void CCmpUnitMotion::PostMove(CCmpUnitMotionManager::MotionState& state, fixed dt)
@ -1027,7 +1055,7 @@ void CCmpUnitMotion::PostMove(CCmpUnitMotionManager::MotionState& state, fixed d
{
if (state.angle != state.initialAngle)
state.cmpPosition->TurnTo(state.angle);
UpdateMovementState(fixed::Zero());
UpdateMovementState(fixed::Zero(), fixed::Zero());
}
else
{
@ -1041,7 +1069,7 @@ void CCmpUnitMotion::PostMove(CCmpUnitMotionManager::MotionState& state, fixed d
state.cmpPosition->MoveAndTurnTo(state.pos.X, state.pos.Y, state.angle);
// Calculate the mean speed over this past turn.
UpdateMovementState(offset.Length() / dt);
UpdateMovementState(state.speed, offset.Length() / dt);
}
if (state.wasObstructed && HandleObstructedMove(state.pos != state.initialPos))
@ -1094,7 +1122,7 @@ bool CCmpUnitMotion::PossiblyAtDestination() const
return false;
}
bool CCmpUnitMotion::PerformMove(fixed dt, const fixed& turnRate, WaypointPath& shortPath, WaypointPath& longPath, CFixedVector2D& pos, entity_angle_t& angle) const
bool CCmpUnitMotion::PerformMove(fixed dt, const fixed& turnRate, 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())
@ -1151,21 +1179,26 @@ bool CCmpUnitMotion::PerformMove(fixed dt, const fixed& turnRate, WaypointPath&
target = CFixedVector2D(shortPath.m_Waypoints.back().x, shortPath.m_Waypoints.back().z);
CFixedVector2D offset = target - pos;
if (turnRate > zero && !offset.IsZero())
fixed angleDiff = angle - atan2_approx(offset.X, offset.Y);
fixed absoluteAngleDiff = angleDiff.Absolute();
if (absoluteAngleDiff > entity_angle_t::Pi())
absoluteAngleDiff = entity_angle_t::Pi() * 2 - absoluteAngleDiff;
// We only rotate to the instantTurnAngle angle. The rest we rotate during movement.
if (absoluteAngleDiff > m_InstantTurnAngle)
{
fixed maxRotation = turnRate.Multiply(timeLeft);
fixed angleDiff = angle - atan2_approx(offset.X, offset.Y);
if (angleDiff != zero)
// Stop moving when rotating this far.
speed = zero;
if (turnRate > zero && !offset.IsZero())
{
fixed absoluteAngleDiff = angleDiff.Absolute();
if (absoluteAngleDiff > entity_angle_t::Pi())
absoluteAngleDiff = entity_angle_t::Pi() * 2 - absoluteAngleDiff;
fixed maxRotation = turnRate.Multiply(timeLeft);
// Figure out whether rotating will increase or decrease the angle, and how far we need to rotate in that direction.
int direction = (entity_angle_t::Zero() < angleDiff && angleDiff <= entity_angle_t::Pi()) || angleDiff < -entity_angle_t::Pi() ? -1 : 1;
// Can't rotate far enough, just rotate in the correct direction.
if (absoluteAngleDiff > maxRotation)
if (absoluteAngleDiff - m_InstantTurnAngle > maxRotation)
{
angle += maxRotation * direction;
if (angle * direction > entity_angle_t::Pi())
@ -1174,13 +1207,21 @@ bool CCmpUnitMotion::PerformMove(fixed dt, const fixed& turnRate, WaypointPath&
}
// Rotate towards the next waypoint and continue moving.
angle = atan2_approx(offset.X, offset.Y);
// Give some 'free' rotation for angles below 0.5 radians.
timeLeft = (std::min(maxRotation, maxRotation - absoluteAngleDiff + fixed::FromInt(1)/2)) / turnRate;
timeLeft = std::min(maxRotation, maxRotation - absoluteAngleDiff + m_InstantTurnAngle) / turnRate;
}
}
else
{
// Modify the speed depending on the angle difference.
fixed sin, cos;
sincos_approx(angleDiff, sin, cos);
speed = speed.Multiply(cos);
}
// Work out how far we can travel in timeLeft.
fixed maxdist = maxSpeed.Multiply(timeLeft);
fixed accelTime = std::min(timeLeft, (maxSpeed - speed) / m_Acceleration);
fixed accelDist = speed.Multiply(accelTime) + accelTime.Square().Multiply(m_Acceleration) / 2;
fixed maxdist = accelDist + maxSpeed.Multiply(timeLeft - accelTime);
// If the target is close, we can move there directly.
fixed offsetLength = offset.Length();
@ -1191,7 +1232,20 @@ bool CCmpUnitMotion::PerformMove(fixed dt, const fixed& turnRate, WaypointPath&
pos = target;
// Spend the rest of the time heading towards the next waypoint.
timeLeft = (maxdist - offsetLength) / maxSpeed;
// Either we still need to accelerate after, or we have reached maxSpeed.
// The former is much less likely than the latter: usually we can reach
// maxSpeed within one waypoint. So the Sqrt is not too bad.
if (offsetLength <= accelDist)
{
fixed requiredTime = (-speed + (speed.Square() + offsetLength.Multiply(m_Acceleration).Multiply(fixed::FromInt(2))).Sqrt()) / m_Acceleration;
timeLeft -= requiredTime;
speed += m_Acceleration.Multiply(requiredTime);
}
else
{
timeLeft -= accelTime + (offsetLength - accelDist) / maxSpeed;
speed = maxSpeed;
}
if (shortPath.m_Waypoints.empty())
longPath.m_Waypoints.pop_back();
@ -1212,6 +1266,8 @@ bool CCmpUnitMotion::PerformMove(fixed dt, const fixed& turnRate, WaypointPath&
offset.Normalize(maxdist);
target = pos + offset;
speed = std::min(maxSpeed, speed + m_Acceleration.Multiply(timeLeft));
if (cmpPathfinder->CheckMovement(GetObstructionFilter(specificIgnore), pos.X, pos.Y, target.X, target.Y, m_Clearance, m_PassClass))
pos = target;
else
@ -1223,18 +1279,18 @@ bool CCmpUnitMotion::PerformMove(fixed dt, const fixed& turnRate, WaypointPath&
return false;
}
void CCmpUnitMotion::UpdateMovementState(entity_pos_t speed)
void CCmpUnitMotion::UpdateMovementState(entity_pos_t speed, entity_pos_t meanSpeed)
{
CmpPtr<ICmpVisual> cmpVisual(GetEntityHandle());
if (cmpVisual)
{
if (speed == fixed::Zero())
if (meanSpeed == fixed::Zero())
cmpVisual->SelectMovementAnimation("idle", fixed::FromInt(1));
else
cmpVisual->SelectMovementAnimation(speed > (m_WalkSpeed / 2).Multiply(m_RunMultiplier + fixed::FromInt(1)) ? "run" : "walk", speed);
cmpVisual->SelectMovementAnimation(meanSpeed > (m_WalkSpeed / 2).Multiply(m_RunMultiplier + fixed::FromInt(1)) ? "run" : "walk", meanSpeed);
}
m_CurSpeed = speed;
m_CurrentSpeed = speed;
}
bool CCmpUnitMotion::HandleObstructedMove(bool moved)

View file

@ -60,6 +60,8 @@ public:
// Accumulated "pushing" from nearby units.
CFixedVector2D push;
fixed speed;
fixed initialAngle;
fixed angle;

View file

@ -167,6 +167,7 @@ void CCmpUnitMotionManager::Move(EntityMap<MotionState>& ents, fixed dt)
it->second.initialPos = it->second.cmpPosition->GetPosition2D();
it->second.initialAngle = it->second.cmpPosition->GetRotation().Y;
it->second.pos = it->second.initialPos;
it->second.speed = it->second.cmpUnitMotion->GetCurrentSpeed();
it->second.angle = it->second.initialAngle;
ENSURE(it->second.pos.X.ToInt_RoundToZero() / PUSHING_GRID_SIZE < m_MovingUnits.width() &&
it->second.pos.Y.ToInt_RoundToZero() / PUSHING_GRID_SIZE < m_MovingUnits.height());

View file

@ -37,6 +37,8 @@ DEFINE_INTERFACE_METHOD("GetWalkSpeed", ICmpUnitMotion, GetWalkSpeed)
DEFINE_INTERFACE_METHOD("GetRunMultiplier", ICmpUnitMotion, GetRunMultiplier)
DEFINE_INTERFACE_METHOD("EstimateFuturePosition", ICmpUnitMotion, EstimateFuturePosition)
DEFINE_INTERFACE_METHOD("SetSpeedMultiplier", ICmpUnitMotion, SetSpeedMultiplier)
DEFINE_INTERFACE_METHOD("GetAcceleration", ICmpUnitMotion, GetAcceleration)
DEFINE_INTERFACE_METHOD("SetAcceleration", ICmpUnitMotion, SetAcceleration)
DEFINE_INTERFACE_METHOD("GetPassabilityClassName", ICmpUnitMotion, GetPassabilityClassName)
DEFINE_INTERFACE_METHOD("GetUnitClearance", ICmpUnitMotion, GetUnitClearance)
DEFINE_INTERFACE_METHOD("SetFacePointAfterMove", ICmpUnitMotion, SetFacePointAfterMove)
@ -124,6 +126,16 @@ public:
return m_Script.Call<CFixedVector2D>("EstimateFuturePosition", dt);
}
virtual fixed GetAcceleration() const
{
return m_Script.Call<fixed>("GetAcceleration");
}
virtual void SetAcceleration(fixed acceleration)
{
m_Script.CallVoid("SetAcceleration", acceleration);
}
virtual void SetFacePointAfterMove(bool facePointAfterMove)
{
m_Script.CallVoid("SetFacePointAfterMove", facePointAfterMove);

View file

@ -87,7 +87,7 @@ public:
virtual void StopMoving() = 0;
/**
* Get the distance travelled over the last turn.
* Get the speed at the end of the current turn.
*/
virtual fixed GetCurrentSpeed() const = 0;
@ -129,6 +129,17 @@ public:
*/
virtual CFixedVector2D EstimateFuturePosition(const fixed dt) 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.
*/