Index: binaries/data/mods/public/globalscripts/Templates.js
===================================================================
--- binaries/data/mods/public/globalscripts/Templates.js
+++ binaries/data/mods/public/globalscripts/Templates.js
@@ -308,8 +308,8 @@
ret.speed = {
"walk": getEntityValue("UnitMotion/WalkSpeed"),
};
- if (template.UnitMotion.Run)
- ret.speed.run = getEntityValue("UnitMotion/Run/Speed");
+ if (template.UnitMotion.RunMultiplier)
+ ret.speed.run = getEntityValue("UnitMotion/WalkSpeed") * getEntityValue("UnitMotion/RunMultiplier");
}
if (template.ProductionQueue)
Index: binaries/data/mods/public/maps/scenarios/Pathfinding_integrated_testmap.js
===================================================================
--- /dev/null
+++ binaries/data/mods/public/maps/scenarios/Pathfinding_integrated_testmap.js
@@ -0,0 +1,201 @@
+warn("Setting up test");
+
+var tests = {};
+var start = 0;
+
+var failedTests = 0;
+
+Trigger.prototype.setupTests = function()
+{
+ let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
+ start = cmpTimer.GetTime();
+
+ let cmpTechMgr = QueryPlayerIDInterface(1, IID_TechnologyManager);
+
+ //XXXtreme hack: create a fake technology to drastically limit the range of everybody in place.
+ cmpTechMgr.modifications = {};
+ cmpTechMgr.modifications['Attack/Ranged/MaxRange'] = [ {"affects":["Unit"], "replace":2.5} ];
+
+ cmpTechMgr = QueryPlayerIDInterface(2, IID_TechnologyManager);
+ cmpTechMgr.modifications = {};
+ cmpTechMgr.modifications['Attack/Ranged/MaxRange'] = [ {"affects":["Unit"], "replace":0} ];
+
+ let cmpFoundation = Engine.QueryInterface(391, IID_Foundation);
+ cmpFoundation.InitialiseConstruction(1, "structures/maur_house");
+
+ // Ready to order people around.
+
+ tests = {
+ "21" : {"target":353, "continuous":true}, // inbetween house: allies present
+ "200" : {"target":352, "continuous":true, "expectfail":true}, // Inbetweenhouse- should fail
+ "24" : {"target":361}, // basic labyrinth
+ "23" : {"target":356}, // corner house
+ "22" : {"target":354}, // between house + around
+ "49" : {"target":355}, // straight
+ "210" : {"target":357}, // formation - sparse
+ "211" : {"target":358}, // formation - wall
+ "186" : {"target":359, "expectfail" : true}, // inside forest - dense
+ "372" : {"target":359, "continuous":true, "expectfail" : true}, // inside forest - dense
+ "187" : {"target":360}, // inside forest - sparse
+ "50" : {"target":352}, // super long trip
+ "46" : {"target":363}, // trip inside hills
+ "53" : {"target":362}, // labyrinth: with hole for small
+ "54" : {"target":365}, // labyrinth: with hole for small - this is the elephant
+ "85" : {"target":362}, // labyrinth: with hole for small - this is the ram
+ "390" : {"target":391, "type" : "build"}, // build a house
+ "393" : {"target":392, "type" : "hunt"}, // hunt a chicken
+ };
+
+ // order units to move
+ for (let test in tests)
+ {
+ let cmpTesterAI = Engine.QueryInterface(+test, IID_UnitAI);
+ let cmpPos = Engine.QueryInterface(tests[test].target, IID_Position);
+
+ if (tests[test].type && tests[test].type === "build")
+ {
+ cmpTesterAI.Repair(tests[test].target, false, false);
+ continue;
+ }
+ else if (tests[test].type && tests[test].type === "hunt")
+ {
+ cmpTesterAI.Gather(tests[test].target, false);
+ continue;
+ }
+
+ if (!tests[test].continuous)
+ cmpTesterAI.Walk(cmpPos.GetPosition2D().x, cmpPos.GetPosition2D().y, false);
+ else
+ {
+ let TgPos = new Vector2D(cmpPos.GetPosition2D().x,cmpPos.GetPosition2D().y);
+ let MyCmpPos = Engine.QueryInterface(+test, IID_Position);
+ let MyPos = new Vector2D(MyCmpPos.GetPosition2D().x,MyCmpPos.GetPosition2D().y);
+
+ // must be below C++ constant for "short pathfinder only"
+ let vector = new Vector2D(TgPos.x,TgPos.y);
+ vector = vector.sub(MyPos).normalize().mult(3.4); // 2 happened to put a waypoint inside a unit, which is unreachable.
+
+ let position = new Vector2D(MyPos.x,MyPos.y);
+ while (position.distanceToSquared(TgPos) > 12)
+ {
+ position.add(vector);
+ cmpTesterAI.Walk(position.x, position.y, true);
+ }
+ }
+ }
+
+ // set up traders, they're not tested but their behavior should be looked at.
+ let traders = Engine.GetEntitiesWithInterface(IID_Trader);
+ for (let tID of traders)
+ {
+ let cmpTraderAI = Engine.QueryInterface(+tID, IID_UnitAI);
+ cmpTraderAI.SetupTradeRoute(401, 402, undefined, false);
+ }
+
+ let cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
+
+ cmpTrigger.RegisterTrigger("OnInterval", "CheckUnits", {
+ "enabled": true,
+ "delay": 2 * 1000,
+ "interval": 1 * 1000,
+ });
+}
+
+let cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
+cmpTrigger.DoAfterDelay(4000, "setupTests", {});
+
+function Success(test)
+{
+ warn("SUCCESS : test " + test + " succeeded");
+ delete(tests[test]);
+}
+
+function Fail(test)
+{
+ error("ERROR : test " + test + " failed");
+ delete(tests[test]);
+ failedTests++;
+}
+
+function testBuild(test)
+{
+ let cmpFoundation = Engine.QueryInterface(tests[test].target, IID_Foundation);
+ if (cmpFoundation.GetBuildProgress() > 0.1)
+ Success(test);
+}
+
+function testHunt(test)
+{
+ /*
+ let cmpFoundation = Engine.QueryInterface(tests[tester].target, IID_Foundation);
+ if (cmpFoundation.GetBuildProgress() > 0)
+ Success(tester);
+ */
+}
+
+function testWalk(test)
+{
+ let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
+ let time = cmpTimer.GetTime();
+
+ let cmpTesterAI = Engine.QueryInterface(+test, IID_UnitAI);
+ let cmpTesterUM = Engine.QueryInterface(+test, IID_UnitMotion);
+ if (cmpTesterUM.IsTryingToMove())
+ return;
+
+ let cmpPos = Engine.QueryInterface(tests[test].target, IID_Position);
+ let TgPos = new Vector2D(cmpPos.GetPosition2D().x,cmpPos.GetPosition2D().y);
+ let MyCmpPos = Engine.QueryInterface(+test, IID_Position);
+ let MyPos = new Vector2D(MyCmpPos.GetPosition2D().x,MyCmpPos.GetPosition2D().y);
+
+ cmpTesterAI.Stop();
+
+ if (MyPos.distanceTo(TgPos) > 8 || (tests[test].underTime && time > tests[test].underTime))
+ if (!tests[test].expectfail)
+ {
+ Fail(test);
+ return;
+ }
+ else if (tests[test].expectfail && MyPos.distanceTo(TgPos) <= 8 && (!tests[test].underTime || time <= tests[test].underTime))
+ {
+ Fail(test);
+ return;
+ }
+ Success(test);
+}
+
+
+Trigger.prototype.CheckUnits = function(data)
+{
+ // Check all tests
+ let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
+ let time = cmpTimer.GetTime();
+
+ let testsleft = 0;
+ for (let test in tests)
+ {
+ testsleft++;
+ let cmpTesterAI = Engine.QueryInterface(+test, IID_UnitAI);
+
+ if (!tests[test].type)
+ testWalk(test);
+ else if (tests[test].type === "build")
+ testBuild(test);
+ else if (tests[test].type === "hunt")
+ testHunt(test);
+ }
+ if (time > 120000)
+ for (let test in tests)
+ {
+ let cmpTesterAI = Engine.QueryInterface(+test, IID_UnitAI);
+ cmpTesterAI.Stop();
+ Fail(test);
+ }
+ if (testsleft === 0)
+ {
+ if (failedTests > 0)
+ QueryPlayerIDInterface(1, IID_Player).SetState("defeated");
+ else
+ QueryPlayerIDInterface(1, IID_Player).SetState("won");
+ }
+}
Index: binaries/data/mods/public/maps/scenarios/Pathfinding_integrated_testmap.xml
===================================================================
--- /dev/null
+++ binaries/data/mods/public/maps/scenarios/Pathfinding_integrated_testmap.xml
@@ -0,0 +1,2920 @@
+
+
+
+
+ default
+
+
+
+
+
+
+ 0
+ 0.5
+
+
+
+
+ ocean
+
+
+ 177.809
+ 4
+ 0.45
+ 0
+
+
+
+ 0
+ 1
+ 0.99
+ 0.1999
+ default
+
+
+
+
+
+
+
+
+
+
+ units/maur_infantry_archer_b
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ units/maur_infantry_archer_b
+ 1
+
+
+
+
+
+ units/maur_infantry_archer_b
+ 1
+
+
+
+
+
+ units/maur_infantry_archer_b
+ 1
+
+
+
+
+
+ units/maur_infantry_archer_b
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ units/maur_infantry_archer_b
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ units/maur_infantry_archer_b
+ 1
+
+
+
+
+
+ units/maur_infantry_archer_b
+ 1
+
+
+
+
+
+ units/maur_ship_fishing
+ 1
+
+
+
+
+
+ structures/maur_dock
+ 1
+
+
+
+
+
+ units/maur_infantry_archer_b
+ 1
+
+
+
+
+
+ units/maur_champion_elephant
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ units/spart_mechanical_siege_ram
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ units/maur_infantry_archer_b
+ 1
+
+
+
+
+
+ units/maur_infantry_archer_b
+ 1
+
+
+
+
+
+ actor|props/special/common/waypoint_flag_factions.xml
+
+
+
+
+
+ actor|props/special/common/waypoint_flag_factions.xml
+
+
+
+
+
+ actor|props/special/common/waypoint_flag_factions.xml
+
+
+
+
+
+ actor|props/special/common/waypoint_flag_factions.xml
+
+
+
+
+
+ actor|props/special/common/waypoint_flag_factions.xml
+
+
+
+
+
+ actor|props/special/common/waypoint_flag_factions.xml
+
+
+
+
+
+ actor|props/special/common/waypoint_flag_factions.xml
+
+
+
+
+
+ actor|props/special/common/waypoint_flag_factions.xml
+
+
+
+
+
+ actor|props/special/common/waypoint_flag_factions.xml
+
+
+
+
+
+ units/spart_support_female_citizen
+ 2
+
+
+
+
+
+ units/maur_infantry_archer_b
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_civil_centre
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 1
+
+
+
+
+
+ units/maur_infantry_archer_b
+ 1
+
+
+
+
+
+ units/maur_infantry_archer_b
+ 1
+
+
+
+
+
+ actor|props/special/common/waypoint_flag_factions.xml
+
+
+
+
+
+ actor|props/special/common/waypoint_flag_factions.xml
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ units/spart_infantry_javelinist_b
+ 1
+
+
+
+
+
+ other/obelisk
+ 1
+
+
+
+
+
+ other/obelisk
+ 1
+
+
+
+
+
+ other/obelisk
+ 1
+
+
+
+
+
+ other/obelisk
+ 1
+
+
+
+
+
+ other/obelisk
+ 1
+
+
+
+
+
+ other/obelisk
+ 1
+
+
+
+
+
+ other/obelisk
+ 1
+
+
+
+
+
+ other/obelisk
+ 1
+
+
+
+
+
+ other/obelisk
+ 1
+
+
+
+
+
+ other/obelisk
+ 1
+
+
+
+
+
+ other/obelisk
+ 1
+
+
+
+
+
+ other/obelisk
+ 1
+
+
+
+
+
+ other/obelisk
+ 1
+
+
+
+
+
+ other/obelisk
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ units/maur_support_female_citizen
+ 1
+
+
+
+
+
+ units/maur_support_female_citizen
+ 1
+
+
+
+
+
+ units/maur_support_female_citizen
+ 1
+
+
+
+
+
+ units/maur_support_female_citizen
+ 1
+
+
+
+
+
+ units/maur_support_female_citizen
+ 1
+
+
+
+
+
+ units/maur_infantry_archer_b
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ units/maur_infantry_archer_b
+ 1
+
+
+
+
+
+ foundation|structures/maur_house
+ 1
+
+
+
+
+
+ gaia/fauna_chicken
+ 1
+
+
+
+
+
+ units/maur_infantry_archer_b
+ 1
+
+
+
+
+
+ structures/maur_civil_centre
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ structures/maur_house
+ 1
+
+
+
+
+
+ units/maur_infantry_archer_b
+ 1
+
+
+
+
+
+ other/obelisk
+ 1
+
+
+
+
+
+ units/maur_infantry_archer_b
+ 1
+
+
+
+
+
+ gaia/flora_bush_berry
+ 1
+
+
+
+
+
+ structures/maur_market
+ 1
+
+
+
+
+
+ structures/maur_market
+ 1
+
+
+
+
+
+ units/maur_support_trader
+ 1
+
+
+
+
+
+ units/maur_support_trader
+ 1
+
+
+
+
+
+ units/maur_support_trader
+ 1
+
+
+
+
+
+ units/maur_support_trader
+ 1
+
+
+
+
+
+ units/maur_support_trader
+ 1
+
+
+
+
+
+ units/maur_support_trader
+ 1
+
+
+
+
+
+ units/maur_support_trader
+ 1
+
+
+
+
+
+ units/maur_support_trader
+ 1
+
+
+
+
+
+ units/maur_support_trader
+ 1
+
+
+
+
+
+ units/maur_support_trader
+ 1
+
+
+
+
+
+ units/maur_support_trader
+ 1
+
+
+
+
+
+ units/maur_support_trader
+ 1
+
+
+
+
+
+ units/maur_support_trader
+ 1
+
+
+
+
+
+ units/maur_support_trader
+ 1
+
+
+
+
+
+ units/maur_support_trader
+ 1
+
+
+
+
+
+ units/maur_support_trader
+ 1
+
+
+
+
+
+ units/maur_support_trader
+ 1
+
+
+
+
+
+ units/maur_support_trader
+ 1
+
+
+
+
+
+ units/maur_support_trader
+ 1
+
+
+
+
+
+
+
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
@@ -865,7 +865,7 @@
{
var cmpUnitMotion = Engine.QueryInterface(ent, IID_UnitMotion);
if (cmpUnitMotion)
- minSpeed = Math.min(minSpeed, cmpUnitMotion.GetWalkSpeed());
+ minSpeed = Math.min(minSpeed, cmpUnitMotion.GetSpeed());
}
minSpeed *= this.GetSpeedMultiplier();
Index: binaries/data/mods/public/simulation/components/GuiInterface.js
===================================================================
--- binaries/data/mods/public/simulation/components/GuiInterface.js
+++ binaries/data/mods/public/simulation/components/GuiInterface.js
@@ -596,8 +596,9 @@
let cmpUnitMotion = Engine.QueryInterface(ent, IID_UnitMotion);
if (cmpUnitMotion)
ret.speed = {
- "walk": cmpUnitMotion.GetWalkSpeed(),
- "run": cmpUnitMotion.GetRunSpeed()
+ "walk": cmpUnitMotion.GetBaseSpeed(),
+ "run": cmpUnitMotion.GetBaseSpeed() * cmpUnitMotion.GetTopSpeedRatio(),
+ "current": cmpUnitMotion.GetSpeed()
};
return ret;
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
@@ -1,3 +1,5 @@
+const WALKING_SPEED = 1.0
+
function UnitAI() {}
UnitAI.prototype.Schema =
@@ -643,7 +645,7 @@
Engine.QueryInterface(this.entity, IID_ResourceGatherer).CommitResources(dropsiteTypes);
// Stop showing the carried resource animation.
- this.SetGathererAnimationOverride();
+ this.SetAnimationVariant();
// Our next order should always be a Gather,
// so just switch back to that order
@@ -1391,12 +1393,12 @@
"enter": function () {
var cmpFormation = Engine.QueryInterface(this.formationController, IID_Formation);
var cmpVisual = Engine.QueryInterface(this.entity, IID_Visual);
- if (cmpFormation && cmpVisual)
+/* TOREPLACE if (cmpFormation && cmpVisual)
{
cmpVisual.ReplaceMoveAnimation("walk", cmpFormation.GetFormationAnimation(this.entity, "walk"));
cmpVisual.ReplaceMoveAnimation("run", cmpFormation.GetFormationAnimation(this.entity, "run"));
}
- this.SelectAnimation("move");
+ */
},
// Occurs when the unit has reached its destination and the controller
@@ -1405,13 +1407,13 @@
// We can only finish this order if the move was really completed.
if (!msg.data.error && this.FinishOrder())
return;
- var cmpVisual = Engine.QueryInterface(this.entity, IID_Visual);
+/* TOREPLACE var cmpVisual = Engine.QueryInterface(this.entity, IID_Visual);
if (cmpVisual)
{
cmpVisual.ResetMoveAnimation("walk");
cmpVisual.ResetMoveAnimation("run");
}
-
+*/
var cmpFormation = Engine.QueryInterface(this.formationController, IID_Formation);
if (cmpFormation)
cmpFormation.SetInPosition(this.entity);
@@ -1424,7 +1426,6 @@
var cmpFormation = Engine.QueryInterface(this.formationController, IID_Formation);
if (cmpFormation)
cmpFormation.UnsetInPosition(this.entity);
- this.SelectAnimation("move");
},
"MoveCompleted": function() {
@@ -1585,7 +1586,6 @@
},
"MoveStarted": function() {
- this.SelectAnimation("move");
},
"MoveCompleted": function() {
@@ -1603,7 +1603,6 @@
"WALKING": {
"enter": function () {
- this.SelectAnimation("move");
},
"MoveCompleted": function() {
@@ -1614,10 +1613,9 @@
"WALKINGANDFIGHTING": {
"enter": function () {
// Show weapons rather than carried resources.
- this.SetGathererAnimationOverride(true);
+ this.SetAnimationVariant("combat");
this.StartTimer(0, 1000);
- this.SelectAnimation("move");
},
"Timer": function(msg) {
@@ -1649,7 +1647,6 @@
}
this.StartTimer(0, 1000);
- this.SelectAnimation("move");
},
"leave": function() {
@@ -1679,10 +1676,9 @@
"ESCORTING": {
"enter": function () {
// Show weapons rather than carried resources.
- this.SetGathererAnimationOverride(true);
+ this.SetAnimationVariant("combat");
this.StartTimer(0, 1000);
- this.SelectAnimation("move");
this.SetHeldPositionOnEntity(this.isGuardOf);
return false;
},
@@ -1699,27 +1695,29 @@
},
"leave": function(msg) {
- this.SetMoveSpeed(this.GetWalkSpeed());
+ this.SetMoveSpeed(WALKING_SPEED);
this.StopTimer();
},
"MoveStarted": function(msg) {
// Adapt the speed to the one of the target if needed
- var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
+ let cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
if (cmpUnitMotion.IsInTargetRange(this.isGuardOf, 0, 3*this.guardRange))
{
- var cmpUnitAI = Engine.QueryInterface(this.isGuardOf, IID_UnitAI);
- if (cmpUnitAI)
+ var cmpOtherMotion = Engine.QueryInterface(this.isGuardOf, IID_UnitMotion);
+ if (cmpOtherMotion)
{
- var speed = cmpUnitAI.GetWalkSpeed();
- if (speed < this.GetWalkSpeed())
+ let otherSpeed = cmpOtherMotion.GetSpeed();
+ let mySpeed = cmpUnitMotion.GetSpeed();
+ let speed = otherSpeed / mySpeed;
+ if (speed < WALKING_SPEED)
this.SetMoveSpeed(speed);
}
}
},
"MoveCompleted": function() {
- this.SetMoveSpeed(this.GetWalkSpeed());
+ this.SetMoveSpeed(WALKING_SPEED);
if (!this.MoveToTargetRangeExplicit(this.isGuardOf, 0, this.guardRange))
this.SetNextState("GUARDING");
},
@@ -1774,19 +1772,12 @@
this.PlaySound("panic");
// Run quickly
- var speed = this.GetRunSpeed();
- this.SelectAnimation("move");
- this.SetMoveSpeed(speed);
- },
-
- "HealthChanged": function() {
- var speed = this.GetRunSpeed();
- this.SetMoveSpeed(speed);
+ this.SetMoveSpeed(this.GetRunSpeed());
},
"leave": function() {
// Reset normal speed
- this.SetMoveSpeed(this.GetWalkSpeed());
+ this.SetMoveSpeed(WALKING_SPEED);
},
"MoveCompleted": function() {
@@ -1811,15 +1802,14 @@
"APPROACHING": {
"enter": function () {
// Show weapons rather than carried resources.
- this.SetGathererAnimationOverride(true);
+ this.SetAnimationVariant("combat");
- this.SelectAnimation("move");
this.StartTimer(1000, 1000);
},
"leave": function() {
// Show carried resources when walking.
- this.SetGathererAnimationOverride();
+ this.SetAnimationVariant();
this.StopTimer();
},
@@ -2096,16 +2086,12 @@
"CHASING": {
"enter": function () {
// Show weapons rather than carried resources.
- this.SetGathererAnimationOverride(true);
+ this.SetAnimationVariant("combat");
- this.SelectAnimation("move");
var cmpUnitAI = Engine.QueryInterface(this.order.data.target, IID_UnitAI);
+ // Run after a fleeing target
if (cmpUnitAI && cmpUnitAI.IsFleeing())
- {
- // Run after a fleeing target
- var speed = this.GetRunSpeed();
- this.SetMoveSpeed(speed);
- }
+ this.SetMoveSpeed(this.GetRunSpeed());
this.StartTimer(1000, 1000);
},
@@ -2113,15 +2099,16 @@
var cmpUnitAI = Engine.QueryInterface(this.order.data.target, IID_UnitAI);
if (!cmpUnitAI || !cmpUnitAI.IsFleeing())
return;
- var speed = this.GetRunSpeed();
- this.SetMoveSpeed(speed);
+ // TODO: figure out what to do with fleeing
+ //var speed = this.GetRunSpeed();
+ //this.SetMoveSpeed(speed);
},
"leave": function() {
// Reset normal speed in case it was changed
- this.SetMoveSpeed(this.GetWalkSpeed());
+ this.SetMoveSpeed(WALKING_SPEED);
// Show carried resources when walking.
- this.SetGathererAnimationOverride();
+ this.SetAnimationVariant();
this.StopTimer();
},
@@ -2147,7 +2134,6 @@
"GATHER": {
"APPROACHING": {
"enter": function() {
- this.SelectAnimation("move");
this.gatheringTarget = this.order.data.target; // temporary, deleted in "leave".
@@ -2271,7 +2257,6 @@
// Walking to a good place to gather resources near, used by GatherNearPosition
"WALKING": {
"enter": function() {
- this.SelectAnimation("move");
},
"MoveCompleted": function(msg) {
@@ -2385,7 +2370,7 @@
delete this.gatheringTarget;
// Show the carried resource, if we've gathered anything.
- this.SetGathererAnimationOverride();
+ this.SetAnimationVariant();
},
"Timer": function(msg) {
@@ -2533,7 +2518,6 @@
"APPROACHING": {
"enter": function () {
- this.SelectAnimation("move");
this.StartTimer(1000, 1000);
},
@@ -2634,7 +2618,6 @@
},
"CHASING": {
"enter": function () {
- this.SelectAnimation("move");
this.StartTimer(1000, 1000);
},
@@ -2662,7 +2645,6 @@
"RETURNRESOURCE": {
"APPROACHING": {
"enter": function () {
- this.SelectAnimation("move");
},
"MoveCompleted": function() {
@@ -2684,7 +2666,7 @@
cmpResourceGatherer.CommitResources(dropsiteTypes);
// Stop showing the carried resource animation.
- this.SetGathererAnimationOverride();
+ this.SetAnimationVariant();
// Our next order should always be a Gather,
// so just switch back to that order
@@ -2720,7 +2702,6 @@
"APPROACHINGMARKET": {
"enter": function () {
- this.SelectAnimation("move");
},
"MoveCompleted": function() {
@@ -2748,7 +2729,6 @@
"REPAIR": {
"APPROACHING": {
"enter": function () {
- this.SelectAnimation("move");
},
"MoveCompleted": function() {
@@ -2852,7 +2832,7 @@
{
let dropsiteTypes = cmpResourceDropsite.GetTypes();
cmpResourceGatherer.CommitResources(dropsiteTypes);
- this.SetGathererAnimationOverride();
+ this.SetAnimationVariant();
}
// We finished building it.
@@ -2861,7 +2841,7 @@
{
if (this.CanReturnResource(msg.data.newentity, true))
{
- this.SetGathererAnimationOverride();
+ this.SetAnimationVariant();
this.PushOrderFront("ReturnResource", { "target": msg.data.newentity, "force": false });
}
return;
@@ -2880,7 +2860,7 @@
{
if (this.CanReturnResource(msg.data.newentity, true))
{
- this.SetGathererAnimationOverride();
+ this.SetAnimationVariant();
this.PushOrder("ReturnResource", { "target": msg.data.newentity, "force": false });
}
this.PerformGather(msg.data.newentity, true, false);
@@ -2945,7 +2925,6 @@
"APPROACHING": {
"enter": function() {
- this.SelectAnimation("move");
},
"MoveCompleted": function() {
@@ -3026,7 +3005,7 @@
if (cmpResourceGatherer)
{
cmpResourceGatherer.CommitResources(dropsiteTypes);
- this.SetGathererAnimationOverride();
+ this.SetAnimationVariant();
}
}
@@ -3148,7 +3127,6 @@
"PICKUP": {
"APPROACHING": {
"enter": function() {
- this.SelectAnimation("move");
},
"MoveCompleted": function() {
@@ -3227,7 +3205,6 @@
"ROAMING": {
"enter": function() {
// Walk in a random direction
- this.SelectAnimation("walk", false, this.GetWalkSpeed());
this.MoveRandomly(+this.template.RoamDistance);
// Set a random timer to switch to feeding state
this.StartTimer(RandomInt(+this.template.RoamTimeMin, +this.template.RoamTimeMax));
@@ -4010,12 +3987,14 @@
//// Message handlers /////
-UnitAI.prototype.OnMotionChanged = function(msg)
+UnitAI.prototype.OnBeginMove = function(msg)
+{
+ this.UnitFsm.ProcessMessage(this, { "type": "MoveStarted", "data": msg });
+};
+
+UnitAI.prototype.OnFinishedMove = function(msg)
{
- if (msg.starting && !msg.error)
- this.UnitFsm.ProcessMessage(this, {"type": "MoveStarted", "data": msg});
- else if (!msg.starting || msg.error)
- this.UnitFsm.ProcessMessage(this, {"type": "MoveCompleted", "data": msg});
+ this.UnitFsm.ProcessMessage(this, { "type": "MoveCompleted", "data": msg });
};
UnitAI.prototype.OnGlobalConstructionFinished = function(msg)
@@ -4076,22 +4055,10 @@
//// Helper functions to be called by the FSM ////
-UnitAI.prototype.GetWalkSpeed = function()
-{
- var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
- return cmpUnitMotion.GetWalkSpeed();
-};
-
UnitAI.prototype.GetRunSpeed = function()
{
var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
- var runSpeed = cmpUnitMotion.GetRunSpeed();
- var walkSpeed = cmpUnitMotion.GetWalkSpeed();
- if (runSpeed <= walkSpeed)
- return runSpeed;
- var cmpHealth = Engine.QueryInterface(this.entity, IID_Health);
- var health = cmpHealth.GetHitpoints()/cmpHealth.GetMaxHitpoints();
- return (health*runSpeed + (1-health)*walkSpeed);
+ return cmpUnitMotion.GetTopSpeedRatio();
};
/**
@@ -4277,37 +4244,42 @@
}
};
-UnitAI.prototype.SetGathererAnimationOverride = function(disable)
+// Select a visual actor variant for the purpose of animation
+// This allows changing the walk animation for normal stance, combat stances, carrying stances…
+// without using a hack of replacing the animation in code like we used to.
+UnitAI.prototype.SetAnimationVariant = function(type)
{
- var cmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer);
- if (!cmpResourceGatherer)
- return;
-
- var cmpVisual = Engine.QueryInterface(this.entity, IID_Visual);
+ let cmpVisual = Engine.QueryInterface(this.entity, IID_Visual);
if (!cmpVisual)
return;
- // Remove the animation override, so that weapons are shown again.
- if (disable)
+ if (!type || type == "normal")
{
- cmpVisual.ResetMoveAnimation("walk");
- return;
- }
+ // switch between default and carrying resources depending.
- // Work out what we're carrying, in order to select an appropriate animation
- var type = cmpResourceGatherer.GetLastCarriedType();
- if (type)
- {
- var typename = "carry_" + type.generic;
+ let cmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer);
+ if (!cmpResourceGatherer)
+ {
+ cmpVisual.SetVariant("animationVariant", "");
+ return;
+ }
+
+ let type = cmpResourceGatherer.GetLastCarriedType();
+ if (type)
+ {
+ let typename = "carry_" + type.generic;
- // Special case for meat
- if (type.specific == "meat")
- typename = "carry_" + type.specific;
+ // Special case for meat
+ if (type.specific == "meat")
+ typename = "carry_" + type.specific;
- cmpVisual.ReplaceMoveAnimation("walk", typename);
+ cmpVisual.SetVariant("animationVariant", typename);
+ }
+ else
+ cmpVisual.SetVariant("animationVariant", "");
}
- else
- cmpVisual.ResetMoveAnimation("walk");
+ else if (type === "combat")
+ cmpVisual.SetVariant("animationVariant", "combat");
};
UnitAI.prototype.SelectAnimation = function(name, once, speed, sound)
@@ -4316,17 +4288,6 @@
if (!cmpVisual)
return;
- // Special case: the "move" animation gets turned into a special
- // movement mode that deals with speeds and walk/run automatically
- if (name == "move")
- {
- // Speed to switch from walking to running animations
- var runThreshold = (this.GetWalkSpeed() + this.GetRunSpeed()) / 2;
-
- cmpVisual.SelectMovementAnimation(runThreshold);
- return;
- }
-
var soundgroup;
if (sound)
{
@@ -4359,18 +4320,20 @@
UnitAI.prototype.StopMoving = function()
{
var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
- cmpUnitMotion.StopMoving();
+ cmpUnitMotion.DiscardMove();
};
UnitAI.prototype.MoveToPoint = function(x, z)
{
var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
+ cmpUnitMotion.SetAbortIfStuck(3);
return cmpUnitMotion.MoveToPointRange(x, z, 0, 0);
};
UnitAI.prototype.MoveToPointRange = function(x, z, rangeMin, rangeMax)
{
var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
+ cmpUnitMotion.SetAbortIfStuck(3);
return cmpUnitMotion.MoveToPointRange(x, z, rangeMin, rangeMax);
};
@@ -4380,6 +4343,7 @@
return false;
var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
+ cmpUnitMotion.SetAbortIfStuck(5);
return cmpUnitMotion.MoveToTargetRange(target, 0, 0);
};
@@ -4394,6 +4358,7 @@
var range = cmpRanged.GetRange(type);
var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
+ cmpUnitMotion.SetAbortIfStuck(5);
return cmpUnitMotion.MoveToTargetRange(target, range.min, range.max);
};
@@ -4450,6 +4415,7 @@
var guessedMaxRange = (range.max + parabolicMaxRange)/2;
var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
+ cmpUnitMotion.SetAbortIfStuck(9);
if (cmpUnitMotion.MoveToTargetRange(target, range.min, guessedMaxRange))
return true;
@@ -4463,6 +4429,7 @@
return false;
var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
+ cmpUnitMotion.SetAbortIfStuck(5);
return cmpUnitMotion.MoveToTargetRange(target, min, max);
};
@@ -4477,6 +4444,7 @@
var range = cmpGarrisonHolder.GetLoadingRange();
var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
+ cmpUnitMotion.SetAbortIfStuck(5);
return cmpUnitMotion.MoveToTargetRange(target, range.min, range.max);
};
@@ -5693,8 +5661,8 @@
UnitAI.prototype.SetMoveSpeed = function(speed)
{
- var cmpMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
- cmpMotion.SetSpeed(speed);
+ var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
+ cmpUnitMotion.SetSpeed(WALKING_SPEED);
};
UnitAI.prototype.SetHeldPosition = function(x, z)
Index: binaries/data/mods/public/simulation/components/UnitMotionFlying.js
===================================================================
--- binaries/data/mods/public/simulation/components/UnitMotionFlying.js
+++ binaries/data/mods/public/simulation/components/UnitMotionFlying.js
@@ -251,6 +251,11 @@
this.targetMinRange = minRange;
this.targetMaxRange = maxRange;
+ // we'll tell the visual actor to set our animation here.
+ let cmpVisual = Engine.QueryInterface(this.entity, IID_Visual);
+ if (cmpVisual)
+ cmpVisual.SetMovingSpeed(this.speed);
+
return true;
};
@@ -260,6 +265,11 @@
if (!cmpTargetPosition || !cmpTargetPosition.IsInWorld())
return false;
+ // we'll tell the visual actor to set our animation here.
+ let cmpVisual = Engine.QueryInterface(this.entity, IID_Visual);
+ if (cmpVisual)
+ cmpVisual.SetMovingSpeed(this.speed);
+
var targetPos = cmpTargetPosition.GetPosition2D();
this.hasTarget = true;
@@ -295,7 +305,7 @@
return this.IsInPointRange(targetPos.x, targetPos.y, minRange, maxRange);
};
-UnitMotionFlying.prototype.GetWalkSpeed = function()
+UnitMotionFlying.prototype.GetSpeed = function()
{
return +this.template.MaxSpeed;
};
@@ -325,7 +335,7 @@
// Ignore this - angle is controlled by the target-seeking code instead
};
-UnitMotionFlying.prototype.StopMoving = function()
+UnitMotionFlying.prototype.DiscardMove = function()
{
//Invert
if (!this.waterDeath)
Index: binaries/data/mods/public/simulation/components/tests/test_UnitAI.js
===================================================================
--- binaries/data/mods/public/simulation/components/tests/test_UnitAI.js
+++ /dev/null
@@ -1,322 +0,0 @@
-Engine.LoadHelperScript("FSM.js");
-Engine.LoadHelperScript("Entity.js");
-Engine.LoadHelperScript("Player.js");
-Engine.LoadComponentScript("interfaces/Attack.js");
-Engine.LoadComponentScript("interfaces/Auras.js");
-Engine.LoadComponentScript("interfaces/BuildingAI.js");
-Engine.LoadComponentScript("interfaces/Capturable.js");
-Engine.LoadComponentScript("interfaces/DamageReceiver.js");
-Engine.LoadComponentScript("interfaces/Formation.js");
-Engine.LoadComponentScript("interfaces/Heal.js");
-Engine.LoadComponentScript("interfaces/Health.js");
-Engine.LoadComponentScript("interfaces/Pack.js");
-Engine.LoadComponentScript("interfaces/ResourceSupply.js");
-Engine.LoadComponentScript("interfaces/Timer.js");
-Engine.LoadComponentScript("interfaces/UnitAI.js");
-Engine.LoadComponentScript("Formation.js");
-Engine.LoadComponentScript("UnitAI.js");
-
-/* Regression test.
- * Tests the FSM behaviour of a unit when walking as part of a formation,
- * then exiting the formation.
- * mode == 0: There is no enemy unit nearby.
- * mode == 1: There is a live enemy unit nearby.
- * mode == 2: There is a dead enemy unit nearby.
- */
-function TestFormationExiting(mode)
-{
- ResetState();
-
- var playerEntity = 5;
- var unit = 10;
- var enemy = 20;
- var controller = 30;
-
-
- AddMock(SYSTEM_ENTITY, IID_Timer, {
- SetInterval: function() { },
- SetTimeout: function() { },
- });
-
- AddMock(SYSTEM_ENTITY, IID_RangeManager, {
- CreateActiveQuery: function(ent, minRange, maxRange, players, iid, flags) {
- return 1;
- },
- EnableActiveQuery: function(id) { },
- ResetActiveQuery: function(id) { if (mode == 0) return []; else return [enemy]; },
- DisableActiveQuery: function(id) { },
- GetEntityFlagMask: function(identifier) { },
- });
-
- AddMock(SYSTEM_ENTITY, IID_TemplateManager, {
- GetCurrentTemplateName: function(ent) { return "formations/line_closed"; },
- });
-
- AddMock(SYSTEM_ENTITY, IID_PlayerManager, {
- GetPlayerByID: function(id) { return playerEntity; },
- GetNumPlayers: function() { return 2; },
- });
-
- AddMock(playerEntity, IID_Player, {
- IsAlly: function() { return false; },
- IsEnemy: function() { return true; },
- GetEnemies: function() { return []; },
- });
-
-
- var unitAI = ConstructComponent(unit, "UnitAI", { "FormationController": "false", "DefaultStance": "aggressive" });
-
- AddMock(unit, IID_Identity, {
- GetClassesList: function() { return []; },
- });
-
- AddMock(unit, IID_Ownership, {
- GetOwner: function() { return 1; },
- });
-
- AddMock(unit, IID_Position, {
- GetTurretParent: function() { return INVALID_ENTITY; },
- GetPosition: function() { return new Vector3D(); },
- GetPosition2D: function() { return new Vector2D(); },
- GetRotation: function() { return { "y": 0 }; },
- IsInWorld: function() { return true; },
- });
-
- AddMock(unit, IID_UnitMotion, {
- GetWalkSpeed: function() { return 1; },
- MoveToFormationOffset: function(target, x, z) { },
- IsInTargetRange: function(target, min, max) { return true; },
- MoveToTargetRange: function(target, min, max) { },
- StopMoving: function() { },
- GetPassabilityClassName: function() { return "default"; },
- });
-
- AddMock(unit, IID_Vision, {
- GetRange: function() { return 10; },
- });
-
- AddMock(unit, IID_Attack, {
- GetRange: function() { return { "max": 10, "min": 0}; },
- GetFullAttackRange: function() { return { "max": 40, "min": 0}; },
- GetBestAttackAgainst: function(t) { return "melee"; },
- GetPreference: function(t) { return 0; },
- GetTimers: function() { return { "prepare": 500, "repeat": 1000 }; },
- CanAttack: function(v) { return true; },
- CompareEntitiesByPreference: function(a, b) { return 0; },
- });
-
- unitAI.OnCreate();
-
- unitAI.SetupRangeQuery(1);
-
-
- if (mode == 1)
- {
- AddMock(enemy, IID_Health, {
- GetHitpoints: function() { return 10; },
- });
- AddMock(enemy, IID_UnitAI, {
- IsAnimal: function() { return false; }
- });
- }
- else if (mode == 2)
- AddMock(enemy, IID_Health, {
- GetHitpoints: function() { return 0; },
- });
-
- var controllerFormation = ConstructComponent(controller, "Formation", {"FormationName": "Line Closed", "FormationShape": "square", "ShiftRows": "false", "SortingClasses": "", "WidthDepthRatio": 1, "UnitSeparationWidthMultiplier": 1, "UnitSeparationDepthMultiplier": 1, "SpeedMultiplier": 1, "Sloppyness": 0});
- var controllerAI = ConstructComponent(controller, "UnitAI", { "FormationController": "true", "DefaultStance": "aggressive" });
-
- AddMock(controller, IID_Position, {
- JumpTo: function(x, z) { this.x = x; this.z = z; },
- GetTurretParent: function() { return INVALID_ENTITY; },
- GetPosition: function() { return new Vector3D(this.x, 0, this.z); },
- GetPosition2D: function() { return new Vector2D(this.x, this.z); },
- GetRotation: function() { return { "y": 0 }; },
- IsInWorld: function() { return true; },
- });
-
- AddMock(controller, IID_UnitMotion, {
- SetSpeed: function(speed) { },
- MoveToPointRange: function(x, z, minRange, maxRange) { },
- GetPassabilityClassName: function() { return "default"; },
- });
-
- controllerAI.OnCreate();
-
-
- TS_ASSERT_EQUALS(controllerAI.fsmStateName, "FORMATIONCONTROLLER.IDLE");
- TS_ASSERT_EQUALS(unitAI.fsmStateName, "INDIVIDUAL.IDLE");
-
- controllerFormation.SetMembers([unit]);
- controllerAI.Walk(100, 100, false);
- controllerAI.OnMotionChanged({ "starting": true });
-
- TS_ASSERT_EQUALS(controllerAI.fsmStateName, "FORMATIONCONTROLLER.WALKING");
- TS_ASSERT_EQUALS(unitAI.fsmStateName, "FORMATIONMEMBER.WALKING");
-
- controllerFormation.Disband();
-
- if (mode == 0)
- TS_ASSERT_EQUALS(unitAI.fsmStateName, "INDIVIDUAL.IDLE");
- else if (mode == 1)
- TS_ASSERT_EQUALS(unitAI.fsmStateName, "INDIVIDUAL.COMBAT.ATTACKING");
- else if (mode == 2)
- TS_ASSERT_EQUALS(unitAI.fsmStateName, "INDIVIDUAL.IDLE");
- else
- TS_FAIL("invalid mode");
-}
-
-function TestMoveIntoFormationWhileAttacking()
-{
- ResetState();
-
- var playerEntity = 5;
- var controller = 10;
- var enemy = 20;
- var unit = 30;
- var units = [];
- var unitCount = 8;
- var unitAIs = [];
-
- AddMock(SYSTEM_ENTITY, IID_Timer, {
- SetInterval: function() { },
- SetTimeout: function() { },
- });
-
-
- AddMock(SYSTEM_ENTITY, IID_RangeManager, {
- CreateActiveQuery: function(ent, minRange, maxRange, players, iid, flags) {
- return 1;
- },
- EnableActiveQuery: function(id) { },
- ResetActiveQuery: function(id) { return [enemy]; },
- DisableActiveQuery: function(id) { },
- GetEntityFlagMask: function(identifier) { },
- });
-
- AddMock(SYSTEM_ENTITY, IID_TemplateManager, {
- GetCurrentTemplateName: function(ent) { return "formations/line_closed"; },
- });
-
- AddMock(SYSTEM_ENTITY, IID_PlayerManager, {
- GetPlayerByID: function(id) { return playerEntity; },
- GetNumPlayers: function() { return 2; },
- });
-
- AddMock(playerEntity, IID_Player, {
- IsAlly: function() { return false; },
- IsEnemy: function() { return true; },
- GetEnemies: function() { return []; },
- });
-
- // create units
- for (var i = 0; i < unitCount; i++) {
-
- units.push(unit + i);
-
- var unitAI = ConstructComponent(unit + i, "UnitAI", { "FormationController": "false", "DefaultStance": "aggressive" });
-
- AddMock(unit + i, IID_Identity, {
- GetClassesList: function() { return []; },
- });
-
- AddMock(unit + i, IID_Ownership, {
- GetOwner: function() { return 1; },
- });
-
- AddMock(unit + i, IID_Position, {
- GetTurretParent: function() { return INVALID_ENTITY; },
- GetPosition: function() { return new Vector3D(); },
- GetPosition2D: function() { return new Vector2D(); },
- GetRotation: function() { return { "y": 0 }; },
- IsInWorld: function() { return true; },
- });
-
- AddMock(unit + i, IID_UnitMotion, {
- GetWalkSpeed: function() { return 1; },
- MoveToFormationOffset: function(target, x, z) { },
- IsInTargetRange: function(target, min, max) { return true; },
- MoveToTargetRange: function(target, min, max) { },
- StopMoving: function() { },
- GetPassabilityClassName: function() { return "default"; },
- });
-
- AddMock(unit + i, IID_Vision, {
- GetRange: function() { return 10; },
- });
-
- AddMock(unit + i, IID_Attack, {
- GetRange: function() { return {"max":10, "min": 0}; },
- GetFullAttackRange: function() { return { "max": 40, "min": 0}; },
- GetBestAttackAgainst: function(t) { return "melee"; },
- GetTimers: function() { return { "prepare": 500, "repeat": 1000 }; },
- CanAttack: function(v) { return true; },
- CompareEntitiesByPreference: function(a, b) { return 0; },
- });
-
- unitAI.OnCreate();
-
- unitAI.SetupRangeQuery(1);
-
- unitAIs.push(unitAI);
- }
-
- // create enemy
- AddMock(enemy, IID_Health, {
- GetHitpoints: function() { return 40; },
- });
-
- var controllerFormation = ConstructComponent(controller, "Formation", {"FormationName": "Line Closed", "FormationShape": "square", "ShiftRows": "false", "SortingClasses": "", "WidthDepthRatio": 1, "UnitSeparationWidthMultiplier": 1, "UnitSeparationDepthMultiplier": 1, "SpeedMultiplier": 1, "Sloppyness": 0});
- var controllerAI = ConstructComponent(controller, "UnitAI", { "FormationController": "true", "DefaultStance": "aggressive" });
-
- AddMock(controller, IID_Position, {
- GetTurretParent: function() { return INVALID_ENTITY; },
- JumpTo: function(x, z) { this.x = x; this.z = z; },
- GetPosition: function() { return new Vector3D(this.x, 0, this.z); },
- GetPosition2D: function() { return new Vector2D(this.x, this.z); },
- GetRotation: function() { return { "y": 0 }; },
- IsInWorld: function() { return true; },
- });
-
- AddMock(controller, IID_UnitMotion, {
- SetSpeed: function(speed) { },
- MoveToPointRange: function(x, z, minRange, maxRange) { },
- IsInTargetRange: function(target, min, max) { return true; },
- GetPassabilityClassName: function() { return "default"; },
- });
-
- AddMock(controller, IID_Attack, {
- GetRange: function() { return {"max":10, "min": 0}; },
- CanAttackAsFormation: function() { return false; },
- });
-
- controllerAI.OnCreate();
-
- controllerFormation.SetMembers(units);
-
- controllerAI.Attack(enemy, []);
-
- for each (var ent in unitAIs) {
- TS_ASSERT_EQUALS(unitAI.fsmStateName, "INDIVIDUAL.COMBAT.ATTACKING");
- }
-
- controllerAI.MoveIntoFormation({"name": "Circle"});
-
- // let all units be in position
- for each (var ent in unitAIs) {
- controllerFormation.SetInPosition(ent);
- }
-
- for each (var ent in unitAIs) {
- TS_ASSERT_EQUALS(unitAI.fsmStateName, "INDIVIDUAL.COMBAT.ATTACKING");
- }
-
- controllerFormation.Disband();
-}
-
-TestFormationExiting(0);
-TestFormationExiting(1);
-TestFormationExiting(2);
-
-TestMoveIntoFormationWhileAttacking();
Index: binaries/data/mods/public/simulation/data/auras/athen_hero_iphicrates_1.json
===================================================================
--- binaries/data/mods/public/simulation/data/auras/athen_hero_iphicrates_1.json
+++ binaries/data/mods/public/simulation/data/auras/athen_hero_iphicrates_1.json
@@ -5,8 +5,7 @@
{ "value": "Armour/Pierce", "add": 1 },
{ "value": "Armour/Hack", "add": 1 },
{ "value": "Armour/Crush", "add": 1 },
- { "value": "UnitMotion/WalkSpeed", "multiply": 1.15 },
- { "value": "UnitMotion/Run/Speed", "multiply": 1.15 }
+ { "value": "UnitMotion/WalkSpeed", "multiply": 1.15 }
],
"auraName": "Formation Reforms",
"auraDescription": "All soldiers in his formation +15% speed and +1 armor."
Index: binaries/data/mods/public/simulation/data/auras/athen_hero_iphicrates_2.json
===================================================================
--- binaries/data/mods/public/simulation/data/auras/athen_hero_iphicrates_2.json
+++ binaries/data/mods/public/simulation/data/auras/athen_hero_iphicrates_2.json
@@ -2,8 +2,7 @@
"type": "global",
"affects": ["Javelin Infantry"],
"modifications": [
- { "value": "UnitMotion/WalkSpeed", "multiply": 1.15 },
- { "value": "UnitMotion/Run/Speed", "multiply": 1.15 }
+ { "value": "UnitMotion/WalkSpeed", "multiply": 1.15 }
],
"auraName": "Peltast Reforms",
"auraDescription": "All javelin infantry +15% speed."
Index: binaries/data/mods/public/simulation/data/auras/athen_hero_themistocles_1.json
===================================================================
--- binaries/data/mods/public/simulation/data/auras/athen_hero_themistocles_1.json
+++ binaries/data/mods/public/simulation/data/auras/athen_hero_themistocles_1.json
@@ -3,8 +3,7 @@
"affects": ["Ship"],
"affectedPlayers": ["MutualAlly"],
"modifications": [
- { "value": "UnitMotion/WalkSpeed", "multiply": 1.5 },
- { "value": "UnitMotion/Run/Speed", "multiply": 1.5 }
+ { "value": "UnitMotion/WalkSpeed", "multiply": 1.5 }
],
"auraName": "Naval Commander Aura",
"auraDescription": "When garrisoned in a ship, his ship is +50% faster."
Index: binaries/data/mods/public/simulation/data/auras/brit_hero_boudicca.json
===================================================================
--- binaries/data/mods/public/simulation/data/auras/brit_hero_boudicca.json
+++ binaries/data/mods/public/simulation/data/auras/brit_hero_boudicca.json
@@ -4,7 +4,6 @@
"affects": ["Champion"],
"modifications": [
{ "value": "UnitMotion/WalkSpeed", "multiply": 1.25 },
- { "value": "UnitMotion/Run/Speed", "multiply": 1.25 },
{ "value": "Attack/Melee/Hack", "multiply": 1.2 },
{ "value": "Attack/Melee/Pierce", "multiply": 1.2 },
{ "value": "Attack/Melee/Crush", "multiply": 1.2 },
Index: binaries/data/mods/public/simulation/data/auras/brit_hero_caratacos.json
===================================================================
--- binaries/data/mods/public/simulation/data/auras/brit_hero_caratacos.json
+++ binaries/data/mods/public/simulation/data/auras/brit_hero_caratacos.json
@@ -2,8 +2,7 @@
"type": "global",
"affects": ["Soldier", "Siege"],
"modifications": [
- { "value": "UnitMotion/WalkSpeed", "multiply": 1.15 },
- { "value": "UnitMotion/Run/Speed", "multiply": 1.15 }
+ { "value": "UnitMotion/WalkSpeed", "multiply": 1.15 }
],
"auraName": "Hero Aura",
"auraDescription": "All soldiers and siege engines +15% speed."
Index: binaries/data/mods/public/simulation/data/auras/cart_hero_hamilcar.json
===================================================================
--- binaries/data/mods/public/simulation/data/auras/cart_hero_hamilcar.json
+++ binaries/data/mods/public/simulation/data/auras/cart_hero_hamilcar.json
@@ -2,8 +2,7 @@
"type": "global",
"affects": ["Soldier", "Siege"],
"modifications": [
- { "value": "UnitMotion/WalkSpeed", "multiply": 1.15 },
- { "value": "UnitMotion/Run/Speed", "multiply": 1.15 }
+ { "value": "UnitMotion/WalkSpeed", "multiply": 1.15 }
],
"auraName": "Lightning Aura",
"auraDescription": "All soldiers and siege engines +15% speed."
Index: binaries/data/mods/public/simulation/data/auras/maur_pillar.json
===================================================================
--- binaries/data/mods/public/simulation/data/auras/maur_pillar.json
+++ binaries/data/mods/public/simulation/data/auras/maur_pillar.json
@@ -3,8 +3,7 @@
"radius": 75,
"affects": ["Trader"],
"modifications": [
- { "value": "UnitMotion/WalkSpeed", "multiply": 1.20 },
- { "value": "UnitMotion/Run/Speed", "multiply": 1.20 }
+ { "value": "UnitMotion/WalkSpeed", "multiply": 1.20 }
],
"auraDescription": "All traders in range +20% speed.",
"overlayIcon": "art/textures/ui/session/auras/build_bonus.png"
Index: binaries/data/mods/public/simulation/data/auras/pers_hero_darius.json
===================================================================
--- binaries/data/mods/public/simulation/data/auras/pers_hero_darius.json
+++ binaries/data/mods/public/simulation/data/auras/pers_hero_darius.json
@@ -2,8 +2,7 @@
"type": "global",
"affects": ["Soldier", "Siege"],
"modifications": [
- { "value": "UnitMotion/WalkSpeed", "multiply": 1.15 },
- { "value": "UnitMotion/Run/Speed", "multiply": 1.15 }
+ { "value": "UnitMotion/WalkSpeed", "multiply": 1.15 }
],
"auraName": "Leadership Aura",
"auraDescription": "+15% movement speed for all soldiers and siege engines."
Index: binaries/data/mods/public/simulation/data/auras/sele_hero_seleucus_victor.json
===================================================================
--- binaries/data/mods/public/simulation/data/auras/sele_hero_seleucus_victor.json
+++ binaries/data/mods/public/simulation/data/auras/sele_hero_seleucus_victor.json
@@ -4,7 +4,6 @@
"affects": ["Elephant Champion"],
"modifications": [
{ "value": "UnitMotion/WalkSpeed", "multiply": 1.2 },
- { "value": "UnitMotion/Run/Speed", "multiply": 1.2 },
{ "value": "Attack/Melee/Hack", "multiply": 1.2 },
{ "value": "Attack/Melee/Crush", "multiply": 1.2 }
],
Index: binaries/data/mods/public/simulation/templates/gaia/fauna_camel.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/gaia/fauna_camel.xml
+++ binaries/data/mods/public/simulation/templates/gaia/fauna_camel.xml
@@ -32,11 +32,6 @@
3.0
-
- 9.0
- 600.0
- 5.0
-
fauna/camel.xml
Index: binaries/data/mods/public/simulation/templates/gaia/fauna_chicken.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/gaia/fauna_chicken.xml
+++ binaries/data/mods/public/simulation/templates/gaia/fauna_chicken.xml
@@ -40,9 +40,6 @@
1.0
-
- 6.0
-
fauna/chicken.xml
Index: binaries/data/mods/public/simulation/templates/gaia/fauna_crocodile.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/gaia/fauna_crocodile.xml
+++ binaries/data/mods/public/simulation/templates/gaia/fauna_crocodile.xml
@@ -39,9 +39,6 @@
2.0
-
- 18.0
-
20
Index: binaries/data/mods/public/simulation/templates/gaia/fauna_deer.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/gaia/fauna_deer.xml
+++ binaries/data/mods/public/simulation/templates/gaia/fauna_deer.xml
@@ -14,11 +14,6 @@
2.0
-
- 10.0
- 600.0
- 5.0
-
fauna/deer.xml
Index: binaries/data/mods/public/simulation/templates/gaia/fauna_elephant_north_african.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/gaia/fauna_elephant_north_african.xml
+++ binaries/data/mods/public/simulation/templates/gaia/fauna_elephant_north_african.xml
@@ -60,9 +60,6 @@
3.0
-
- 10.0
-
fauna/elephant_african_forest.xml
Index: binaries/data/mods/public/simulation/templates/gaia/fauna_gazelle.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/gaia/fauna_gazelle.xml
+++ binaries/data/mods/public/simulation/templates/gaia/fauna_gazelle.xml
@@ -12,12 +12,6 @@
pitch
-
-
- 600.0
- 5.0
-
-
fauna/gazelle.xml
Index: binaries/data/mods/public/simulation/templates/gaia/fauna_giraffe.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/gaia/fauna_giraffe.xml
+++ binaries/data/mods/public/simulation/templates/gaia/fauna_giraffe.xml
@@ -32,11 +32,6 @@
4.0
-
- 12.0
- 600.0
- 5.0
-
fauna/giraffe_adult.xml
Index: binaries/data/mods/public/simulation/templates/gaia/fauna_giraffe_infant.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/gaia/fauna_giraffe_infant.xml
+++ binaries/data/mods/public/simulation/templates/gaia/fauna_giraffe_infant.xml
@@ -21,11 +21,6 @@
4.0
-
- 10.0
- 600.0
- 5.0
-
fauna/giraffe_baby.xml
Index: binaries/data/mods/public/simulation/templates/gaia/fauna_hawk.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/gaia/fauna_hawk.xml
+++ binaries/data/mods/public/simulation/templates/gaia/fauna_hawk.xml
@@ -7,7 +7,7 @@
1.0
- 1000.0
+ 1000.0
Index: binaries/data/mods/public/simulation/templates/gaia/fauna_horse.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/gaia/fauna_horse.xml
+++ binaries/data/mods/public/simulation/templates/gaia/fauna_horse.xml
@@ -27,11 +27,6 @@
5.0
-
- 12.0
- 600.0
- 5.0
-
fauna/horse_a.xml
Index: binaries/data/mods/public/simulation/templates/gaia/fauna_lion.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/gaia/fauna_lion.xml
+++ binaries/data/mods/public/simulation/templates/gaia/fauna_lion.xml
@@ -30,9 +30,6 @@
3.0
-
- 15.0
-
fauna/lion.xml
Index: binaries/data/mods/public/simulation/templates/gaia/fauna_lioness.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/gaia/fauna_lioness.xml
+++ binaries/data/mods/public/simulation/templates/gaia/fauna_lioness.xml
@@ -30,9 +30,6 @@
3.0
-
- 15.0
-
fauna/lioness.xml
Index: binaries/data/mods/public/simulation/templates/gaia/fauna_peacock.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/gaia/fauna_peacock.xml
+++ binaries/data/mods/public/simulation/templates/gaia/fauna_peacock.xml
@@ -14,11 +14,6 @@
2.0
-
- 5.0
- 600.0
- 5.0
-
fauna/peacock.xml
Index: binaries/data/mods/public/simulation/templates/gaia/fauna_shark.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/gaia/fauna_shark.xml
+++ binaries/data/mods/public/simulation/templates/gaia/fauna_shark.xml
@@ -44,9 +44,6 @@
ship-small
4.0
-
- 35.0
-
false
Index: binaries/data/mods/public/simulation/templates/gaia/fauna_wildebeest.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/gaia/fauna_wildebeest.xml
+++ binaries/data/mods/public/simulation/templates/gaia/fauna_wildebeest.xml
@@ -19,11 +19,6 @@
6.0
-
- 15.0
- 600.0
- 5.0
-
fauna/wildebeest.xml
Index: binaries/data/mods/public/simulation/templates/gaia/fauna_zebra.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/gaia/fauna_zebra.xml
+++ binaries/data/mods/public/simulation/templates/gaia/fauna_zebra.xml
@@ -18,11 +18,6 @@
6.0
-
- 15.0
- 600.0
- 5.0
-
fauna/zebra.xml
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
@@ -106,13 +106,6 @@
false
9
-
- 15.0
- 50.0
- 0.0
- 0.1
- 0.2
-
default
Index: binaries/data/mods/public/simulation/templates/template_unit_cavalry.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_cavalry.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_cavalry.xml
@@ -94,11 +94,7 @@
16.5
-
- 26.0
- 600.0
- 5.0
-
+ 2.5
92
Index: binaries/data/mods/public/simulation/templates/template_unit_cavalry_melee_spearman.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_cavalry_melee_spearman.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_cavalry_melee_spearman.xml
@@ -19,10 +19,5 @@
22.0
-
- 40.0
- 600.0
- 5.0
-
Index: binaries/data/mods/public/simulation/templates/template_unit_cavalry_melee_swordsman.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_cavalry_melee_swordsman.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_cavalry_melee_swordsman.xml
@@ -29,10 +29,5 @@
20.0
-
- 28.75
- 600.0
- 5.0
-
Index: binaries/data/mods/public/simulation/templates/template_unit_cavalry_ranged_archer.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_cavalry_ranged_archer.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_cavalry_ranged_archer.xml
@@ -25,8 +25,5 @@
17.5
-
- 28.0
-
Index: binaries/data/mods/public/simulation/templates/template_unit_cavalry_ranged_javelinist.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_cavalry_ranged_javelinist.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_cavalry_ranged_javelinist.xml
@@ -25,8 +25,5 @@
17.5
-
- 28.0
-
Index: binaries/data/mods/public/simulation/templates/template_unit_champion_cavalry.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_champion_cavalry.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_champion_cavalry.xml
@@ -59,11 +59,6 @@
16.5
-
- 26.0
- 1000.0
- 10.0
-
96
Index: binaries/data/mods/public/simulation/templates/template_unit_champion_cavalry_archer.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_champion_cavalry_archer.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_champion_cavalry_archer.xml
@@ -34,10 +34,5 @@
20.5
-
- 28.0
- 1000.0
- 10.0
-
Index: binaries/data/mods/public/simulation/templates/template_unit_champion_cavalry_javelinist.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_champion_cavalry_javelinist.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_champion_cavalry_javelinist.xml
@@ -34,10 +34,5 @@
20.5
-
- 28.0
- 1000.0
- 10.0
-
Index: binaries/data/mods/public/simulation/templates/template_unit_champion_cavalry_spearman.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_champion_cavalry_spearman.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_champion_cavalry_spearman.xml
@@ -28,8 +28,5 @@
25.0
-
- 40.0
-
Index: binaries/data/mods/public/simulation/templates/template_unit_champion_cavalry_swordsman.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_champion_cavalry_swordsman.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_champion_cavalry_swordsman.xml
@@ -29,8 +29,5 @@
23.0
-
- 40.0
-
Index: binaries/data/mods/public/simulation/templates/template_unit_champion_elephant.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_champion_elephant.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_champion_elephant.xml
@@ -59,11 +59,6 @@
large
8.5
-
- 14.0
- 1000.0
- 10.0
-
100
Index: binaries/data/mods/public/simulation/templates/template_unit_champion_infantry.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_champion_infantry.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_champion_infantry.xml
@@ -46,11 +46,6 @@
8.5
-
- 17.5
- 600.0
- 5.0
-
84
Index: binaries/data/mods/public/simulation/templates/template_unit_champion_infantry_archer.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_champion_infantry_archer.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_champion_infantry_archer.xml
@@ -36,8 +36,5 @@
11.0
-
- 18.0
-
Index: binaries/data/mods/public/simulation/templates/template_unit_champion_infantry_javelinist.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_champion_infantry_javelinist.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_champion_infantry_javelinist.xml
@@ -36,8 +36,5 @@
16.0
-
- 18.0
-
Index: binaries/data/mods/public/simulation/templates/template_unit_champion_infantry_pikeman.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_champion_infantry_pikeman.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_champion_infantry_pikeman.xml
@@ -42,10 +42,5 @@
7.0
-
- 13.0
- 600.0
- 5.0
-
Index: binaries/data/mods/public/simulation/templates/template_unit_champion_infantry_spearman.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_champion_infantry_spearman.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_champion_infantry_spearman.xml
@@ -39,10 +39,5 @@
11.5
-
- 23.0
- 600.0
- 5.0
-
Index: binaries/data/mods/public/simulation/templates/template_unit_champion_infantry_swordsman.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_champion_infantry_swordsman.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_champion_infantry_swordsman.xml
@@ -33,10 +33,5 @@
12.5
-
- 16.0
- 600.0
- 5.0
-
Index: binaries/data/mods/public/simulation/templates/template_unit_dog.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_dog.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_dog.xml
@@ -86,11 +86,7 @@
14.5
-
- 26.0
- 600.0
- 5.0
-
+ 2.5
30
Index: binaries/data/mods/public/simulation/templates/template_unit_fauna.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_fauna.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_fauna.xml
@@ -32,9 +32,7 @@
6.5
-
- 15.0
-
+ 2.0
true
Index: binaries/data/mods/public/simulation/templates/template_unit_fauna_hunt_whale.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_fauna_hunt_whale.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_fauna_hunt_whale.xml
@@ -49,8 +49,6 @@
ship-small
11.5
-
- 15.0
-
+ 1.0
Index: binaries/data/mods/public/simulation/templates/template_unit_hero.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_hero.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_hero.xml
@@ -60,9 +60,6 @@
10.5
-
- 22.5
-
88
Index: binaries/data/mods/public/simulation/templates/template_unit_hero_cavalry.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_hero_cavalry.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_hero_cavalry.xml
@@ -50,11 +50,7 @@
16.5
-
- 26.0
- 1000.0
- 16.0
-
+ 2.5
100
Index: binaries/data/mods/public/simulation/templates/template_unit_hero_cavalry_javelinist.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_hero_cavalry_javelinist.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_hero_cavalry_javelinist.xml
@@ -39,10 +39,5 @@
17.0
-
- 28.0
- 1000.0
- 16.0
-
Index: binaries/data/mods/public/simulation/templates/template_unit_hero_elephant_melee.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_hero_elephant_melee.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_hero_elephant_melee.xml
@@ -50,11 +50,6 @@
8.5
-
- 14.0
- 1000.0
- 10.0
-
80
Index: binaries/data/mods/public/simulation/templates/template_unit_hero_infantry.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_hero_infantry.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_hero_infantry.xml
@@ -38,4 +38,7 @@
actor/human/death/{gender}_death.xml
+
+ 1.6
+
Index: binaries/data/mods/public/simulation/templates/template_unit_hero_infantry_pikeman.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_hero_infantry_pikeman.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_hero_infantry_pikeman.xml
@@ -32,8 +32,5 @@
8.5
-
- 17.5
-
Index: binaries/data/mods/public/simulation/templates/template_unit_hero_infantry_spearman.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_hero_infantry_spearman.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_hero_infantry_spearman.xml
@@ -31,8 +31,5 @@
9
-
- 18.75
-
Index: binaries/data/mods/public/simulation/templates/template_unit_hero_infantry_swordsman.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_hero_infantry_swordsman.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_hero_infantry_swordsman.xml
@@ -20,11 +20,6 @@
9.5
-
- 20.0
- 600.0
- 8.0
-
80
Index: binaries/data/mods/public/simulation/templates/template_unit_infantry.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_infantry.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_infantry.xml
@@ -119,9 +119,7 @@
9
-
- 18.75
-
+ 1.6
80
Index: binaries/data/mods/public/simulation/templates/template_unit_infantry_melee_pikeman.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_infantry_melee_pikeman.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_infantry_melee_pikeman.xml
@@ -37,8 +37,5 @@
6.0
-
- 8.0
-
Index: binaries/data/mods/public/simulation/templates/template_unit_infantry_melee_spearman.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_infantry_melee_spearman.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_infantry_melee_spearman.xml
@@ -32,8 +32,5 @@
8.5
-
- 15.0
-
Index: binaries/data/mods/public/simulation/templates/template_unit_infantry_melee_swordsman.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_infantry_melee_swordsman.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_infantry_melee_swordsman.xml
@@ -29,8 +29,5 @@
9.5
-
- 16.0
-
Index: binaries/data/mods/public/simulation/templates/template_unit_infantry_ranged.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_infantry_ranged.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_infantry_ranged.xml
@@ -34,4 +34,7 @@
attack/weapon/arrowfly.xml
+
+ 2.0
+
Index: binaries/data/mods/public/simulation/templates/template_unit_infantry_ranged_archer.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_infantry_ranged_archer.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_infantry_ranged_archer.xml
@@ -38,8 +38,5 @@
8.0
-
- 18.0
-
Index: binaries/data/mods/public/simulation/templates/template_unit_infantry_ranged_javelinist.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_infantry_ranged_javelinist.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_infantry_ranged_javelinist.xml
@@ -32,8 +32,5 @@
13.5
-
- 24.0
-
Index: binaries/data/mods/public/simulation/templates/template_unit_infantry_ranged_slinger.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_infantry_ranged_slinger.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_infantry_ranged_slinger.xml
@@ -32,8 +32,5 @@
11.0
-
- 24.0
-
Index: binaries/data/mods/public/simulation/templates/template_unit_mechanical.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_mechanical.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_mechanical.xml
@@ -22,4 +22,7 @@
4.0
+
+ 1.0
+
Index: binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_bireme.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_bireme.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_bireme.xml
@@ -69,8 +69,5 @@
14
-
- 18.0
-
Index: binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_fire.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_fire.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_fire.xml
@@ -45,9 +45,6 @@
ship-small
17.5
-
- 22.0
-
60
Index: binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_quinquereme.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_quinquereme.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_quinquereme.xml
@@ -77,9 +77,6 @@
16
-
- 20
-
110
Index: binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_trireme.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_trireme.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_trireme.xml
@@ -69,8 +69,5 @@
16
-
- 20.0
-
Index: binaries/data/mods/public/simulation/templates/template_unit_mechanical_siege_ballista.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_mechanical_siege_ballista.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_mechanical_siege_ballista.xml
@@ -53,9 +53,6 @@
8
-
- 12.0
-
120
Index: binaries/data/mods/public/simulation/templates/template_unit_mechanical_siege_onager.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_mechanical_siege_onager.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_mechanical_siege_onager.xml
@@ -69,9 +69,6 @@
7
-
- 10.0
-
120
Index: binaries/data/mods/public/simulation/templates/template_unit_mechanical_siege_ram.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_mechanical_siege_ram.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_mechanical_siege_ram.xml
@@ -48,9 +48,6 @@
8
-
- 11.0
-
80
Index: binaries/data/mods/public/simulation/templates/template_unit_mechanical_siege_tower.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_mechanical_siege_tower.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_mechanical_siege_tower.xml
@@ -71,9 +71,6 @@
6.5
-
- 10.0
-
80
Index: binaries/data/mods/public/simulation/templates/template_unit_support.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_support.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_support.xml
@@ -31,4 +31,7 @@
passive
+
+ 2.0
+
Index: binaries/data/mods/public/simulation/templates/template_unit_support_female_citizen.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_support_female_citizen.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_support_female_citizen.xml
@@ -87,11 +87,6 @@
9.5
-
- 16.0
- 200.0
- 0.0
-
32
Index: binaries/data/mods/public/simulation/templates/template_unit_support_healer.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_support_healer.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_support_healer.xml
@@ -41,11 +41,6 @@
9
-
- 12.0
- 200.0
- 0.0
-
30
Index: binaries/data/mods/public/simulation/templates/template_unit_support_slave.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_support_slave.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_support_slave.xml
@@ -85,10 +85,5 @@
8
-
- 15.0
- 1000.0
- 8.0
-
Index: binaries/data/mods/public/simulation/templates/units/athen_mechanical_siege_lithobolos_unpacked.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/units/athen_mechanical_siege_lithobolos_unpacked.xml
+++ binaries/data/mods/public/simulation/templates/units/athen_mechanical_siege_lithobolos_unpacked.xml
@@ -10,9 +10,6 @@
0.001
-
- 0.001
-
units/athenians/siege_rock.xml
Index: binaries/data/mods/public/simulation/templates/units/athen_mechanical_siege_oxybeles_unpacked.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/units/athen_mechanical_siege_oxybeles_unpacked.xml
+++ binaries/data/mods/public/simulation/templates/units/athen_mechanical_siege_oxybeles_unpacked.xml
@@ -10,9 +10,6 @@
0.001
-
- 0.001
-
units/athenians/siege_spear.xml
Index: binaries/data/mods/public/simulation/templates/units/cart_cavalry_swordsman_gaul_b.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/units/cart_cavalry_swordsman_gaul_b.xml
+++ binaries/data/mods/public/simulation/templates/units/cart_cavalry_swordsman_gaul_b.xml
@@ -21,9 +21,6 @@
15.5
-
- 24.0
-
units/celts/cavalry_swordsman_b.xml
Index: binaries/data/mods/public/simulation/templates/units/cart_mechanical_siege_ballista_unpacked.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/units/cart_mechanical_siege_ballista_unpacked.xml
+++ binaries/data/mods/public/simulation/templates/units/cart_mechanical_siege_ballista_unpacked.xml
@@ -10,9 +10,6 @@
0.001
-
- 0.001
-
units/carthaginians/siege_rock.xml
Index: binaries/data/mods/public/simulation/templates/units/cart_mechanical_siege_oxybeles_unpacked.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/units/cart_mechanical_siege_oxybeles_unpacked.xml
+++ binaries/data/mods/public/simulation/templates/units/cart_mechanical_siege_oxybeles_unpacked.xml
@@ -10,9 +10,6 @@
0.001
-
- 0.001
-
units/carthaginians/siege_spear.xml
Index: binaries/data/mods/public/simulation/templates/units/gaul_champion_fanatic.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/units/gaul_champion_fanatic.xml
+++ binaries/data/mods/public/simulation/templates/units/gaul_champion_fanatic.xml
@@ -24,11 +24,6 @@
5
-
- 1.5
- 600.0
- 5.0
-
units/celts/fanatic.xml
Index: binaries/data/mods/public/simulation/templates/units/mace_mechanical_siege_lithobolos_unpacked.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/units/mace_mechanical_siege_lithobolos_unpacked.xml
+++ binaries/data/mods/public/simulation/templates/units/mace_mechanical_siege_lithobolos_unpacked.xml
@@ -10,9 +10,6 @@
0.001
-
- 0.001
-
units/hellenes/siege_rock.xml
Index: binaries/data/mods/public/simulation/templates/units/mace_mechanical_siege_oxybeles_unpacked.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/units/mace_mechanical_siege_oxybeles_unpacked.xml
+++ binaries/data/mods/public/simulation/templates/units/mace_mechanical_siege_oxybeles_unpacked.xml
@@ -10,9 +10,6 @@
0.001
-
- 0.001
-
units/hellenes/siege_spear.xml
Index: binaries/data/mods/public/simulation/templates/units/maur_elephant_archer_b.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/units/maur_elephant_archer_b.xml
+++ binaries/data/mods/public/simulation/templates/units/maur_elephant_archer_b.xml
@@ -41,11 +41,6 @@
large
8.5
-
- 14.0
- 1000.0
- 10.0
-
units/mauryans/elephant_archer_b.xml
Index: binaries/data/mods/public/simulation/templates/units/maur_hero_chanakya.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/units/maur_hero_chanakya.xml
+++ binaries/data/mods/public/simulation/templates/units/maur_hero_chanakya.xml
@@ -50,11 +50,6 @@
9
-
- 12.0
- 200.0
- 0.0
-
30
Index: binaries/data/mods/public/simulation/templates/units/maur_support_elephant.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/units/maur_support_elephant.xml
+++ binaries/data/mods/public/simulation/templates/units/maur_support_elephant.xml
@@ -66,9 +66,6 @@
large
5.5
-
- 10.0
-
50
Index: binaries/data/mods/public/simulation/templates/units/ptol_mechanical_siege_lithobolos_unpacked.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/units/ptol_mechanical_siege_lithobolos_unpacked.xml
+++ binaries/data/mods/public/simulation/templates/units/ptol_mechanical_siege_lithobolos_unpacked.xml
@@ -10,9 +10,6 @@
0.001
-
- 0.001
-
units/hellenes/siege_rock.xml
Index: binaries/data/mods/public/simulation/templates/units/ptol_mechanical_siege_polybolos_unpacked.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/units/ptol_mechanical_siege_polybolos_unpacked.xml
+++ binaries/data/mods/public/simulation/templates/units/ptol_mechanical_siege_polybolos_unpacked.xml
@@ -10,9 +10,6 @@
0.001
-
- 0.001
-
units/ptolemies/siege_spear.xml
Index: binaries/data/mods/public/simulation/templates/units/rome_mechanical_siege_ballista_unpacked.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/units/rome_mechanical_siege_ballista_unpacked.xml
+++ binaries/data/mods/public/simulation/templates/units/rome_mechanical_siege_ballista_unpacked.xml
@@ -15,9 +15,6 @@
0.001
-
- 0.001
-
units/romans/siege_rock.xml
Index: binaries/data/mods/public/simulation/templates/units/rome_mechanical_siege_onager_unpacked.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/units/rome_mechanical_siege_onager_unpacked.xml
+++ binaries/data/mods/public/simulation/templates/units/rome_mechanical_siege_onager_unpacked.xml
@@ -24,9 +24,6 @@
0.001
-
- 0.001
-
88
Index: binaries/data/mods/public/simulation/templates/units/rome_mechanical_siege_scorpio_unpacked.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/units/rome_mechanical_siege_scorpio_unpacked.xml
+++ binaries/data/mods/public/simulation/templates/units/rome_mechanical_siege_scorpio_unpacked.xml
@@ -10,9 +10,6 @@
0.001
-
- 0.001
-
units/romans/siege_scorpio.xml
Index: binaries/data/mods/public/simulation/templates/units/sele_mechanical_siege_lithobolos_unpacked.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/units/sele_mechanical_siege_lithobolos_unpacked.xml
+++ binaries/data/mods/public/simulation/templates/units/sele_mechanical_siege_lithobolos_unpacked.xml
@@ -10,9 +10,6 @@
0.001
-
- 0.001
-
units/hellenes/siege_rock.xml
Index: binaries/data/mods/public/simulation/templates/units/spart_mechanical_siege_oxybeles_unpacked.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/units/spart_mechanical_siege_oxybeles_unpacked.xml
+++ binaries/data/mods/public/simulation/templates/units/spart_mechanical_siege_oxybeles_unpacked.xml
@@ -10,9 +10,6 @@
0.001
-
- 0.001
-
units/athenians/siege_spear.xml
Index: source/simulation2/MessageTypes.h
===================================================================
--- source/simulation2/MessageTypes.h
+++ source/simulation2/MessageTypes.h
@@ -317,21 +317,55 @@
};
/**
- * Sent by CCmpUnitMotion during Update, whenever the motion status has changed
- * since the previous update.
+ * Sent by CCmpUnitMotion during Update,
+ * whenever we have started actually moving and were not moving before.
+ * We may or may not already have been trying to move
*/
-class CMessageMotionChanged : public CMessage
+class CMessageBeginMove : public CMessage
{
public:
- DEFAULT_MESSAGE_IMPL(MotionChanged)
+ DEFAULT_MESSAGE_IMPL(BeginMove)
- CMessageMotionChanged(bool starting, bool error) :
- starting(starting), error(error)
+ CMessageBeginMove()
+ {
+ }
+};
+
+/**
+ * Sent by CCmpUnitMotion during Update,
+ * whenever we were actually moving before, and cannot continue
+ * this can be because we've arrived (failed=false) or we failed moving (failed=true)
+ * After this message is sent, the unit won't remove/repath without orders.
+ * Will never be sent on the same turn as MT_BeginMove.
+ */
+class CMessageFinishedMove : public CMessage
+{
+public:
+ DEFAULT_MESSAGE_IMPL(FinishedMove)
+
+ CMessageFinishedMove(bool fail) : failed(fail)
{
}
- bool starting; // whether this is a start or end of movement
- bool error; // whether we failed to start moving (couldn't find any path)
+ bool failed; // move failed
+};
+
+/**
+ * Sent by CCmpUnitMotion during Update,
+ * whenever we were actually moving before, and now stopped
+ * In this case, we will retry moving/pathing in the future on our own
+ * Unless ordered otherwise.
+ * We are just possibly stuck short-term, or must repath.
+ * Will never be sent on the same turn as MT_BeginMove.
+ */
+class CMessagePausedMove : public CMessage
+{
+public:
+ DEFAULT_MESSAGE_IMPL(PausedMove)
+
+ CMessagePausedMove()
+ {
+ }
};
/**
Index: source/simulation2/TypeList.h
===================================================================
--- source/simulation2/TypeList.h
+++ source/simulation2/TypeList.h
@@ -45,7 +45,9 @@
MESSAGE(PositionChanged)
MESSAGE(InterpolatedPositionChanged)
MESSAGE(TerritoryPositionChanged)
-MESSAGE(MotionChanged)
+MESSAGE(BeginMove)
+MESSAGE(FinishedMove)
+MESSAGE(PausedMove)
MESSAGE(RangeUpdate)
MESSAGE(TerrainChanged)
MESSAGE(VisibilityChanged)
Index: source/simulation2/components/CCmpObstructionManager.cpp
===================================================================
--- source/simulation2/components/CCmpObstructionManager.cpp
+++ source/simulation2/components/CCmpObstructionManager.cpp
@@ -20,6 +20,7 @@
#include "simulation2/system/Component.h"
#include "ICmpObstructionManager.h"
+#include "ICmpPosition.h"
#include "ICmpTerrain.h"
#include "simulation2/MessageTypes.h"
@@ -465,6 +466,9 @@
}
}
+ virtual bool IsInPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t px, entity_pos_t pz, entity_pos_t minRange, entity_pos_t maxRange);
+ virtual bool IsInTargetRange(entity_pos_t x, entity_pos_t z, entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange);
+
virtual bool TestLine(const IObstructionTestFilter& filter, entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, entity_pos_t r, bool relaxClearanceForUnits = false);
virtual bool TestStaticShape(const IObstructionTestFilter& filter, entity_pos_t x, entity_pos_t z, entity_pos_t a, entity_pos_t w, entity_pos_t h, std::vector* out);
virtual bool TestUnitShape(const IObstructionTestFilter& filter, entity_pos_t x, entity_pos_t z, entity_pos_t r, std::vector* out);
@@ -658,6 +662,83 @@
REGISTER_COMPONENT_TYPE(ObstructionManager)
+bool CCmpObstructionManager::IsInPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t px, entity_pos_t pz, entity_pos_t minRange, entity_pos_t maxRange)
+{
+ CFixedVector2D pos(x, z);
+
+ entity_pos_t distance = (pos - CFixedVector2D(px, pz)).Length();
+
+ if (distance < minRange)
+ return false;
+ else if (maxRange >= entity_pos_t::Zero() && distance > maxRange)
+ return false;
+ else
+ return true;
+}
+
+bool CCmpObstructionManager::IsInTargetRange(entity_pos_t x, entity_pos_t z, entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange)
+{
+ CFixedVector2D pos(x, z);
+
+ CmpPtr cmpObstructionManager(GetSystemEntity());
+ if (!cmpObstructionManager)
+ return false;
+
+ bool hasObstruction = false;
+ ICmpObstructionManager::ObstructionSquare obstruction;
+ CmpPtr cmpObstruction(GetSimContext(), target);
+ if (cmpObstruction)
+ hasObstruction = cmpObstruction->GetObstructionSquare(obstruction);
+
+ if (hasObstruction)
+ {
+ CFixedVector2D halfSize(obstruction.hw, obstruction.hh);
+ entity_pos_t distance = Geometry::DistanceToSquare(pos - CFixedVector2D(obstruction.x, obstruction.z), obstruction.u, obstruction.v, halfSize, true);
+
+ // Compare with previous obstruction
+ ICmpObstructionManager::ObstructionSquare previousObstruction;
+ cmpObstruction->GetPreviousObstructionSquare(previousObstruction);
+ entity_pos_t previousDistance = Geometry::DistanceToSquare(pos - CFixedVector2D(previousObstruction.x, previousObstruction.z), obstruction.u, obstruction.v, halfSize, true);
+
+ // See if we're too close to the target square
+ bool inside = distance.IsZero() && !Geometry::DistanceToSquare(pos - CFixedVector2D(obstruction.x, obstruction.z), obstruction.u, obstruction.v, halfSize).IsZero();
+ if ((distance < minRange && previousDistance < minRange) || inside)
+ return false;
+
+ // See if we're close enough to the target square
+ if (maxRange < entity_pos_t::Zero() || distance <= maxRange || previousDistance <= maxRange)
+ return true;
+
+ entity_pos_t circleRadius = halfSize.Length();
+
+ if (Geometry::ShouldTreatTargetAsCircle(maxRange, circleRadius))
+ {
+ // The target is small relative to our range, so pretend it's a circle
+ // and see if we're close enough to that.
+ // Also check circle around previous position.
+ entity_pos_t circleDistance = (pos - CFixedVector2D(obstruction.x, obstruction.z)).Length() - circleRadius;
+ entity_pos_t previousCircleDistance = (pos - CFixedVector2D(previousObstruction.x, previousObstruction.z)).Length() - circleRadius;
+
+ return circleDistance <= maxRange || previousCircleDistance <= maxRange;
+ }
+
+ // take minimal clearance required in MoveToTargetRange into account, multiplying by 3/2 for diagonals
+ return distance <= maxRange || previousDistance <= maxRange;
+ }
+ else
+ {
+ CmpPtr cmpTargetPosition(GetSimContext(), target);
+ if (!cmpTargetPosition || !cmpTargetPosition->IsInWorld())
+ return false;
+
+ CFixedVector2D targetPos = cmpTargetPosition->GetPreviousPosition2D();
+ entity_pos_t distance = (pos - targetPos).Length();
+
+ return minRange <= distance && (maxRange < entity_pos_t::Zero() || distance <= maxRange);
+ }
+}
+
+
bool CCmpObstructionManager::TestLine(const IObstructionTestFilter& filter, entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, entity_pos_t r, bool relaxClearanceForUnits)
{
PROFILE("TestLine");
Index: source/simulation2/components/CCmpPathfinder_Vertex.cpp
===================================================================
--- source/simulation2/components/CCmpPathfinder_Vertex.cpp
+++ source/simulation2/components/CCmpPathfinder_Vertex.cpp
@@ -654,8 +654,6 @@
edges.emplace_back(Edge{ ev3, ev0 });
}
- // TODO: should clip out vertexes and edges that are outside the range,
- // to reduce the search space
}
// Add terrain obstructions
Index: source/simulation2/components/CCmpUnitMotion.cpp
===================================================================
--- source/simulation2/components/CCmpUnitMotion.cpp
+++ source/simulation2/components/CCmpUnitMotion.cpp
@@ -27,6 +27,7 @@
#include "simulation2/components/ICmpPathfinder.h"
#include "simulation2/components/ICmpRangeManager.h"
#include "simulation2/components/ICmpValueModificationManager.h"
+#include "simulation2/components/ICmpVisual.h"
#include "simulation2/helpers/Geometry.h"
#include "simulation2/helpers/Render.h"
#include "simulation2/MessageTypes.h"
@@ -106,15 +107,104 @@
*/
static const fixed CHECK_TARGET_MOVEMENT_MIN_COS = fixed::FromInt(866)/1000;
+/**
+ * See unitmotion logic for details. Higher means units will retry more often before potentially failing.
+ */
+static const size_t MAX_PATH_REATTEMPS = 8;
+
static const CColor OVERLAY_COLOR_LONG_PATH(1, 1, 1, 1);
static const CColor OVERLAY_COLOR_SHORT_PATH(1, 0, 0, 1);
class CCmpUnitMotion : public ICmpUnitMotion
{
+private:
+ struct SMotionGoal
+ {
+ private:
+ bool m_Valid = false;
+
+ entity_pos_t m_TargetMinRange;
+ entity_pos_t m_TargetMaxRange;
+
+ entity_id_t m_TargetEntity;
+ // pathfinder-compliant goal.
+ PathGoal m_Goal;
+ public:
+ SMotionGoal() : m_Valid(false) {};
+
+ SMotionGoal(PathGoal& goal, entity_pos_t minRange, entity_pos_t maxRange)
+ {
+ m_TargetEntity = INVALID_ENTITY;
+
+ m_TargetMinRange = minRange;
+ m_TargetMaxRange = maxRange;
+
+ m_Goal = goal;
+ m_Valid = true;
+ }
+
+ SMotionGoal(const CSimContext& context, entity_id_t target, PathGoal& goal, entity_pos_t minRange, entity_pos_t maxRange)
+ {
+ m_TargetEntity = target;
+ m_TargetMinRange = minRange;
+ m_TargetMaxRange = maxRange;
+
+ m_Goal = goal;
+ m_Valid = true;
+
+ UpdateTargetPosition(context);
+ }
+
+ template
+ void SerializeCommon(S& serialize)
+ {
+ serialize.Bool("valid", m_Valid);
+
+ serialize.NumberFixed_Unbounded("target min range", m_TargetMinRange);
+ serialize.NumberFixed_Unbounded("target max range", m_TargetMaxRange);
+
+ serialize.NumberU32_Unbounded("target entity", m_TargetEntity);
+
+ SerializeGoal()(serialize, "goal", m_Goal);
+ }
+
+ const PathGoal& Goal() const { return m_Goal; };
+
+ bool TargetIsEntity() const { return m_TargetEntity != INVALID_ENTITY; }
+ entity_id_t GetEntity() const { return m_TargetEntity; }
+
+ bool Valid() const { return m_Valid; }
+ void Clear() { m_Valid = false; }
+
+ entity_pos_t MinRange() const { return m_TargetMinRange; };
+ entity_pos_t MaxRange() const { return m_TargetMaxRange; };
+
+ CFixedVector2D Pos() const { return CFixedVector2D(m_Goal.x, m_Goal.z); }
+ entity_pos_t X() const { return m_Goal.x; }
+ entity_pos_t Z() const { return m_Goal.z; }
+
+ void UpdateTargetPosition(const CSimContext& context)
+ {
+ if (!TargetIsEntity())
+ return;
+
+ CmpPtr cmpPosition(context, m_TargetEntity);
+ if (!cmpPosition || !cmpPosition->IsInWorld())
+ return;
+
+ m_Goal.x = cmpPosition->GetPosition2D().X;
+ m_Goal.z = cmpPosition->GetPosition2D().Y;
+ }
+
+ bool IsNotAPoint() const
+ {
+ return m_TargetMaxRange > fixed::Zero() || m_Goal.type != PathGoal::POINT;
+ }
+ };
+
public:
static void ClassInit(CComponentManager& componentManager)
{
- componentManager.SubscribeToMessageType(MT_Update_MotionFormation);
componentManager.SubscribeToMessageType(MT_Update_MotionUnit);
componentManager.SubscribeToMessageType(MT_PathResult);
componentManager.SubscribeToMessageType(MT_OwnershipChanged);
@@ -128,121 +218,46 @@
std::vector m_DebugOverlayLongPathLines;
std::vector m_DebugOverlayShortPathLines;
- // Template state:
-
- bool m_FormationController;
- fixed m_WalkSpeed, m_OriginalWalkSpeed; // in metres per second
- fixed m_RunSpeed, m_OriginalRunSpeed;
+ // Template state, never changed after init.
+ fixed m_TemplateWalkSpeed, m_TemplateTopSpeedRatio;
pass_class_t m_PassClass;
std::string m_PassClassName;
-
- // Dynamic state:
-
entity_pos_t m_Clearance;
- bool m_Moving;
- bool m_FacePointAfterMove;
-
- enum State
- {
- /*
- * Not moving at all.
- */
- STATE_IDLE,
-
- /*
- * Not moving at all. Will go to IDLE next turn.
- * (This one-turn delay is a hack to fix animation timings.)
- */
- STATE_STOPPING,
-
- /*
- * Member of a formation.
- * Pathing to the target (depending on m_PathState).
- * Target is m_TargetEntity plus m_TargetOffset.
- */
- STATE_FORMATIONMEMBER_PATH,
-
- /*
- * Individual unit or formation controller.
- * Pathing to the target (depending on m_PathState).
- * Target is m_TargetPos, m_TargetMinRange, m_TargetMaxRange;
- * if m_TargetEntity is not INVALID_ENTITY then m_TargetPos is tracking it.
- */
- STATE_INDIVIDUAL_PATH,
-
- STATE_MAX
- };
- u8 m_State;
-
- enum PathState
- {
- /*
- * There is no path.
- * (This should only happen in IDLE and STOPPING.)
- */
- PATHSTATE_NONE,
-
- /*
- * We have an outstanding long path request.
- * No paths are usable yet, so we can't move anywhere.
- */
- PATHSTATE_WAITING_REQUESTING_LONG,
-
- /*
- * We have an outstanding short path request.
- * m_LongPath is valid.
- * m_ShortPath is not yet valid, so we can't move anywhere.
- */
- PATHSTATE_WAITING_REQUESTING_SHORT,
-
- /*
- * We are following our path, and have no path requests.
- * m_LongPath and m_ShortPath are valid.
- */
- PATHSTATE_FOLLOWING,
-
- /*
- * We are following our path, and have an outstanding long path request.
- * (This is because our target moved a long way and we need to recompute
- * the whole path).
- * m_LongPath and m_ShortPath are valid.
- */
- PATHSTATE_FOLLOWING_REQUESTING_LONG,
-
- /*
- * We are following our path, and have an outstanding short path request.
- * (This is because our target moved and we've got a new long path
- * which we need to follow).
- * m_LongPath is valid; m_ShortPath is valid but obsolete.
- */
- PATHSTATE_FOLLOWING_REQUESTING_SHORT,
-
- PATHSTATE_MAX
- };
- u8 m_PathState;
-
- u32 m_ExpectedPathTicket; // asynchronous request ID we're waiting for, or 0 if none
- entity_id_t m_TargetEntity;
- CFixedVector2D m_TargetPos;
- CFixedVector2D m_TargetOffset;
- entity_pos_t m_TargetMinRange;
- entity_pos_t m_TargetMaxRange;
+ // cached for efficiency
+ fixed m_TechModifiedWalkSpeed, m_TechModifiedTopSpeedRatio;
+ // TARGET
+ // As long as we have a valid target, the unit is considered "on the move".
+ // It may not be actually moving for a variety of reasons (no path, blocked path)… but it will shortly.
+ SMotionGoal m_FinalGoal;
+
+ // MOTION PLANNING
+ // We will abort if we are stuck after X tries.
+ u8 m_AbortIfStuck;
+ // turn towards our target at the end
+ bool m_FacePointAfterMove;
+ // actual unit speed, after technology and ratio
fixed m_Speed;
+ // cached for convenience
+ fixed m_SpeedRatio;
- // Current mean speed (over the last turn).
- fixed m_CurSpeed;
+ // asynchronous request ID we're waiting for, or 0 if none
+ u32 m_ExpectedPathTicket;
// Currently active paths (storing waypoints in reverse order).
// The last item in each path is the point we're currently heading towards.
- WaypointPath m_LongPath;
- WaypointPath m_ShortPath;
-
- // Motion planning
- u8 m_Tries; // how many tries we've done to get to our current Final Goal.
-
- PathGoal m_FinalGoal;
+ WaypointPath m_Path;
+ // used for the short pathfinder, incremented on each unsuccessful try.
+ u8 m_Tries;
+ // Turns to wait before a certain action.
+ u8 m_WaitingTurns;
+ // if we actually started moving at some point.
+ bool m_StartedMoving;
+
+ // Speed over the last turn
+ // cached so we can tell the visual actor when it changes
+ fixed m_ActualSpeed;
static std::string GetSchema()
{
@@ -259,14 +274,8 @@
""
""
""
- ""
- ""
- ""
- ""
- ""
- ""
- ""
- ""
+ ""
+ ""
""
""
""
@@ -276,19 +285,15 @@
virtual void Init(const CParamNode& paramNode)
{
- m_FormationController = paramNode.GetChild("FormationController").ToBool();
-
- m_Moving = false;
m_FacePointAfterMove = true;
- m_WalkSpeed = m_OriginalWalkSpeed = paramNode.GetChild("WalkSpeed").ToFixed();
- m_Speed = m_WalkSpeed;
- m_CurSpeed = fixed::Zero();
-
- if (paramNode.GetChild("Run").IsOk())
- m_RunSpeed = m_OriginalRunSpeed = paramNode.GetChild("Run").GetChild("Speed").ToFixed();
- else
- m_RunSpeed = m_OriginalRunSpeed = m_WalkSpeed;
+ m_TechModifiedWalkSpeed = m_TemplateWalkSpeed = m_Speed = paramNode.GetChild("WalkSpeed").ToFixed();
+ m_ActualSpeed = fixed::Zero();
+ m_SpeedRatio = fixed::FromInt(1);
+
+ m_TechModifiedTopSpeedRatio = m_TemplateTopSpeedRatio = fixed::FromInt(1);
+ if (paramNode.GetChild("RunMultiplier").IsOk())
+ m_TechModifiedTopSpeedRatio = m_TemplateTopSpeedRatio = paramNode.GetChild("RunMultiplier").ToFixed();
CmpPtr cmpPathfinder(GetSystemEntity());
if (cmpPathfinder)
@@ -302,18 +307,13 @@
cmpObstruction->SetUnitClearance(m_Clearance);
}
- m_State = STATE_IDLE;
- m_PathState = PATHSTATE_NONE;
-
m_ExpectedPathTicket = 0;
m_Tries = 0;
-
- m_TargetEntity = INVALID_ENTITY;
-
- m_FinalGoal.type = PathGoal::POINT;
+ m_WaitingTurns = 0;
m_DebugOverlayEnabled = false;
+ m_AbortIfStuck = 0;
}
virtual void Deinit()
@@ -323,33 +323,24 @@
template
void SerializeCommon(S& serialize)
{
- serialize.NumberU8("state", m_State, 0, STATE_MAX-1);
- serialize.NumberU8("path state", m_PathState, 0, PATHSTATE_MAX-1);
-
- serialize.StringASCII("pass class", m_PassClassName, 0, 64);
+ serialize.NumberU8("abort if stuck", m_AbortIfStuck, 0, 255);
+ serialize.Bool("face point after move", m_FacePointAfterMove);
+ serialize.NumberFixed_Unbounded("speed", m_Speed);
+ serialize.NumberFixed_Unbounded("speed ratio", m_SpeedRatio);
serialize.NumberU32_Unbounded("ticket", m_ExpectedPathTicket);
- serialize.NumberU32_Unbounded("target entity", m_TargetEntity);
- serialize.NumberFixed_Unbounded("target pos x", m_TargetPos.X);
- serialize.NumberFixed_Unbounded("target pos y", m_TargetPos.Y);
- serialize.NumberFixed_Unbounded("target offset x", m_TargetOffset.X);
- serialize.NumberFixed_Unbounded("target offset y", m_TargetOffset.Y);
- serialize.NumberFixed_Unbounded("target min range", m_TargetMinRange);
- serialize.NumberFixed_Unbounded("target max range", m_TargetMaxRange);
-
- serialize.NumberFixed_Unbounded("speed", m_Speed);
- serialize.NumberFixed_Unbounded("current speed", m_CurSpeed);
-
- serialize.Bool("moving", m_Moving);
- serialize.Bool("facePointAfterMove", m_FacePointAfterMove);
+ SerializeVector()(serialize, "path", m_Path.m_Waypoints);
serialize.NumberU8("tries", m_Tries, 0, 255);
+ serialize.NumberU8("waiting turns", m_WaitingTurns, 0, 255);
- SerializeVector()(serialize, "long path", m_LongPath.m_Waypoints);
- SerializeVector()(serialize, "short path", m_ShortPath.m_Waypoints);
+ serialize.Bool("started moving", m_StartedMoving);
- SerializeGoal()(serialize, "goal", m_FinalGoal);
+ // strictly speaking this doesn't need to be serialized since it's graphics-only, but it's nicer to.
+ serialize.NumberFixed_Unbounded("actual speed", m_ActualSpeed);
+
+ m_FinalGoal.SerializeCommon(serialize);
}
virtual void Serialize(ISerializer& serialize)
@@ -372,22 +363,10 @@
{
switch (msg.GetType())
{
- case MT_Update_MotionFormation:
- {
- if (m_FormationController)
- {
- fixed dt = static_cast (msg).turnLength;
- Move(dt);
- }
- break;
- }
case MT_Update_MotionUnit:
{
- if (!m_FormationController)
- {
- fixed dt = static_cast (msg).turnLength;
- Move(dt);
- }
+ fixed dt = static_cast (msg).turnLength;
+ Move(dt);
break;
}
case MT_RenderSubmit:
@@ -410,24 +389,25 @@
break;
}
// fall-through
- case MT_OwnershipChanged:
case MT_Deserialized:
{
+ // tell the visual actor our speed.
+ // don't call setactualspeed since the if check will return immediately.
+ CmpPtr cmpVisualActor(GetEntityHandle());
+ if (cmpVisualActor)
+ cmpVisualActor->SetMovingSpeed(m_ActualSpeed);
+ }
+ case MT_OwnershipChanged:
+ {
CmpPtr cmpValueModificationManager(GetSystemEntity());
if (!cmpValueModificationManager)
break;
- fixed newWalkSpeed = cmpValueModificationManager->ApplyModifications(L"UnitMotion/WalkSpeed", m_OriginalWalkSpeed, GetEntityId());
- fixed newRunSpeed = cmpValueModificationManager->ApplyModifications(L"UnitMotion/Run/Speed", m_OriginalRunSpeed, GetEntityId());
+ m_TechModifiedWalkSpeed = cmpValueModificationManager->ApplyModifications(L"UnitMotion/WalkSpeed", m_TemplateWalkSpeed, GetEntityId());
+ m_TechModifiedTopSpeedRatio = cmpValueModificationManager->ApplyModifications(L"UnitMotion/RunMultiplier", m_TemplateTopSpeedRatio, GetEntityId());
- // update m_Speed (the actual speed) if set to one of the variables
- if (m_Speed == m_WalkSpeed)
- m_Speed = newWalkSpeed;
- else if (m_Speed == m_RunSpeed)
- m_Speed = newRunSpeed;
+ m_Speed = m_SpeedRatio.Multiply(GetBaseSpeed());
- m_WalkSpeed = newWalkSpeed;
- m_RunSpeed = newRunSpeed;
break;
}
}
@@ -439,19 +419,53 @@
GetSimContext().GetComponentManager().DynamicSubscriptionNonsync(MT_RenderSubmit, this, needRender);
}
- virtual bool IsMoving()
+ virtual bool IsActuallyMoving()
{
- return m_Moving;
+ return m_StartedMoving;
}
- virtual fixed GetWalkSpeed()
+ virtual bool IsTryingToMove()
{
- return m_WalkSpeed;
+ // speed check as sanity check to avoid infinite loops.
+ return m_FinalGoal.Valid() && m_Speed > fixed::Zero();
}
- virtual fixed GetRunSpeed()
+ virtual fixed GetBaseSpeed()
{
- return m_RunSpeed;
+ return m_TechModifiedWalkSpeed;
+ }
+
+ virtual fixed GetSpeed()
+ {
+ return m_Speed;
+ }
+
+ virtual fixed GetSpeedRatio()
+ {
+ return m_SpeedRatio;
+ }
+
+ virtual fixed GetTopSpeedRatio()
+ {
+ return m_TechModifiedTopSpeedRatio;
+ }
+
+ virtual void SetSpeed(fixed ratio)
+ {
+ m_SpeedRatio = std::min(ratio, GetTopSpeedRatio());
+ m_Speed = m_SpeedRatio.Multiply(GetBaseSpeed());
+ }
+
+ // convenience wrapper
+ void SetActualSpeed(fixed newRealSpeed)
+ {
+ if (m_ActualSpeed == newRealSpeed)
+ return;
+
+ m_ActualSpeed = newRealSpeed;
+ CmpPtr cmpVisualActor(GetEntityHandle());
+ if (cmpVisualActor)
+ cmpVisualActor->SetMovingSpeed(m_ActualSpeed);
}
virtual pass_class_t GetPassabilityClass()
@@ -472,16 +486,6 @@
m_PassClass = cmpPathfinder->GetPassabilityClass(passClassName);
}
- virtual fixed GetCurrentSpeed()
- {
- return m_CurSpeed;
- }
-
- virtual void SetSpeed(fixed speed)
- {
- m_Speed = speed;
- }
-
virtual void SetFacePointAfterMove(bool facePointAfterMove)
{
m_FacePointAfterMove = facePointAfterMove;
@@ -493,101 +497,110 @@
UpdateMessageSubscriptions();
}
+ virtual entity_pos_t GetUnitClearance()
+ {
+ return m_Clearance;
+ }
+
virtual bool MoveToPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t minRange, entity_pos_t maxRange);
virtual bool IsInPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t minRange, entity_pos_t maxRange);
virtual bool MoveToTargetRange(entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange);
virtual bool IsInTargetRange(entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange);
- virtual void MoveToFormationOffset(entity_id_t target, entity_pos_t x, entity_pos_t z);
virtual void FaceTowardsPoint(entity_pos_t x, entity_pos_t z);
- virtual void StopMoving()
+ virtual void SetAbortIfStuck(u8 shouldAbort)
{
- m_Moving = false;
- m_ExpectedPathTicket = 0;
- m_State = STATE_STOPPING;
- m_PathState = PATHSTATE_NONE;
- m_LongPath.m_Waypoints.clear();
- m_ShortPath.m_Waypoints.clear();
+ m_AbortIfStuck = shouldAbort;
}
- virtual entity_pos_t GetUnitClearance()
+ virtual void DiscardMove()
{
- return m_Clearance;
+ StopMovingQuietly();
}
-private:
- bool ShouldAvoidMovingUnits() const
+ // stop moving and send message
+ virtual void CompleteMove()
{
- return !m_FormationController;
+ // highlight bugs.
+ if (!IsTryingToMove())
+ {
+ LOGERROR("Entity %i trying to stop moving but has not actually started", GetEntityId());
+ return;
+ }
+
+ StopMovingQuietly();
+
+ if (m_FacePointAfterMove)
+ {
+ CmpPtr cmpPosition(GetEntityHandle());
+ if (cmpPosition && cmpPosition->IsInWorld())
+ FaceTowardsPointFromPos(cmpPosition->GetPosition2D(), m_FinalGoal.X(), m_FinalGoal.Z());
+ }
+
+ CMessageFinishedMove msg(false);
+ GetSimContext().GetComponentManager().PostMessage(GetEntityId(), msg);
}
+private:
+/*
+ TODO: reimplement
bool IsFormationMember() const
{
return m_State == STATE_FORMATIONMEMBER_PATH;
}
-
+*/
entity_id_t GetGroup() const
{
- return IsFormationMember() ? m_TargetEntity : GetEntityId();
+ //return IsFormationMember() ? m_TargetEntity : GetEntityId();
+ return GetEntityId();
}
bool HasValidPath() const
{
- return m_PathState == PATHSTATE_FOLLOWING
- || m_PathState == PATHSTATE_FOLLOWING_REQUESTING_LONG
- || m_PathState == PATHSTATE_FOLLOWING_REQUESTING_SHORT;
+ return !m_Path.m_Waypoints.empty();
}
- void StartFailed()
+ void StopMovingQuietly()
{
- StopMoving();
- m_State = STATE_IDLE; // don't go through the STOPPING state since we never even started
+ // sanity
+ m_Tries = 0;
+ m_WaitingTurns = 0;
+ m_StartedMoving = false;
+
+ SetActualSpeed(fixed::Zero());
+
+ // reset state.
+ m_ExpectedPathTicket = 0;
+ m_FinalGoal.Clear();
+ m_Path.m_Waypoints.clear();
CmpPtr cmpObstruction(GetEntityHandle());
if (cmpObstruction)
cmpObstruction->SetMovingFlag(false);
-
- CMessageMotionChanged msg(true, true);
- GetSimContext().GetComponentManager().PostMessage(GetEntityId(), msg);
}
void MoveFailed()
{
- StopMoving();
-
- CmpPtr cmpObstruction(GetEntityHandle());
- if (cmpObstruction)
- cmpObstruction->SetMovingFlag(false);
+ StopMovingQuietly();
- CMessageMotionChanged msg(false, true);
+ CMessageFinishedMove msg(true);
GetSimContext().GetComponentManager().PostMessage(GetEntityId(), msg);
}
- void StartSucceeded()
+ void MovePaused()
{
- CmpPtr cmpObstruction(GetEntityHandle());
- if (cmpObstruction)
- cmpObstruction->SetMovingFlag(true);
+ m_StartedMoving = false;
- m_Moving = true;
-
- CMessageMotionChanged msg(true, false);
+ CMessagePausedMove msg;
GetSimContext().GetComponentManager().PostMessage(GetEntityId(), msg);
}
- void MoveSucceeded()
+ void MoveStarted()
{
- m_Moving = false;
-
- CmpPtr cmpObstruction(GetEntityHandle());
- if (cmpObstruction)
- cmpObstruction->SetMovingFlag(false);
+ m_StartedMoving = true;
- // No longer moving, so speed is 0.
- m_CurSpeed = fixed::Zero();
-
- CMessageMotionChanged msg(false, false);
+ CMessageBeginMove msg;
GetSimContext().GetComponentManager().PostMessage(GetEntityId(), msg);
}
@@ -604,45 +617,16 @@
void Move(fixed dt);
/**
- * Decide whether to approximate the given range from a square target as a circle,
- * rather than as a square.
- */
- bool ShouldTreatTargetAsCircle(entity_pos_t range, entity_pos_t circleRadius) const;
-
- /**
- * Computes the current location of our target entity (plus offset).
- * Returns false if no target entity or no valid position.
- */
- bool ComputeTargetPosition(CFixedVector2D& out);
-
- /**
- * Attempts to replace the current path with a straight line to the goal,
- * if this goal is a point, is close enough and the route is not obstructed.
- */
- bool TryGoingStraightToGoalPoint(const CFixedVector2D& from);
-
- /**
- * Attempts to replace the current path with a straight line to the target
- * entity, if it's close enough and the route is not obstructed.
- */
- bool TryGoingStraightToTargetEntity(const CFixedVector2D& from);
-
- /**
* Returns whether the target entity has moved more than minDelta since our
* last path computations, and we're close enough to it to care.
*/
bool CheckTargetMovement(const CFixedVector2D& from, entity_pos_t minDelta);
/**
- * Update goal position if moving target
- */
- void UpdateFinalGoal();
-
- /**
* Returns whether we are close enough to the target to assume it's a good enough
* position to stop.
*/
- bool ShouldConsiderOurselvesAtDestination(const CFixedVector2D& from);
+ bool ShouldConsiderOurselvesAtDestination();
/**
* Returns whether the length of the given path, plus the distance from
@@ -663,11 +647,11 @@
ControlGroupMovementObstructionFilter GetObstructionFilter(bool noTarget = false) const;
/**
- * Start moving to the given goal, from our current position 'from'.
+ * Dump current paths and request a new one.
* Might go in a straight line immediately, or might start an asynchronous
* path request.
*/
- void BeginPathing(const CFixedVector2D& from, const PathGoal& goal);
+ void RequestNewPath();
/**
* Start an asynchronous long path query.
@@ -691,489 +675,295 @@
void CCmpUnitMotion::PathResult(u32 ticket, const WaypointPath& path)
{
- // reset our state for sanity.
- CmpPtr cmpObstruction(GetEntityHandle());
- if (cmpObstruction)
- cmpObstruction->SetMovingFlag(false);
-
- m_Moving = false;
-
// Ignore obsolete path requests
if (ticket != m_ExpectedPathTicket)
return;
m_ExpectedPathTicket = 0; // we don't expect to get this result again
- // Check that we are still able to do something with that path
- CmpPtr cmpPosition(GetEntityHandle());
- if (!cmpPosition || !cmpPosition->IsInWorld())
- {
- if (m_PathState == PATHSTATE_WAITING_REQUESTING_LONG || m_PathState == PATHSTATE_WAITING_REQUESTING_SHORT)
- StartFailed();
- else if (m_PathState == PATHSTATE_FOLLOWING_REQUESTING_LONG || m_PathState == PATHSTATE_FOLLOWING_REQUESTING_SHORT)
- StopMoving();
+ if (!m_FinalGoal.Valid())
return;
- }
- if (m_PathState == PATHSTATE_WAITING_REQUESTING_LONG || m_PathState == PATHSTATE_FOLLOWING_REQUESTING_LONG)
+ if (path.m_Waypoints.empty())
{
- m_LongPath = path;
-
- // If we are following a path, leave the old m_ShortPath so we can carry on following it
- // until a new short path has been computed
- if (m_PathState == PATHSTATE_WAITING_REQUESTING_LONG)
- m_ShortPath.m_Waypoints.clear();
-
- // If there's no waypoints then we couldn't get near the target.
- // Sort of hack: Just try going directly to the goal point instead
- // (via the short pathfinder), so if we're stuck and the user clicks
- // close enough to the unit then we can probably get unstuck
- if (m_LongPath.m_Waypoints.empty())
- m_LongPath.m_Waypoints.emplace_back(Waypoint{ m_FinalGoal.x, m_FinalGoal.z });
-
- if (!HasValidPath())
- StartSucceeded();
-
- m_PathState = PATHSTATE_FOLLOWING;
-
- if (cmpObstruction)
- cmpObstruction->SetMovingFlag(true);
+ // no waypoints, path failed.
+ // if we have some room, pop waypoint
+ // TODO: this isn't particularly bright.
+ if (!m_Path.m_Waypoints.empty())
+ m_Path.m_Waypoints.pop_back();
- m_Moving = true;
+ // we will then deal with this on the next Move() call.
+ return;
}
- else if (m_PathState == PATHSTATE_WAITING_REQUESTING_SHORT || m_PathState == PATHSTATE_FOLLOWING_REQUESTING_SHORT)
- {
- m_ShortPath = path;
- // If there's no waypoints then we couldn't get near the target
- if (m_ShortPath.m_Waypoints.empty())
- {
- // If we're globally following a long path, try to remove the next waypoint, it might be obstructed
- // If not, and we are not in a formation, retry
- // unless we are close to our target and we don't have a target entity.
- // This makes sure that units don't clump too much when they are not in a formation and tasked to move.
- if (m_LongPath.m_Waypoints.size() > 1)
- m_LongPath.m_Waypoints.pop_back();
- else if (IsFormationMember())
- {
- m_Moving = false;
- CMessageMotionChanged msg(true, true);
- GetSimContext().GetComponentManager().PostMessage(GetEntityId(), msg);
- return;
- }
-
- CMessageMotionChanged msg(false, false);
- GetSimContext().GetComponentManager().PostMessage(GetEntityId(), msg);
-
- CmpPtr cmpPosition(GetEntityHandle());
- if (!cmpPosition || !cmpPosition->IsInWorld())
- return;
-
- CFixedVector2D pos = cmpPosition->GetPosition2D();
-
- if (ShouldConsiderOurselvesAtDestination(pos))
- return;
-
- UpdateFinalGoal();
- RequestLongPath(pos, m_FinalGoal);
- m_PathState = PATHSTATE_WAITING_REQUESTING_LONG;
- return;
- }
-
- // else we could, so reset our number of tries.
- m_Tries = 0;
-
- // Now we've got a short path that we can follow
- if (!HasValidPath())
- StartSucceeded();
-
- m_PathState = PATHSTATE_FOLLOWING;
-
- if (cmpObstruction)
- cmpObstruction->SetMovingFlag(true);
-
- m_Moving = true;
- }
- else
- LOGWARNING("unexpected PathResult (%u %d %d)", GetEntityId(), m_State, m_PathState);
+ // add to the top of our current waypoints
+ m_Path.m_Waypoints.insert(m_Path.m_Waypoints.end(), path.m_Waypoints.begin(), path.m_Waypoints.end());
}
void CCmpUnitMotion::Move(fixed dt)
{
PROFILE("Move");
- if (m_State == STATE_STOPPING)
+ if (!IsTryingToMove())
{
- m_State = STATE_IDLE;
- MoveSucceeded();
+ SetActualSpeed(fixed::Zero());
return;
}
- if (m_State == STATE_IDLE)
+ m_FinalGoal.UpdateTargetPosition(GetSimContext());
+
+ // TODO: units will look at each other's position in an arbitrary order that must be the same for any simulation
+ // In particular this means no threading. Maybe we should update this someday if it's possible.
+
+ CmpPtr cmpPathfinder(GetSystemEntity());
+ if (!cmpPathfinder)
return;
- switch (m_PathState)
- {
- case PATHSTATE_NONE:
- {
- // If we're not pathing, do nothing
+ CmpPtr cmpPosition(GetEntityHandle());
+ if (!cmpPosition || !cmpPosition->IsInWorld())
return;
- }
- case PATHSTATE_WAITING_REQUESTING_LONG:
- case PATHSTATE_WAITING_REQUESTING_SHORT:
+ CFixedVector2D initialPos = cmpPosition->GetPosition2D();
+
+ // Check wether we are at our destination.
+ // This must be done only at the beginning of a turn, if we do at the end of a turn (after a unit position has changed)
+ // the unit's position will interpolate but the unit will already be doing the next thing, so it looks like it's gliding.
+ if (ShouldConsiderOurselvesAtDestination())
{
- // If we're waiting for a path and don't have one yet, do nothing
+ CompleteMove();
return;
}
- case PATHSTATE_FOLLOWING:
- case PATHSTATE_FOLLOWING_REQUESTING_SHORT:
- case PATHSTATE_FOLLOWING_REQUESTING_LONG:
- {
- // TODO: there's some asymmetry here when units look at other
- // units' positions - the result will depend on the order of execution.
- // Maybe we should split the updates into multiple phases to minimise
- // that problem.
+ // TODO: here should go things such as:
+ // - has our target moved enough that we should re-path?
+ // end TODO
- CmpPtr cmpPathfinder(GetSystemEntity());
- if (!cmpPathfinder)
- return;
+ // Keep track of the current unit's position during the update
+ CFixedVector2D pos = initialPos;
- CmpPtr cmpPosition(GetEntityHandle());
- if (!cmpPosition || !cmpPosition->IsInWorld())
- return;
+ // Find the speed factor of the underlying terrain
+ // (We only care about the tile we start on - it doesn't matter if we're moving
+ // partially onto a much slower/faster tile)
+ // TODO: Terrain-dependent speeds are not currently supported
+ // TODO: note that this is also linked to pathfinding so maybe never supported
+ // fixed terrainSpeed = fixed::FromInt(1);
- CFixedVector2D initialPos = cmpPosition->GetPosition2D();
+ bool wasObstructed = false;
- // If we're chasing a potentially-moving unit and are currently close
- // enough to its current position, and we can head in a straight line
- // to it, then throw away our current path and go straight to it
- if (m_PathState == PATHSTATE_FOLLOWING)
- TryGoingStraightToTargetEntity(initialPos);
-
- // Keep track of the current unit's position during the update
- CFixedVector2D pos = initialPos;
-
- // If in formation, run to keep up; otherwise just walk
- fixed basicSpeed;
- if (IsFormationMember())
- basicSpeed = GetRunSpeed();
- else
- basicSpeed = m_Speed; // (typically but not always WalkSpeed)
+ // We want to move (at most) m_Speed*dt units from pos towards the next waypoint
- // Find the speed factor of the underlying terrain
- // (We only care about the tile we start on - it doesn't matter if we're moving
- // partially onto a much slower/faster tile)
- // TODO: Terrain-dependent speeds are not currently supported
- fixed terrainSpeed = fixed::FromInt(1);
+ fixed timeLeft = dt;
- fixed maxSpeed = basicSpeed.Multiply(terrainSpeed);
-
- bool wasObstructed = false;
-
- // We want to move (at most) maxSpeed*dt units from pos towards the next waypoint
+ // TODO: I think this may be a little buggy if we want to compute it several times per turn.
+ while (timeLeft > fixed::Zero())
+ {
+ // If we ran out of path, we have to stop
+ if (!HasValidPath())
+ break;
- fixed timeLeft = dt;
- fixed zero = fixed::Zero();
+ CFixedVector2D target;
+ target = CFixedVector2D(m_Path.m_Waypoints.back().x, m_Path.m_Waypoints.back().z);
- while (timeLeft > zero)
+ CFixedVector2D offset = target - pos;
+ fixed offsetLength = offset.Length();
+ // Work out how far we can travel in timeLeft
+ fixed maxdist = m_Speed.Multiply(timeLeft);
+
+ CFixedVector2D destination;
+ if (offsetLength <= maxdist)
+ destination = target;
+ else
{
- // If we ran out of path, we have to stop
- if (m_ShortPath.m_Waypoints.empty() && m_LongPath.m_Waypoints.empty())
- break;
-
- CFixedVector2D target;
- if (m_ShortPath.m_Waypoints.empty())
- target = CFixedVector2D(m_LongPath.m_Waypoints.back().x, m_LongPath.m_Waypoints.back().z);
- else
- target = CFixedVector2D(m_ShortPath.m_Waypoints.back().x, m_ShortPath.m_Waypoints.back().z);
-
- CFixedVector2D offset = target - pos;
-
- // Work out how far we can travel in timeLeft
- fixed maxdist = maxSpeed.Multiply(timeLeft);
-
- // 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))
- {
- pos = target;
-
- // Spend the rest of the time heading towards the next waypoint
- timeLeft = timeLeft - (offsetLength / maxSpeed);
-
- if (m_ShortPath.m_Waypoints.empty())
- m_LongPath.m_Waypoints.pop_back();
- else
- m_ShortPath.m_Waypoints.pop_back();
-
- continue;
- }
- else
- {
- // Error - path was obstructed
- wasObstructed = true;
- break;
- }
- }
- else
- {
- // Not close enough, so just move in the right direction
- offset.Normalize(maxdist);
- target = pos + offset;
-
- if (cmpPathfinder->CheckMovement(GetObstructionFilter(), pos.X, pos.Y, target.X, target.Y, m_Clearance, m_PassClass))
- pos = target;
- else
- wasObstructed = true; // Error - path was obstructed
-
- break;
- }
+ offset.Normalize(maxdist);
+ destination = pos + offset;
}
- // Update the Position component after our movement (if we actually moved anywhere)
- if (pos != initialPos)
+ // TODO: try moving as much as we can still?
+ // TODO: get more information about what blocked us.
+ if (cmpPathfinder->CheckMovement(GetObstructionFilter(), pos.X, pos.Y, destination.X, destination.Y, m_Clearance, m_PassClass))
{
- CFixedVector2D offset = pos - initialPos;
+ pos = destination;
- // Face towards the target
- entity_angle_t angle = atan2_approx(offset.X, offset.Y);
- cmpPosition->MoveAndTurnTo(pos.X,pos.Y, angle);
+ timeLeft = (timeLeft.Multiply(m_Speed) - offsetLength) / m_Speed;
- // Calculate the mean speed over this past turn.
- m_CurSpeed = cmpPosition->GetDistanceTravelled() / dt;
+ if (destination == target)
+ m_Path.m_Waypoints.pop_back();
+ continue;
}
-
- if (wasObstructed)
+ else
{
- // Oops, we hit something (very likely another unit).
- // This is when we might easily get stuck wrongly.
+ // Error - path was obstructed
+ wasObstructed = true;
+ break;
+ }
+ }
- // check if we've arrived.
- if (ShouldConsiderOurselvesAtDestination(pos))
- return;
+ if (!m_StartedMoving && wasObstructed)
+ // If this is the turn we start moving, and we're already obstructed,
+ // fail the move entirely to avoid weirdness.
+ // (we would need to send a "move started" and a "move failed" message in the same turn)
+ pos = initialPos;
- // If we still have long waypoints, try and compute a short path
- // This will get us around units, amongst others.
- // However in some cases a long waypoint will be in located in the obstruction of
- // an idle unit. In that case, we need to scrap that waypoint or we might never be able to reach it.
- // I am not sure why this happens but the following code seems to work.
- if (!m_LongPath.m_Waypoints.empty())
- {
- CmpPtr cmpObstructionManager(GetSystemEntity());
- if (cmpObstructionManager)
- {
- // create a fake obstruction to represent our waypoint.
- ICmpObstructionManager::ObstructionSquare square;
- square.hh = m_Clearance;
- square.hw = m_Clearance;
- square.u = CFixedVector2D(entity_pos_t::FromInt(1),entity_pos_t::FromInt(0));
- square.v = CFixedVector2D(entity_pos_t::FromInt(0),entity_pos_t::FromInt(1));
- square.x = m_LongPath.m_Waypoints.back().x;
- square.z = m_LongPath.m_Waypoints.back().z;
- std::vector unitOnGoal;
- // don't ignore moving units as those might be units like us, ie not really moving.
- cmpObstructionManager->GetUnitsOnObstruction(square, unitOnGoal, GetObstructionFilter(), true);
- if (!unitOnGoal.empty())
- m_LongPath.m_Waypoints.pop_back();
- }
- if (!m_LongPath.m_Waypoints.empty())
- {
- PathGoal goal;
- if (m_LongPath.m_Waypoints.size() > 1 || m_FinalGoal.DistanceToPoint(pos) > LONG_PATH_MIN_DIST)
- goal = { PathGoal::POINT, m_LongPath.m_Waypoints.back().x, m_LongPath.m_Waypoints.back().z };
- else
- {
- UpdateFinalGoal();
- goal = m_FinalGoal;
- m_LongPath.m_Waypoints.clear();
- CFixedVector2D target = goal.NearestPointOnGoal(pos);
- m_LongPath.m_Waypoints.emplace_back(Waypoint{ target.X, target.Y });
- }
- RequestShortPath(pos, goal, true);
- m_PathState = PATHSTATE_WAITING_REQUESTING_SHORT;
- return;
- }
- }
- // Else, just entirely recompute
- UpdateFinalGoal();
- BeginPathing(pos, m_FinalGoal);
+ // Update the Position component after our movement (if we actually moved anywhere)
+ if (pos != initialPos)
+ {
+ CFixedVector2D offset = pos - initialPos;
+
+ // Face towards the target
+ entity_angle_t angle = atan2_approx(offset.X, offset.Y);
+ cmpPosition->MoveAndTurnTo(pos.X,pos.Y, angle);
- // potential TODO: We could switch the short-range pathfinder for something else entirely.
+ // Calculate the mean speed over this past turn.
+ // TODO: this is often just a little different from our actual top speed
+ // so we end up changing the actual speed quite often, which is a little silly.
+ SetActualSpeed(cmpPosition->GetDistanceTravelled() / dt);
+
+ // tell other components and visual actor we are moving.
+ if (!m_StartedMoving)
+ MoveStarted();
+
+ if (!wasObstructed)
+ {
+ // everything is going smoothly, return.
+ m_Tries = 0;
+ m_WaitingTurns = 0;
return;
}
+ }
+ else
+ SetActualSpeed(fixed::Zero());
+
+
+ // tell relevant components we have paused if necessary
+ if (m_StartedMoving)
+ MovePaused();
- // We successfully moved along our path, until running out of
- // waypoints or time.
+ // Oops, we've had a problem. Either we were obstructed, or we ran out of path (but still have a goal).
+ // Handle it.
+ // Failure to handle it will result in stuckness and players complaining.
- if (m_PathState == PATHSTATE_FOLLOWING)
+ if (m_ExpectedPathTicket != 0)
+ // wait until we get our path to see where that leads us.
+ return;
+
+ // if our next waypoint is close enough to our goal and our goal isn't a point, drop our path and recompute directly.
+ if (m_FinalGoal.IsNotAPoint() && !m_Path.m_Waypoints.empty())
+ {
+
+ CmpPtr cmpObstructionManager(GetSystemEntity());
+ if (cmpObstructionManager)
{
- // If we're not currently computing any new paths:
- if (m_LongPath.m_Waypoints.empty() && m_ShortPath.m_Waypoints.empty())
+ bool inRange = false;
+ if (m_FinalGoal.TargetIsEntity())
+ inRange = cmpObstructionManager->IsInTargetRange(m_Path.m_Waypoints.back().x, m_Path.m_Waypoints.back().z,
+ m_FinalGoal.GetEntity(), m_FinalGoal.MinRange(), m_FinalGoal.MaxRange());
+ else
+ inRange = cmpObstructionManager->IsInPointRange(m_Path.m_Waypoints.back().x, m_Path.m_Waypoints.back().z,
+ m_FinalGoal.X(), m_FinalGoal.Z(), m_FinalGoal.MinRange(), m_FinalGoal.MaxRange());
+ if (inRange)
{
- if (IsFormationMember())
- {
- // We've reached our assigned position. If the controller
- // is idle, send a notification in case it should disband,
- // otherwise continue following the formation next turn.
- CmpPtr cmpUnitMotion(GetSimContext(), m_TargetEntity);
- if (cmpUnitMotion && !cmpUnitMotion->IsMoving())
- {
- CmpPtr cmpObstruction(GetEntityHandle());
- if (cmpObstruction)
- cmpObstruction->SetMovingFlag(false);
-
- m_Moving = false;
- CMessageMotionChanged msg(false, false);
- GetSimContext().GetComponentManager().PostMessage(GetEntityId(), msg);
- }
- }
- else
- {
- // check if target was reached in case of a moving target
- CmpPtr cmpUnitMotion(GetSimContext(), m_TargetEntity);
- if (cmpUnitMotion && cmpUnitMotion->IsMoving() &&
- MoveToTargetRange(m_TargetEntity, m_TargetMinRange, m_TargetMaxRange))
- return;
-
- // Not in formation, so just finish moving
- StopMoving();
- m_State = STATE_IDLE;
- MoveSucceeded();
-
- if (m_FacePointAfterMove)
- FaceTowardsPointFromPos(pos, m_FinalGoal.x, m_FinalGoal.z);
- // TODO: if the goal was a square building, we ought to point towards the
- // nearest point on the square, not towards its center
- }
+ m_Path.m_Waypoints.clear();
+ m_WaitingTurns = MAX_PATH_REATTEMPS; // short path
}
-
- // If we have a target entity, and we're not miles away from the end of
- // our current path, and the target moved enough, then recompute our
- // whole path
- if (IsFormationMember())
- CheckTargetMovement(pos, CHECK_TARGET_MOVEMENT_MIN_DELTA_FORMATION);
- else
- CheckTargetMovement(pos, CHECK_TARGET_MOVEMENT_MIN_DELTA);
}
}
+
+ // give us some turns to recover.
+ // TODO: only do this if we ran into a moving unit and not something else, because something else won't move
+ // specifically: if we ran into a moving unit, we should wait a turn and see what happens
+ // if we ran into a static unit, recompute a short-path directly
+ // if we ran into a static obstruction, recompute long-path directly
+ // And then maybe we could add some finetuning based on target.
+ if (m_WaitingTurns == 0)
+ {
+ if (HasValidPath())
+ m_WaitingTurns = MAX_PATH_REATTEMPS;
+ else
+ m_WaitingTurns = 3;
}
-}
-bool CCmpUnitMotion::ComputeTargetPosition(CFixedVector2D& out)
-{
- if (m_TargetEntity == INVALID_ENTITY)
- return false;
+ --m_WaitingTurns;
- CmpPtr cmpPosition(GetSimContext(), m_TargetEntity);
- if (!cmpPosition || !cmpPosition->IsInWorld())
- return false;
+ // Try again next turn, no changes
+ if (m_WaitingTurns >= MAX_PATH_REATTEMPS)
+ return;
- if (m_TargetOffset.IsZero())
+ // already waited one turn, no changes, so try computing a short path.
+ if (m_WaitingTurns >= 3)
{
- // No offset, just return the position directly
- out = cmpPosition->GetPosition2D();
+ PathGoal goal;
+ if (m_Path.m_Waypoints.empty())
+ goal = { PathGoal::POINT, m_FinalGoal.X(), m_FinalGoal.Z() };
+ else
+ {
+ goal = { PathGoal::POINT, m_Path.m_Waypoints.back().x, m_Path.m_Waypoints.back().z };
+ m_Path.m_Waypoints.pop_back();
+ }
+ RequestShortPath(pos, goal, true);
+ return;
}
- else
+
+ // Last resort, compute a long path
+ if (m_WaitingTurns == 2)
{
- // There is an offset, so compute it relative to orientation
- entity_angle_t angle = cmpPosition->GetRotation().Y;
- CFixedVector2D offset = m_TargetOffset.Rotate(angle);
- out = cmpPosition->GetPosition2D() + offset;
+ PathGoal goal;
+ if (m_Path.m_Waypoints.empty())
+ goal = { PathGoal::POINT, m_FinalGoal.X(), m_FinalGoal.Z() };
+ else
+ {
+ goal = { PathGoal::POINT, m_Path.m_Waypoints.back().x, m_Path.m_Waypoints.back().z };
+ m_Path.m_Waypoints.pop_back();
+ }
+ RequestLongPath(pos, goal);
+ return;
}
- return true;
-}
-bool CCmpUnitMotion::TryGoingStraightToGoalPoint(const CFixedVector2D& from)
-{
- // Make sure the goal is a point (and not a point-like target like a formation controller)
- if (m_FinalGoal.type != PathGoal::POINT || m_TargetEntity != INVALID_ENTITY)
- return false;
- // Fail if the goal is too far away
- CFixedVector2D goalPos(m_FinalGoal.x, m_FinalGoal.z);
- if ((goalPos - from).CompareLength(DIRECT_PATH_RANGE) > 0)
- return false;
+ // m_waitingTurns == 1 here
- CmpPtr cmpPathfinder(GetSystemEntity());
- if (!cmpPathfinder)
- return false;
+ // we tried getting a renewed path and still got stuck
+ if (m_AbortIfStuck == 0)
+ {
+ MoveFailed();
+ return;
+ }
- // Check if there's any collisions on that route
- if (!cmpPathfinder->CheckMovement(GetObstructionFilter(), from.X, from.Y, goalPos.X, goalPos.Y, m_Clearance, m_PassClass))
- return false;
+ --m_AbortIfStuck;
- // That route is okay, so update our path
- m_LongPath.m_Waypoints.clear();
- m_ShortPath.m_Waypoints.clear();
- m_ShortPath.m_Waypoints.emplace_back(Waypoint{ goalPos.X, goalPos.Y });
+ // Recompute a new path, but wait a few turns first
+ m_WaitingTurns = 4 + MAX_PATH_REATTEMPS;
- return true;
+ return;
}
-bool CCmpUnitMotion::TryGoingStraightToTargetEntity(const CFixedVector2D& from)
+// TODO: this should care about target movement
+bool CCmpUnitMotion::CheckTargetMovement(const CFixedVector2D& from, entity_pos_t minDelta)
{
- CFixedVector2D targetPos;
- if (!ComputeTargetPosition(targetPos))
+ if (!m_FinalGoal.TargetIsEntity())
return false;
- // Fail if the target is too far away
- if ((targetPos - from).CompareLength(DIRECT_PATH_RANGE) > 0)
- return false;
-
- CmpPtr cmpPathfinder(GetSystemEntity());
- if (!cmpPathfinder)
- return false;
-
- // Move the goal to match the target entity's new position
- PathGoal goal = m_FinalGoal;
- goal.x = targetPos.X;
- goal.z = targetPos.Y;
- // (we ignore changes to the target's rotation, since only buildings are
- // square and buildings don't move)
-
- // Find the point on the goal shape that we should head towards
- CFixedVector2D goalPos = goal.NearestPointOnGoal(from);
+ if (!HasValidPath())
+ return true;
- // Check if there's any collisions on that route
- if (!cmpPathfinder->CheckMovement(GetObstructionFilter(true), from.X, from.Y, goalPos.X, goalPos.Y, m_Clearance, m_PassClass))
- return false;
-
- // That route is okay, so update our path
- m_FinalGoal = goal;
- m_LongPath.m_Waypoints.clear();
- m_ShortPath.m_Waypoints.clear();
- m_ShortPath.m_Waypoints.emplace_back(Waypoint{ goalPos.X, goalPos.Y });
-
- return true;
-}
+ // Fail unless the target has moved enough
+ CFixedVector2D oldTargetPos = CFixedVector2D(m_Path.m_Waypoints[0].x,m_Path.m_Waypoints[0].z);
-bool CCmpUnitMotion::CheckTargetMovement(const CFixedVector2D& from, entity_pos_t minDelta)
-{
- CFixedVector2D targetPos;
- if (!ComputeTargetPosition(targetPos))
+ if ((m_FinalGoal.Pos() - oldTargetPos).CompareLength(minDelta) < 0)
return false;
- // Fail unless the target has moved enough
- CFixedVector2D oldTargetPos(m_FinalGoal.x, m_FinalGoal.z);
- if ((targetPos - oldTargetPos).CompareLength(minDelta) < 0)
- return false;
CmpPtr cmpPosition(GetEntityHandle());
if (!cmpPosition || !cmpPosition->IsInWorld())
return false;
CFixedVector2D pos = cmpPosition->GetPosition2D();
CFixedVector2D oldDir = (oldTargetPos - pos);
- CFixedVector2D newDir = (targetPos - pos);
+ CFixedVector2D newDir = (m_FinalGoal.Pos() - pos);
oldDir.Normalize();
newDir.Normalize();
// Fail unless we're close enough to the target to care about its movement
// and the angle between the (straight-line) directions of the previous and new target positions is small
- if (oldDir.Dot(newDir) > CHECK_TARGET_MOVEMENT_MIN_COS && !PathIsShort(m_LongPath, from, CHECK_TARGET_MOVEMENT_AT_MAX_DIST))
+ if (oldDir.Dot(newDir) > CHECK_TARGET_MOVEMENT_MIN_COS && !PathIsShort(m_Path, from, CHECK_TARGET_MOVEMENT_AT_MAX_DIST))
return false;
// Fail if the target is no longer visible to this entity's owner
@@ -1183,47 +973,26 @@
if (cmpOwnership)
{
CmpPtr cmpRangeManager(GetSystemEntity());
- if (cmpRangeManager && cmpRangeManager->GetLosVisibility(m_TargetEntity, cmpOwnership->GetOwner()) == ICmpRangeManager::VIS_HIDDEN)
+ if (cmpRangeManager && cmpRangeManager->GetLosVisibility(m_FinalGoal.GetEntity(), cmpOwnership->GetOwner()) == ICmpRangeManager::VIS_HIDDEN)
return false;
}
// The target moved and we need to update our current path;
- // change the goal here and expect our caller to start the path request
- m_FinalGoal.x = targetPos.X;
- m_FinalGoal.z = targetPos.Y;
- RequestLongPath(from, m_FinalGoal);
- m_PathState = PATHSTATE_FOLLOWING_REQUESTING_LONG;
+ // Expect our caller to recompute
+ // Dump our current path.
+ m_Path.m_Waypoints.clear();
return true;
}
-void CCmpUnitMotion::UpdateFinalGoal()
-{
- if (m_TargetEntity == INVALID_ENTITY)
- return;
- CmpPtr cmpUnitMotion(GetSimContext(), m_TargetEntity);
- if (!cmpUnitMotion)
- return;
- if (IsFormationMember())
- return;
- CFixedVector2D targetPos;
- if (!ComputeTargetPosition(targetPos))
- return;
- m_FinalGoal.x = targetPos.X;
- m_FinalGoal.z = targetPos.Y;
-}
-
-bool CCmpUnitMotion::ShouldConsiderOurselvesAtDestination(const CFixedVector2D& from)
+// TODO: ought to be cleverer here.
+// In particular maybe we should support some "margin" for error.
+bool CCmpUnitMotion::ShouldConsiderOurselvesAtDestination()
{
- if (m_TargetEntity != INVALID_ENTITY || m_FinalGoal.DistanceToPoint(from) > SHORT_PATH_GOAL_RADIUS)
- return false;
-
- StopMoving();
- MoveSucceeded();
-
- if (m_FacePointAfterMove)
- FaceTowardsPointFromPos(from, m_FinalGoal.x, m_FinalGoal.z);
- return true;
+ if (m_FinalGoal.TargetIsEntity())
+ return IsInTargetRange(m_FinalGoal.GetEntity(), m_FinalGoal.MinRange(), m_FinalGoal.MaxRange());
+ else
+ return IsInPointRange(m_FinalGoal.X(),m_FinalGoal.Z(), m_FinalGoal.MinRange(), m_FinalGoal.MaxRange());
}
bool CCmpUnitMotion::PathIsShort(const WaypointPath& path, const CFixedVector2D& from, entity_pos_t minDistance) const
@@ -1275,79 +1044,49 @@
ControlGroupMovementObstructionFilter CCmpUnitMotion::GetObstructionFilter(bool noTarget) const
{
- entity_id_t group = noTarget ? m_TargetEntity : GetGroup();
- return ControlGroupMovementObstructionFilter(ShouldAvoidMovingUnits(), group);
+ entity_id_t group = noTarget ? m_FinalGoal.GetEntity() : GetGroup();
+ // TODO: if we sometimes want to consider moving units, change here.
+ return ControlGroupMovementObstructionFilter(false, group);
}
-
-
-void CCmpUnitMotion::BeginPathing(const CFixedVector2D& from, const PathGoal& goal)
+// TODO: this should be improved, it's a little limited
+// EG use of hierarchical pathfinder,…
+// also it should probably make the goal passable directly, to avoid conflict with the paths returned.
+void CCmpUnitMotion::RequestNewPath()
{
- // reset our state for sanity.
- m_ExpectedPathTicket = 0;
+ ENSURE(m_ExpectedPathTicket == 0);
- CmpPtr cmpObstruction(GetEntityHandle());
- if (cmpObstruction)
- cmpObstruction->SetMovingFlag(false);
+ CmpPtr cmpPosition(GetEntityHandle());
+ if (!cmpPosition)
+ return;
- m_Moving = false;
+ // dump current path
+ m_Path.m_Waypoints.clear();
- m_PathState = PATHSTATE_NONE;
+ CFixedVector2D position = cmpPosition->GetPosition2D();
#if DISABLE_PATHFINDER
{
CmpPtr cmpPathfinder (GetSimContext(), SYSTEM_ENTITY);
- CFixedVector2D goalPos = m_FinalGoal.NearestPointOnGoal(from);
+ CFixedVector2D goalPos = m_FinalGoal.Goal().NearestPointOnGoal(position);
m_LongPath.m_Waypoints.clear();
m_ShortPath.m_Waypoints.clear();
m_ShortPath.m_Waypoints.emplace_back(Waypoint{ goalPos.X, goalPos.Y });
- m_PathState = PATHSTATE_FOLLOWING;
return;
}
#endif
- // If we're aiming at a target entity and it's close and we can reach
- // it in a straight line, then we'll just go along the straight line
- // instead of computing a path.
- if (TryGoingStraightToTargetEntity(from))
- {
- if (!HasValidPath())
- StartSucceeded();
- m_PathState = PATHSTATE_FOLLOWING;
- return;
- }
-
- // Same thing applies to non-entity points
- if (TryGoingStraightToGoalPoint(from))
- {
- if (!HasValidPath())
- StartSucceeded();
- m_PathState = PATHSTATE_FOLLOWING;
- return;
- }
-
- // Otherwise we need to compute a path.
-
// If it's close then just do a short path, not a long path
// TODO: If it's close on the opposite side of a river then we really
// need a long path, so we shouldn't simply check linear distance
// the check is arbitrary but should be a reasonably small distance.
- if (goal.DistanceToPoint(from) < LONG_PATH_MIN_DIST)
- {
- // add our final goal as a long range waypoint so we don't forget
- // where we are going if the short-range pathfinder returns
- // an aborted path.
- m_LongPath.m_Waypoints.clear();
- CFixedVector2D target = m_FinalGoal.NearestPointOnGoal(from);
- m_LongPath.m_Waypoints.emplace_back(Waypoint{ target.X, target.Y });
- m_PathState = PATHSTATE_WAITING_REQUESTING_SHORT;
- RequestShortPath(from, goal, true);
- }
+ // Maybe use PathIsShort?
+
+ // TODO: note by wraitii: figure out if the above comment is still true. It seems false.
+ if (m_FinalGoal.Goal().DistanceToPoint(position) < LONG_PATH_MIN_DIST)
+ RequestShortPath(position, m_FinalGoal.Goal(), true);
else
- {
- m_PathState = PATHSTATE_WAITING_REQUESTING_LONG;
- RequestLongPath(from, goal);
- }
+ RequestLongPath(position, m_FinalGoal.Goal());
}
void CCmpUnitMotion::RequestLongPath(const CFixedVector2D& from, const PathGoal& goal)
@@ -1373,7 +1112,7 @@
return;
// wrapping around on m_Tries isn't really a problem so don't check for overflow.
- fixed searchRange = std::max(SHORT_PATH_MIN_SEARCH_RANGE * ++m_Tries, goal.DistanceToPoint(from));
+ fixed searchRange = std::max(SHORT_PATH_MIN_SEARCH_RANGE * (++m_Tries + 1), goal.DistanceToPoint(from));
if (goal.type != PathGoal::POINT && searchRange < goal.hw && searchRange < SHORT_PATH_MIN_SEARCH_RANGE * 2)
searchRange = std::min(goal.hw, SHORT_PATH_MIN_SEARCH_RANGE * 2);
if (searchRange > SHORT_PATH_MAX_SEARCH_RANGE)
@@ -1389,8 +1128,11 @@
bool CCmpUnitMotion::MoveToPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t minRange, entity_pos_t maxRange, entity_id_t target)
{
+ // Must closely mirror CmpObstructionManager::IsInPointRange
PROFILE("MoveToPointRange");
+ DiscardMove();
+
CmpPtr cmpPosition(GetEntityHandle());
if (!cmpPosition || !cmpPosition->IsInWorld())
return false;
@@ -1442,76 +1184,23 @@
}
}
- m_State = STATE_INDIVIDUAL_PATH;
- m_TargetEntity = target;
- m_TargetOffset = CFixedVector2D();
- m_TargetMinRange = minRange;
- m_TargetMaxRange = maxRange;
- m_FinalGoal = goal;
- m_Tries = 0;
-
- BeginPathing(pos, goal);
-
- return true;
-}
-
-bool CCmpUnitMotion::IsInPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t minRange, entity_pos_t maxRange)
-{
- CmpPtr cmpPosition(GetEntityHandle());
- if (!cmpPosition || !cmpPosition->IsInWorld())
- return false;
-
- CFixedVector2D pos = cmpPosition->GetPosition2D();
-
- bool hasObstruction = false;
- CmpPtr cmpObstructionManager(GetSystemEntity());
- ICmpObstructionManager::ObstructionSquare obstruction;
-//TODO if (cmpObstructionManager)
-// hasObstruction = cmpObstructionManager->FindMostImportantObstruction(GetObstructionFilter(), x, z, m_Radius, obstruction);
-
- if (minRange.IsZero() && maxRange.IsZero() && hasObstruction)
- {
- // Handle the non-ranged mode:
- CFixedVector2D halfSize(obstruction.hw, obstruction.hh);
- entity_pos_t distance = Geometry::DistanceToSquare(pos - CFixedVector2D(obstruction.x, obstruction.z), obstruction.u, obstruction.v, halfSize);
-
- // See if we're too close to the target square
- if (distance < minRange)
- return false;
-
- // See if we're close enough to the target square
- if (maxRange < entity_pos_t::Zero() || distance <= maxRange)
- return true;
-
- return false;
- }
+ if (target == INVALID_ENTITY)
+ m_FinalGoal = SMotionGoal(goal, minRange, maxRange);
else
- {
- entity_pos_t distance = (pos - CFixedVector2D(x, z)).Length();
+ m_FinalGoal = SMotionGoal(GetSimContext(), target, goal, minRange, maxRange);
- if (distance < minRange)
- return false;
- else if (maxRange >= entity_pos_t::Zero() && distance > maxRange)
- return false;
- else
- return true;
- }
-}
+ RequestNewPath();
-bool CCmpUnitMotion::ShouldTreatTargetAsCircle(entity_pos_t range, entity_pos_t circleRadius) const
-{
- // Given a square, plus a target range we should reach, the shape at that distance
- // is a round-cornered square which we can approximate as either a circle or as a square.
- // Previously, we used the shape that minimized the worst-case error.
- // However that is unsage in some situations. So let's be less clever and
- // just check if our range is at least three times bigger than the circleradius
- return (range > circleRadius*3);
+ return true;
}
bool CCmpUnitMotion::MoveToTargetRange(entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange)
{
+ // Must closely mirror CmpObstructionManager::IsInTargetRange
PROFILE("MoveToTargetRange");
+ DiscardMove();
+
CmpPtr cmpPosition(GetEntityHandle());
if (!cmpPosition || !cmpPosition->IsInWorld())
return false;
@@ -1588,7 +1277,7 @@
entity_pos_t goalDistance = minRange + Pathfinding::GOAL_DELTA;
- if (ShouldTreatTargetAsCircle(minRange, circleRadius))
+ if (Geometry::ShouldTreatTargetAsCircle(minRange, circleRadius))
{
// The target is small relative to our range, so pretend it's a circle
goal.type = PathGoal::INVERTED_CIRCLE;
@@ -1616,7 +1305,7 @@
// Circumscribe the square
entity_pos_t circleRadius = halfSize.Length();
- if (ShouldTreatTargetAsCircle(maxRange, circleRadius))
+ if (Geometry::ShouldTreatTargetAsCircle(maxRange, circleRadius))
{
// The target is small relative to our range, so pretend it's a circle
@@ -1655,117 +1344,47 @@
}
}
- m_State = STATE_INDIVIDUAL_PATH;
- m_TargetEntity = target;
- m_TargetOffset = CFixedVector2D();
- m_TargetMinRange = minRange;
- m_TargetMaxRange = maxRange;
- m_FinalGoal = goal;
- m_Tries = 0;
+ if (target == INVALID_ENTITY)
+ m_FinalGoal = SMotionGoal(goal, minRange, maxRange);
+ else
+ m_FinalGoal = SMotionGoal(GetSimContext(), target, goal, minRange, maxRange);
- BeginPathing(pos, goal);
+ RequestNewPath();
return true;
}
-bool CCmpUnitMotion::IsInTargetRange(entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange)
+bool CCmpUnitMotion::IsInPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t minRange, entity_pos_t maxRange)
{
- // This function closely mirrors MoveToTargetRange - it needs to return true
- // after that Move has completed
-
CmpPtr cmpPosition(GetEntityHandle());
- if (!cmpPosition || !cmpPosition->IsInWorld())
+ if (!cmpPosition)
return false;
- CFixedVector2D pos = cmpPosition->GetPosition2D();
-
CmpPtr cmpObstructionManager(GetSystemEntity());
if (!cmpObstructionManager)
- return false;
+ return true; // what's a sane default here?
- bool hasObstruction = false;
- ICmpObstructionManager::ObstructionSquare obstruction;
- CmpPtr cmpObstruction(GetSimContext(), target);
- if (cmpObstruction)
- hasObstruction = cmpObstruction->GetObstructionSquare(obstruction);
-
- if (hasObstruction)
- {
- CFixedVector2D halfSize(obstruction.hw, obstruction.hh);
- entity_pos_t distance = Geometry::DistanceToSquare(pos - CFixedVector2D(obstruction.x, obstruction.z), obstruction.u, obstruction.v, halfSize, true);
-
- // Compare with previous obstruction
- ICmpObstructionManager::ObstructionSquare previousObstruction;
- cmpObstruction->GetPreviousObstructionSquare(previousObstruction);
- entity_pos_t previousDistance = Geometry::DistanceToSquare(pos - CFixedVector2D(previousObstruction.x, previousObstruction.z), obstruction.u, obstruction.v, halfSize, true);
-
- // See if we're too close to the target square
- bool inside = distance.IsZero() && !Geometry::DistanceToSquare(pos - CFixedVector2D(obstruction.x, obstruction.z), obstruction.u, obstruction.v, halfSize).IsZero();
- if ((distance < minRange && previousDistance < minRange) || inside)
- return false;
-
- // See if we're close enough to the target square
- if (maxRange < entity_pos_t::Zero() || distance <= maxRange || previousDistance <= maxRange)
- return true;
-
- entity_pos_t circleRadius = halfSize.Length();
-
- if (ShouldTreatTargetAsCircle(maxRange, circleRadius))
- {
- // The target is small relative to our range, so pretend it's a circle
- // and see if we're close enough to that.
- // Also check circle around previous position.
- entity_pos_t circleDistance = (pos - CFixedVector2D(obstruction.x, obstruction.z)).Length() - circleRadius;
- entity_pos_t previousCircleDistance = (pos - CFixedVector2D(previousObstruction.x, previousObstruction.z)).Length() - circleRadius;
-
- return circleDistance <= maxRange || previousCircleDistance <= maxRange;
- }
-
- // take minimal clearance required in MoveToTargetRange into account, multiplying by 3/2 for diagonals
- entity_pos_t maxDist = std::max(maxRange, (m_Clearance + entity_pos_t::FromInt(TERRAIN_TILE_SIZE)/16)*3/2);
- return distance <= maxDist || previousDistance <= maxDist;
- }
- else
- {
- CmpPtr cmpTargetPosition(GetSimContext(), target);
- if (!cmpTargetPosition || !cmpTargetPosition->IsInWorld())
- return false;
-
- CFixedVector2D targetPos = cmpTargetPosition->GetPreviousPosition2D();
- entity_pos_t distance = (pos - targetPos).Length();
+ CFixedVector2D pos = cmpPosition->GetPosition2D();
- return minRange <= distance && (maxRange < entity_pos_t::Zero() || distance <= maxRange);
- }
+ return cmpObstructionManager->IsInPointRange(pos.X, pos.Y, x, z, minRange, maxRange + m_Clearance.Multiply(fixed::FromInt(3)/2));
}
-void CCmpUnitMotion::MoveToFormationOffset(entity_id_t target, entity_pos_t x, entity_pos_t z)
+bool CCmpUnitMotion::IsInTargetRange(entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange)
{
- CmpPtr cmpPosition(GetSimContext(), target);
- if (!cmpPosition || !cmpPosition->IsInWorld())
- return;
+ CmpPtr cmpPosition(GetEntityHandle());
+ if (!cmpPosition)
+ return false;
- CFixedVector2D pos = cmpPosition->GetPosition2D();
+ CmpPtr cmpObstructionManager(GetSystemEntity());
+ if (!cmpObstructionManager)
+ return true; // what's a sane default here?
- PathGoal goal;
- goal.type = PathGoal::POINT;
- goal.x = pos.X;
- goal.z = pos.Y;
-
- m_State = STATE_FORMATIONMEMBER_PATH;
- m_TargetEntity = target;
- m_TargetOffset = CFixedVector2D(x, z);
- m_TargetMinRange = entity_pos_t::Zero();
- m_TargetMaxRange = entity_pos_t::Zero();
- m_FinalGoal = goal;
- m_Tries = 0;
+ CFixedVector2D pos = cmpPosition->GetPosition2D();
- BeginPathing(pos, goal);
+ return cmpObstructionManager->IsInTargetRange(pos.X, pos.Y, target, minRange, maxRange + m_Clearance.Multiply(fixed::FromInt(3)/2));
}
-
-
-
void CCmpUnitMotion::RenderPath(const WaypointPath& path, std::vector& lines, CColor color)
{
bool floating = false;
@@ -1800,8 +1419,7 @@
if (!m_DebugOverlayEnabled)
return;
- RenderPath(m_LongPath, m_DebugOverlayLongPathLines, OVERLAY_COLOR_LONG_PATH);
- RenderPath(m_ShortPath, m_DebugOverlayShortPathLines, OVERLAY_COLOR_SHORT_PATH);
+ RenderPath(m_Path, m_DebugOverlayLongPathLines, OVERLAY_COLOR_LONG_PATH);
for (size_t i = 0; i < m_DebugOverlayLongPathLines.size(); ++i)
collector.Submit(&m_DebugOverlayLongPathLines[i]);
Index: source/simulation2/components/CCmpVisualActor.cpp
===================================================================
--- source/simulation2/components/CCmpVisualActor.cpp
+++ source/simulation2/components/CCmpVisualActor.cpp
@@ -54,7 +54,6 @@
public:
static void ClassInit(CComponentManager& componentManager)
{
- componentManager.SubscribeToMessageType(MT_Update_Final);
componentManager.SubscribeToMessageType(MT_InterpolatedPositionChanged);
componentManager.SubscribeToMessageType(MT_OwnershipChanged);
componentManager.SubscribeToMessageType(MT_ValueModification);
@@ -71,10 +70,7 @@
fixed m_R, m_G, m_B; // shading color
- std::map m_AnimOverride;
-
// Current animation state
- fixed m_AnimRunThreshold; // if non-zero this is the special walk/run mode
std::string m_AnimName;
bool m_AnimOnce;
fixed m_AnimSpeed;
@@ -83,6 +79,9 @@
fixed m_AnimSyncRepeatTime; // 0.0 if not synced
fixed m_AnimSyncOffsetTime;
+ std::string m_MovingPrefix;
+ fixed m_MovingSpeed;
+
std::map m_VariantSelections;
u32 m_Seed; // seed used for random variations
@@ -190,6 +189,7 @@
{
m_Unit = NULL;
m_R = m_G = m_B = fixed::FromInt(1);
+ m_MovingSpeed = fixed::FromInt(1);
m_ConstructionPreview = paramNode.GetChild("ConstructionPreview").IsOk();
@@ -225,9 +225,6 @@
serialize.NumberFixed_Unbounded("g", m_G);
serialize.NumberFixed_Unbounded("b", m_B);
- SerializeMap()(serialize, "anim overrides", m_AnimOverride);
-
- serialize.NumberFixed_Unbounded("anim run threshold", m_AnimRunThreshold);
serialize.StringASCII("anim name", m_AnimName, 0, 256);
serialize.Bool("anim once", m_AnimOnce);
serialize.NumberFixed_Unbounded("anim speed", m_AnimSpeed);
@@ -282,12 +279,6 @@
{
switch (msg.GetType())
{
- case MT_Update_Final:
- {
- const CMessageUpdate_Final& msgData = static_cast (msg);
- Update(msgData.turnLength);
- break;
- }
case MT_OwnershipChanged:
{
if (!m_Unit)
@@ -423,7 +414,6 @@
virtual void SelectAnimation(const std::string& name, bool once, fixed speed, const std::wstring& soundgroup)
{
- m_AnimRunThreshold = fixed::Zero();
m_AnimName = name;
m_AnimOnce = once;
m_AnimSpeed = speed;
@@ -432,28 +422,36 @@
m_AnimSyncRepeatTime = fixed::Zero();
m_AnimSyncOffsetTime = fixed::Zero();
- SetVariant("animation", m_AnimName);
+ // TODO: change this once we support walk/run-anims
+ std::string animName = name;
+ /*if (!m_MovingPrefix.empty() && m_AnimName != "idle")
+ animName = m_MovingPrefix + "-" + m_AnimName;
+ else */if (!m_MovingPrefix.empty())
+ animName = m_MovingPrefix;
- if (m_Unit && m_Unit->GetAnimation())
- m_Unit->GetAnimation()->SetAnimationState(m_AnimName, m_AnimOnce, m_AnimSpeed.ToFloat(), m_AnimDesync.ToFloat(), m_SoundGroup.c_str());
- }
+ SetVariant("animation", animName);
- virtual void ReplaceMoveAnimation(const std::string& name, const std::string& replace)
- {
- m_AnimOverride[name] = replace;
+ if (m_Unit && m_Unit->GetAnimation())
+ m_Unit->GetAnimation()->SetAnimationState(animName, m_AnimOnce, m_MovingSpeed.Multiply(m_AnimSpeed).ToFloat(), m_AnimDesync.ToFloat(), m_SoundGroup.c_str());
}
- virtual void ResetMoveAnimation(const std::string& name)
+ virtual void SetMovingSpeed(fixed movingSpeed)
{
- std::map::const_iterator it = m_AnimOverride.find(name);
- if (it != m_AnimOverride.end())
- m_AnimOverride.erase(name);
- }
+ // TODO: don't copy strings for fun.
+ std::string prefix;
+ if (movingSpeed.IsZero())
+ prefix = "";
+ else
+ {
+ CmpPtr cmpUnitMotion(GetEntityHandle());
+ if (!cmpUnitMotion)
+ return;
+ prefix = cmpUnitMotion->GetSpeedRatio() <= fixed::FromInt(1) ? "walk" : "run";
+ }
+ m_MovingPrefix = prefix;
+ m_MovingSpeed = movingSpeed.IsZero() ? fixed::FromInt(1) : movingSpeed;
- virtual void SelectMovementAnimation(fixed runThreshold)
- {
- SelectAnimation("walk", false, fixed::FromFloat(1.f), L"");
- m_AnimRunThreshold = runThreshold;
+ SelectAnimation(m_AnimName, m_AnimOnce, m_AnimSpeed, m_SoundGroup);
}
virtual void SetAnimationSyncRepeat(fixed repeattime)
@@ -534,8 +532,6 @@
// ReloadUnitAnimation is used for a minimal reloading upon deserialization, when the actor and seed are identical.
// It is also used by ReloadActor.
void ReloadUnitAnimation();
-
- void Update(fixed turnLength);
};
REGISTER_COMPONENT_TYPE(VisualActor)
@@ -741,45 +737,3 @@
m_Unit->GetAnimation()->SetAnimationSyncOffset(m_AnimSyncOffsetTime.ToFloat());
}
-void CCmpVisualActor::Update(fixed UNUSED(turnLength))
-{
- // This function is currently only used to update the animation if the speed in
- // CCmpUnitMotion changes. This also only happens in the "special movement mode"
- // triggered by SelectMovementAnimation.
-
- // TODO: This should become event based, in order to save performance and to make the code
- // far less hacky. We should also take into account the speed when the animation is different
- // from the "special movement mode" walking animation.
-
- // If we're not in the special movement mode, nothing to do.
- if (m_AnimRunThreshold.IsZero())
- return;
-
- CmpPtr cmpPosition(GetEntityHandle());
- if (!cmpPosition || !cmpPosition->IsInWorld())
- return;
-
- CmpPtr cmpUnitMotion(GetEntityHandle());
- if (!cmpUnitMotion)
- return;
-
- fixed speed = cmpUnitMotion->GetCurrentSpeed();
- std::string name;
-
- if (speed.IsZero())
- {
- speed = fixed::FromFloat(1.f);
- name = "idle";
- }
- else
- name = speed < m_AnimRunThreshold ? "walk" : "run";
-
- std::map::const_iterator it = m_AnimOverride.find(name);
- if (it != m_AnimOverride.end())
- name = it->second;
-
- // Selecting the animation is going to reset the anim run threshold, so save it
- fixed runThreshold = m_AnimRunThreshold;
- SelectAnimation(name, false, speed, L"");
- m_AnimRunThreshold = runThreshold;
-}
Index: source/simulation2/components/ICmpObstructionManager.h
===================================================================
--- source/simulation2/components/ICmpObstructionManager.h
+++ source/simulation2/components/ICmpObstructionManager.h
@@ -159,6 +159,16 @@
virtual void RemoveShape(tag_t tag) = 0;
/**
+ * Check if the given point is in range of the other point given those parameters
+ */
+ virtual bool IsInPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t px, entity_pos_t pz, entity_pos_t minRange, entity_pos_t maxRange) = 0;
+
+ /**
+ * Check if the given point is in range of the target given those parameters
+ */
+ virtual bool IsInTargetRange(entity_pos_t x, entity_pos_t z, entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange) = 0;
+
+ /**
* Collision test a flat-ended thick line against the current set of shapes.
* The line caps extend by @p r beyond the end points.
* Only intersections going from outside to inside a shape are counted.
Index: source/simulation2/components/ICmpUnitMotion.h
===================================================================
--- source/simulation2/components/ICmpUnitMotion.h
+++ source/simulation2/components/ICmpUnitMotion.h
@@ -47,14 +47,12 @@
virtual bool MoveToPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t minRange, entity_pos_t maxRange) = 0;
/**
- * Determine wether the givven point is within the given range, using the same measurement
- * as MoveToPointRange.
+ * Wrapper around ObstructionManager::IsInPointRange with unit position
*/
virtual bool IsInPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t minRange, entity_pos_t maxRange) = 0;
/**
- * Determine whether the target is within the given range, using the same measurement
- * as MoveToTargetRange.
+ * Wrapper around ObstructionManager::IsInTargetRange with unit position
*/
virtual bool IsInTargetRange(entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange) = 0;
@@ -71,45 +69,70 @@
virtual bool MoveToTargetRange(entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange) = 0;
/**
- * Join a formation, and move towards a given offset relative to the formation controller entity.
- * Continues following the formation until given a different command.
+ * Turn to look towards the given point.
*/
- virtual void MoveToFormationOffset(entity_id_t target, entity_pos_t x, entity_pos_t z) = 0;
+ virtual void FaceTowardsPoint(entity_pos_t x, entity_pos_t z) = 0;
/**
- * Turn to look towards the given point.
+ * Determine whether to abort or retry X times if pathing fails.
+ * Generally safer to let it abort and inform us.
*/
- virtual void FaceTowardsPoint(entity_pos_t x, entity_pos_t z) = 0;
+ virtual void SetAbortIfStuck(u8 shouldAbort) = 0;
+
+ /**
+ * Stop moving immediately, don't send messages.
+ * This should be used if you are going to ask for a new path,
+ * in the same function, for example.
+ * In doubt, UnitAI should probably call this.
+ * Use with caution.
+ */
+ virtual void DiscardMove() = 0;
/**
- * Stop moving immediately.
+ * Stop moving immediately, send messages.
+ * In doubt, components that are not UnitIA should probably call this.
*/
- virtual void StopMoving() = 0;
+ virtual void CompleteMove() = 0;
/**
- * Get the current movement speed.
+ * Get how much faster/slower we are at than normal.
*/
- virtual fixed GetCurrentSpeed() = 0;
+ virtual fixed GetSpeedRatio() = 0;
+
+ /**
+ * Get how much faster than our regular speed we can go.
+ */
+ virtual fixed GetTopSpeedRatio() = 0;
/**
* Set the current movement speed.
+ * 'speed' in % of top speed (ie 3.0 will be 3 times top speed).
*/
virtual void SetSpeed(fixed speed) = 0;
/**
- * Get whether the unit is moving.
+ * Get whether the unit is actually moving on the map this turn.
+ */
+ virtual bool IsActuallyMoving() = 0;
+
+ /**
+ * Get whether a unit is trying to go somewhere
+ * NB: this does not mean its position is actually changing right now.
*/
- virtual bool IsMoving() = 0;
+ virtual bool IsTryingToMove() = 0;
/**
- * Get the default speed that this unit will have when walking, in metres per second.
+ * Get the unit theoretical speed in metres per second.
+ * GetActualSpeed will return historical speed
+ * This is affected by SetSpeed.
*/
- virtual fixed GetWalkSpeed() = 0;
+ virtual fixed GetSpeed() = 0;
/**
- * Get the default speed that this unit will have when running, in metres per second.
+ * Get the unit base/walk speed in metres per second.
+ * This is NOT affected by SetSpeed.
*/
- virtual fixed GetRunSpeed() = 0;
+ virtual fixed GetBaseSpeed() = 0;
/**
* Set whether the unit will turn to face the target point after finishing moving.
Index: source/simulation2/components/ICmpUnitMotion.cpp
===================================================================
--- source/simulation2/components/ICmpUnitMotion.cpp
+++ source/simulation2/components/ICmpUnitMotion.cpp
@@ -27,14 +27,16 @@
DEFINE_INTERFACE_METHOD_4("IsInPointRange", bool, ICmpUnitMotion, IsInPointRange, entity_pos_t, entity_pos_t, entity_pos_t, entity_pos_t)
DEFINE_INTERFACE_METHOD_3("IsInTargetRange", bool, ICmpUnitMotion, IsInTargetRange, entity_id_t, entity_pos_t, entity_pos_t)
DEFINE_INTERFACE_METHOD_3("MoveToTargetRange", bool, ICmpUnitMotion, MoveToTargetRange, entity_id_t, entity_pos_t, entity_pos_t)
-DEFINE_INTERFACE_METHOD_3("MoveToFormationOffset", void, ICmpUnitMotion, MoveToFormationOffset, entity_id_t, entity_pos_t, entity_pos_t)
DEFINE_INTERFACE_METHOD_2("FaceTowardsPoint", void, ICmpUnitMotion, FaceTowardsPoint, entity_pos_t, entity_pos_t)
-DEFINE_INTERFACE_METHOD_0("StopMoving", void, ICmpUnitMotion, StopMoving)
-DEFINE_INTERFACE_METHOD_0("GetCurrentSpeed", fixed, ICmpUnitMotion, GetCurrentSpeed)
+DEFINE_INTERFACE_METHOD_1("SetAbortIfStuck", void, ICmpUnitMotion, SetAbortIfStuck, u8)
+DEFINE_INTERFACE_METHOD_0("DiscardMove", void, ICmpUnitMotion, DiscardMove)
+DEFINE_INTERFACE_METHOD_0("CompleteMove", void, ICmpUnitMotion, CompleteMove)
+DEFINE_INTERFACE_METHOD_0("GetTopSpeedRatio", fixed, ICmpUnitMotion, GetTopSpeedRatio)
DEFINE_INTERFACE_METHOD_1("SetSpeed", void, ICmpUnitMotion, SetSpeed, fixed)
-DEFINE_INTERFACE_METHOD_0("IsMoving", bool, ICmpUnitMotion, IsMoving)
-DEFINE_INTERFACE_METHOD_0("GetWalkSpeed", fixed, ICmpUnitMotion, GetWalkSpeed)
-DEFINE_INTERFACE_METHOD_0("GetRunSpeed", fixed, ICmpUnitMotion, GetRunSpeed)
+DEFINE_INTERFACE_METHOD_0("IsActuallyMoving", bool, ICmpUnitMotion, IsActuallyMoving)
+DEFINE_INTERFACE_METHOD_0("IsTryingToMove", bool, ICmpUnitMotion, IsTryingToMove)
+DEFINE_INTERFACE_METHOD_0("GetSpeed", fixed, ICmpUnitMotion, GetSpeed)
+DEFINE_INTERFACE_METHOD_0("GetBaseSpeed", fixed, ICmpUnitMotion, GetBaseSpeed)
DEFINE_INTERFACE_METHOD_0("GetPassabilityClassName", std::string, ICmpUnitMotion, GetPassabilityClassName)
DEFINE_INTERFACE_METHOD_0("GetUnitClearance", entity_pos_t, ICmpUnitMotion, GetUnitClearance)
DEFINE_INTERFACE_METHOD_1("SetFacePointAfterMove", void, ICmpUnitMotion, SetFacePointAfterMove, bool)
@@ -66,24 +68,29 @@
return m_Script.Call("MoveToTargetRange", target, minRange, maxRange);
}
- virtual void MoveToFormationOffset(entity_id_t target, entity_pos_t x, entity_pos_t z)
+ virtual void FaceTowardsPoint(entity_pos_t x, entity_pos_t z)
{
- m_Script.CallVoid("MoveToFormationOffset", target, x, z);
+ m_Script.CallVoid("FaceTowardsPoint", x, z);
}
- virtual void FaceTowardsPoint(entity_pos_t x, entity_pos_t z)
+ virtual void DiscardMove()
{
- m_Script.CallVoid("FaceTowardsPoint", x, z);
+ m_Script.CallVoid("DiscardMove");
}
- virtual void StopMoving()
+ virtual void CompleteMove()
{
- m_Script.CallVoid("StopMoving");
+ m_Script.CallVoid("CompleteMove");
}
- virtual fixed GetCurrentSpeed()
+ virtual void SetAbortIfStuck(u8 shouldAbort)
{
- return m_Script.Call("GetCurrentSpeed");
+ m_Script.CallVoid("SetAbortIfStuck", shouldAbort);
+ }
+
+ virtual fixed GetActualSpeed()
+ {
+ return m_Script.Call("GetActualSpeed");
}
virtual void SetSpeed(fixed speed)
@@ -91,19 +98,29 @@
m_Script.CallVoid("SetSpeed", speed);
}
- virtual bool IsMoving()
+ virtual fixed GetTopSpeedRatio()
+ {
+ return m_Script.Call("GetTopSpeedRatio");
+ }
+
+ virtual bool IsActuallyMoving()
+ {
+ return m_Script.Call("IsActuallyMoving");
+ }
+
+ virtual bool IsTryingToMove()
{
- return m_Script.Call("IsMoving");
+ return m_Script.Call("IsTryingToMove");
}
- virtual fixed GetWalkSpeed()
+ virtual fixed GetSpeed()
{
- return m_Script.Call("GetWalkSpeed");
+ return m_Script.Call("GetSpeed");
}
- virtual fixed GetRunSpeed()
+ virtual fixed GetBaseSpeed()
{
- return m_Script.Call("GetRunSpeed");
+ return m_Script.Call("GetBaseSpeed");
}
virtual void SetFacePointAfterMove(bool facePointAfterMove)
@@ -116,6 +133,11 @@
return m_Script.Call("GetPassabilityClass");
}
+ virtual fixed GetSpeedRatio()
+ {
+ return fixed::FromInt(1);
+ }
+
virtual std::string GetPassabilityClassName()
{
return m_Script.Call("GetPassabilityClassName");
Index: source/simulation2/components/ICmpVisual.h
===================================================================
--- source/simulation2/components/ICmpVisual.h
+++ source/simulation2/components/ICmpVisual.h
@@ -99,25 +99,10 @@
virtual void SelectAnimation(const std::string& name, bool once, fixed speed, const std::wstring& soundgroup) = 0;
/**
- * Replaces a specified animation with another. Only affects the special speed-based
- * animation determination behaviour.
- * @param name Animation to match.
- * @param replace Animation that should replace the matched animation.
+ * Tell the visual actor that the unit is currently moving at the given speed.
+ * If speed is 0, the unit will become idle.
*/
- virtual void ReplaceMoveAnimation(const std::string& name, const std::string& replace) = 0;
-
- /**
- * Ensures that the given animation will be used when it normally would be,
- * removing reference to any animation that might replace it.
- * @param name Animation name to remove from the replacement map.
- */
- virtual void ResetMoveAnimation(const std::string& name) = 0;
-
- /**
- * Start playing the walk/run animations, scaled to the unit's movement speed.
- * @param runThreshold movement speed at which to switch to the run animation
- */
- virtual void SelectMovementAnimation(fixed runThreshold) = 0;
+ virtual void SetMovingSpeed(fixed movingSpeed) = 0;
/**
* Adjust the speed of the current animation, so it can match simulation events.
Index: source/simulation2/components/ICmpVisual.cpp
===================================================================
--- source/simulation2/components/ICmpVisual.cpp
+++ source/simulation2/components/ICmpVisual.cpp
@@ -24,13 +24,11 @@
BEGIN_INTERFACE_WRAPPER(Visual)
DEFINE_INTERFACE_METHOD_2("SetVariant", void, ICmpVisual, SetVariant, CStr, CStr)
DEFINE_INTERFACE_METHOD_4("SelectAnimation", void, ICmpVisual, SelectAnimation, std::string, bool, fixed, std::wstring)
-DEFINE_INTERFACE_METHOD_1("SelectMovementAnimation", void, ICmpVisual, SelectMovementAnimation, fixed)
-DEFINE_INTERFACE_METHOD_1("ResetMoveAnimation", void, ICmpVisual, ResetMoveAnimation, std::string)
-DEFINE_INTERFACE_METHOD_2("ReplaceMoveAnimation", void, ICmpVisual, ReplaceMoveAnimation, std::string, std::string)
DEFINE_INTERFACE_METHOD_1("SetAnimationSyncRepeat", void, ICmpVisual, SetAnimationSyncRepeat, fixed)
DEFINE_INTERFACE_METHOD_1("SetAnimationSyncOffset", void, ICmpVisual, SetAnimationSyncOffset, fixed)
DEFINE_INTERFACE_METHOD_4("SetShadingColor", void, ICmpVisual, SetShadingColor, fixed, fixed, fixed, fixed)
DEFINE_INTERFACE_METHOD_2("SetVariable", void, ICmpVisual, SetVariable, std::string, float)
+DEFINE_INTERFACE_METHOD_1("SetMovingSpeed", void, ICmpVisual, SetMovingSpeed, fixed)
DEFINE_INTERFACE_METHOD_0("GetActorSeed", u32, ICmpVisual, GetActorSeed)
DEFINE_INTERFACE_METHOD_1("SetActorSeed", void, ICmpVisual, SetActorSeed, u32)
DEFINE_INTERFACE_METHOD_0("HasConstructionPreview", bool, ICmpVisual, HasConstructionPreview)
Index: source/simulation2/helpers/Geometry.h
===================================================================
--- source/simulation2/helpers/Geometry.h
+++ source/simulation2/helpers/Geometry.h
@@ -30,6 +30,21 @@
namespace Geometry
{
+/*
+ * Check if we should treat a square as a circle, given the radius
+ * of the resulting circle and a distance to it
+ * used by UnitMotion and ObstructionManager
+ */
+inline bool ShouldTreatTargetAsCircle(const fixed& range, const fixed& circleRadius)
+{
+ // Given a square, plus a target range we should reach, the shape at that distance
+ // is a round-cornered square which we can approximate as either a circle or as a square.
+ // Previously, we used the shape that minimized the worst-case error.
+ // However that is unsage in some situations. So let's be less clever and
+ // just check if our range is at least three times bigger than the circleradius
+ return (range > circleRadius*3);
+}
+
/**
* Checks if a point is inside the given rotated rectangle.
* Points precisely on an edge are considered to be inside.
Index: source/simulation2/scripting/MessageTypeConversions.cpp
===================================================================
--- source/simulation2/scripting/MessageTypeConversions.cpp
+++ source/simulation2/scripting/MessageTypeConversions.cpp
@@ -267,20 +267,46 @@
////////////////////////////////
-JS::Value CMessageMotionChanged::ToJSVal(ScriptInterface& scriptInterface) const
+JS::Value CMessageBeginMove::ToJSVal(ScriptInterface& scriptInterface) const
{
TOJSVAL_SETUP();
- SET_MSG_PROPERTY(starting);
- SET_MSG_PROPERTY(error);
return JS::ObjectValue(*obj);
}
-CMessage* CMessageMotionChanged::FromJSVal(ScriptInterface& scriptInterface, JS::HandleValue val)
+CMessage* CMessageBeginMove::FromJSVal(ScriptInterface& scriptInterface, JS::HandleValue val)
{
FROMJSVAL_SETUP();
- GET_MSG_PROPERTY(bool, starting);
- GET_MSG_PROPERTY(bool, error);
- return new CMessageMotionChanged(starting, error);
+ return new CMessageBeginMove();
+}
+
+////////////////////////////////
+
+JS::Value CMessagePausedMove::ToJSVal(ScriptInterface& scriptInterface) const
+{
+ TOJSVAL_SETUP();
+ return JS::ObjectValue(*obj);
+}
+
+CMessage* CMessagePausedMove::FromJSVal(ScriptInterface& scriptInterface, JS::HandleValue val)
+{
+ FROMJSVAL_SETUP();
+ return new CMessagePausedMove();
+}
+
+////////////////////////////////
+
+JS::Value CMessageFinishedMove::ToJSVal(ScriptInterface& scriptInterface) const
+{
+ TOJSVAL_SETUP();
+ SET_MSG_PROPERTY(failed);
+ return JS::ObjectValue(*obj);
+}
+
+CMessage* CMessageFinishedMove::FromJSVal(ScriptInterface& scriptInterface, JS::HandleValue val)
+{
+ FROMJSVAL_SETUP();
+ GET_MSG_PROPERTY(bool, failed);
+ return new CMessageFinishedMove(failed);
}
////////////////////////////////
Index: source/tools/atlas/GameInterface/ActorViewer.cpp
===================================================================
--- source/tools/atlas/GameInterface/ActorViewer.cpp
+++ source/tools/atlas/GameInterface/ActorViewer.cpp
@@ -375,7 +375,7 @@
{
CmpPtr cmpUnitMotion(m.Simulation2, m.Entity);
if (cmpUnitMotion)
- speed = cmpUnitMotion->GetWalkSpeed().ToFloat();
+ speed = cmpUnitMotion->GetBaseSpeed().ToFloat();
else
speed = 7.f; // typical unit speed
@@ -385,7 +385,7 @@
{
CmpPtr cmpUnitMotion(m.Simulation2, m.Entity);
if (cmpUnitMotion)
- speed = cmpUnitMotion->GetRunSpeed().ToFloat();
+ speed = cmpUnitMotion->GetBaseSpeed().ToFloat();
else
speed = 12.f; // typical unit speed