Index: binaries/data/mods/public/globalscripts/Templates.js
===================================================================
--- binaries/data/mods/public/globalscripts/Templates.js
+++ binaries/data/mods/public/globalscripts/Templates.js
@@ -309,8 +309,9 @@
ret.speed = {
"walk": getEntityValue("UnitMotion/WalkSpeed"),
};
- if (template.UnitMotion.Run)
- ret.speed.run = getEntityValue("UnitMotion/Run/Speed");
+ ret.speed.run = getEntityValue("UnitMotion/WalkSpeed");
+ if (template.UnitMotion.RunMultiplier)
+ ret.speed.run *= getEntityValue("UnitMotion/RunMultiplier");
}
if (template.ProductionQueue)
Index: binaries/data/mods/public/gui/session/input.js
===================================================================
--- binaries/data/mods/public/gui/session/input.js
+++ binaries/data/mods/public/gui/session/input.js
@@ -1517,12 +1517,11 @@
function performFormation(entities, formationTemplate)
{
- if (entities)
- Engine.PostNetworkCommand({
- "type": "formation",
- "entities": entities,
- "name": formationTemplate
- });
+ Engine.PostNetworkCommand({
+ "type": "formation",
+ "entities": entities,
+ "name": formationTemplate
+ });
}
function performGroup(action, groupId)
Index: binaries/data/mods/public/gui/session/selection_panels.js
===================================================================
--- binaries/data/mods/public/gui/session/selection_panels.js
+++ binaries/data/mods/public/gui/session/selection_panels.js
@@ -407,11 +407,8 @@
g_FormationsInfo.set(data.item, Engine.GuiInterfaceCall("GetFormationInfoFromTemplate", { "templateName": data.item }));
let formationInfo = g_FormationsInfo.get(data.item);
- let formationOk = canMoveSelectionIntoFormation(data.item);
- let formationSelected = Engine.GuiInterfaceCall("IsFormationSelected", {
- "ents": data.unitEntStates.map(state => state.id),
- "formationTemplate": data.item
- });
+ let formationOk = true;//TODO: reenable? canMoveSelectionIntoFormation(data.item);
+ let formationSelected = Engine.GuiInterfaceCall("IsFormationSelected", { "entities" : data.unitEntStates.map(state => state.id), "formationTemplate": data.item });
data.button.onPress = function() {
performFormation(data.unitEntStates.map(state => state.id), data.item);
Index: binaries/data/mods/public/gui/session/unit_actions.js
===================================================================
--- binaries/data/mods/public/gui/session/unit_actions.js
+++ binaries/data/mods/public/gui/session/unit_actions.js
@@ -947,10 +947,31 @@
return false;
}
+ // if the target has a static obstruction, move the rallypoint position closer to us
+ // keep this in sync with Rallypoint.js
+ let position = {};
+ let template = GetTemplateData(targetState.template);
+ if (template.obstruction && template.obstruction.shape && template.obstruction.shape.type == "static")
+ {
+ let size = Math.min(+template.obstruction.shape.width, +template.obstruction.shape.depth);
+ let vector = new Vector2D(targetState.position.x-entState.position.x,targetState.position.z-entState.position.z);
+ let pos = new Vector2D(targetState.position.x, targetState.position.z);
+ pos = pos.sub(vector.normalize().mult(size * 0.49));
+ position.x = pos.x;
+ position.z = pos.y;
+ position.y = targetState.position.y;
+ }
+ else
+ {
+ position.x = targetState.position.x;
+ position.z = targetState.position.z;
+ position.y = targetState.position.y;
+ }
+
return {
"possible": true,
"data": data,
- "position": targetState.position,
+ "position": position,
"cursor": cursor,
"tooltip": tooltip
};
Index: binaries/data/mods/public/maps/random/pathfinder_pertest.js
===================================================================
--- /dev/null
+++ binaries/data/mods/public/maps/random/pathfinder_pertest.js
@@ -0,0 +1,15 @@
+RMS.LoadLibrary("rmgen");
+
+// initialize map
+log("Initializing map...");
+InitMap();
+
+
+// General map setup
+let mapSize = getMapSize();
+let mapCenterX = mapSize/2;
+let mapCenterY = mapSize/2;
+
+// Nothing to create really, it'll all be done in triggers.
+
+ExportMap();
Index: binaries/data/mods/public/maps/random/pathfinder_pertest.json
===================================================================
--- /dev/null
+++ binaries/data/mods/public/maps/random/pathfinder_pertest.json
@@ -0,0 +1,15 @@
+{
+ "settings" : {
+ "Name" : "Pathfinder Perf test",
+ "Script" : "pathfinder_pertest.js",
+ "Description" : "Test the performance of the pathfinder in a bunch of situations",
+ "BaseTerrain" : ["desert_sand_dunes_100"],
+ "BaseHeight" : 1,
+ "Keywords" : ["demo"],
+ "CircularMap" : true,
+ "TriggerScripts": [
+ "scripts/TriggerHelper.js",
+ "random/pathfinder_pertest_triggers.js"
+ ]
+ }
+}
\ No newline at end of file
Index: binaries/data/mods/public/maps/random/pathfinder_pertest_triggers.js
===================================================================
--- /dev/null
+++ binaries/data/mods/public/maps/random/pathfinder_pertest_triggers.js
@@ -0,0 +1,134 @@
+warn("Setting up test");
+
+let cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
+cmpTrigger.DoAfterDelay(1000, "TestUnobstructedShortDistance", {});
+cmpTrigger.DoAfterDelay(10000, "KillAll", {});
+cmpTrigger.DoAfterDelay(11000, "TestShortRunIntoEachOther", {});
+cmpTrigger.DoAfterDelay(20000, "KillAll", {});
+cmpTrigger.DoAfterDelay(21000, "TestLongDiagonalpath", {});
+
+const TEST_TEMPLATE_REGULAR = "units/cart_support_female_citizen";
+const TEST_TEMPLATE_OBELISK = "other/obelisk";
+
+Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager).SetLosRevealAll(1, true);
+
+
+Trigger.prototype.KillAll = function()
+{
+ let ents = Engine.GetEntitiesWithInterface(IID_Position);
+ for (let ent of ents)
+ Engine.DestroyEntity(ent);
+}
+
+Trigger.prototype.TestUnobstructedShortDistance = function()
+{
+ warn("Starting TestUnobstructedShortDistance");
+ // here we will plop units and make them walk in a straight, unobstructed, short line.
+ // in principle the largest cost here is the unit move order
+ let cmpTerrain = Engine.QueryInterface(SYSTEM_ENTITY, IID_Terrain);
+ let mapSize = 4*cmpTerrain.GetTilesPerSide();
+
+ let mapCenter = new Vector2D(mapSize/2.0, mapSize/2.0);
+ let testSquareSize = mapSize / 5;
+
+ // create units
+ for (let z = mapCenter.y - testSquareSize; z <= mapCenter.y + testSquareSize; z += 20)
+ for (let x = mapCenter.x - testSquareSize; x <= mapCenter.x + testSquareSize;x += 4)
+ {
+ let ent = Engine.AddEntity(TEST_TEMPLATE_REGULAR);
+
+ let component = Engine.QueryInterface(ent, IID_Position);
+ component.JumpTo(x, z);
+
+ component = Engine.QueryInterface(ent, IID_Ownership);
+ component.SetOwnerQuiet(1);
+
+ component = Engine.QueryInterface(ent, IID_UnitAI);
+ for (let i = 0; i < 10; ++i)
+ {
+ component.Walk(x, z + 15, true);
+ component.Walk(x, z, true);
+ }
+ }
+}
+
+Trigger.prototype.TestShortRunIntoEachOther = function()
+{
+ warn("Starting TestShortRunIntoEachOther");
+ // Same principle but units will run into each other.
+ // highlights slowness of hort-range pathfinder
+ let cmpTerrain = Engine.QueryInterface(SYSTEM_ENTITY, IID_Terrain);
+ let mapSize = 4*cmpTerrain.GetTilesPerSide();
+
+ let mapCenter = new Vector2D(mapSize/2.0, mapSize/2.0);
+ let testSquareSize = mapSize / 5;
+
+ let dir = false;
+ // create units
+ for (let z = mapCenter.y - testSquareSize; z <= mapCenter.y + testSquareSize; z += 20)
+ {
+ for (let x = mapCenter.x - testSquareSize; x <= mapCenter.x + testSquareSize;x += 4)
+ {
+ let ent = Engine.AddEntity(TEST_TEMPLATE_REGULAR);
+
+ let component = Engine.QueryInterface(ent, IID_Position);
+ component.JumpTo(x, z);
+
+ component = Engine.QueryInterface(ent, IID_Ownership);
+ component.SetOwnerQuiet(1);
+
+ component = Engine.QueryInterface(ent, IID_UnitAI);
+ for (let i = 0; i < 10; ++i)
+ {
+ component.Walk(x, z + (dir ? 40 : -40), true);
+ component.Walk(x, z, true);
+ }
+ }
+ dir = !dir;
+ }
+}
+
+Trigger.prototype.TestLongDiagonalpath = function()
+{
+ warn("Starting TestLongDiagonalpath");
+
+ // stress-test long-range pathfinder. This is a diagonal move in the wrong direction on an largely empty map
+ // which means that JPS will basically loop over the whole map but also run in a ton of jump points.
+ // and generally speaking this is a worst case of some kind.
+ // units will then start moving.
+ // this scales poorly, on a medium map it's OK, but on a large map with more units and stuff it's just horribly slow.
+
+ // the movement will stress-test TestLine() but it's a bit of an atypical case to say the least.
+
+ let cmpTerrain = Engine.QueryInterface(SYSTEM_ENTITY, IID_Terrain);
+ let mapSize = 4*cmpTerrain.GetTilesPerSide();
+
+ let mapCenter = new Vector2D(mapSize/2.0, mapSize/2.0);
+ let testSquareSize = mapSize / 5;
+
+ // create blocking obelisks
+ for (let z = mapCenter.y-testSquareSize; z <= mapCenter.y + testSquareSize*2; z += 8)
+ for (let x = mapCenter.x-testSquareSize; x <= mapCenter.x + testSquareSize*2;x += 8)
+ {
+ let ent = Engine.AddEntity(TEST_TEMPLATE_OBELISK);
+
+ let component = Engine.QueryInterface(ent, IID_Position);
+ component.JumpTo(x, z);
+ }
+
+ // create units
+ for (let z = mapCenter.y - testSquareSize*2; z <= mapCenter.y; z += 8)
+ for (let x = mapCenter.x - testSquareSize*2; x <= mapCenter.x;x += 8)
+ {
+ let ent = Engine.AddEntity(TEST_TEMPLATE_REGULAR);
+
+ let component = Engine.QueryInterface(ent, IID_Position);
+ component.JumpTo(x, z);
+
+ component = Engine.QueryInterface(ent, IID_Ownership);
+ component.SetOwnerQuiet(1);
+
+ component = Engine.QueryInterface(ent, IID_UnitAI);
+ component.Walk(x + testSquareSize*2, z + testSquareSize*2);
+ }
+}
\ No newline at end of file
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;
+
+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":1.5} ];
+
+cmpTechMgr = QueryPlayerIDInterface(2, IID_TechnologyManager);
+cmpTechMgr.modifications = {};
+cmpTechMgr.modifications['Attack/Ranged/MaxRange'] = [ {"affects":[["Unit"]], "replace":0} ];
+
+Trigger.prototype.setupTests = function()
+{
+ let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
+ start = cmpTimer.GetTime();
+
+ 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", "becomes":440},
+ "428" : {"target":426, "expectfail":true}, // try to reach unreachable obelisk.
+ "422" : {"target":363}, // Get out of impassable house
+ };
+
+ // 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 cmpResourceSupply = Engine.QueryInterface(tests[test].becomes, IID_ResourceSupply);
+ if (cmpResourceSupply && cmpResourceSupply.GetCurrentAmount() < cmpResourceSupply.GetMaxAmount())
+ Success(test);
+}
+
+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) > 10 || (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,3060 @@
+
+
+
+
+ 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
+
+
+
+
+
+ units/maur_infantry_archer_b
+ 1
+
+
+
+
+
+ units/maur_infantry_archer_b
+ 1
+
+
+
+
+
+ other/obelisk
+ 1
+
+
+
+
+
+ structures/maur_house
+ 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
+
+
+
+
+
+ gaia/flora_tree_cretan_date_palm_tall
+ 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
+
+
+
+
+
+ units/maur_infantry_archer_b
+ 1
+
+
+
+
+
+ units/maur_infantry_archer_b
+ 1
+
+
+
+
+
+
+
\ No newline at end of file
Index: binaries/data/mods/public/simulation/components/Attack.js
===================================================================
--- binaries/data/mods/public/simulation/components/Attack.js
+++ binaries/data/mods/public/simulation/components/Attack.js
@@ -210,10 +210,6 @@
Attack.prototype.CanAttack = function(target)
{
- let cmpFormation = Engine.QueryInterface(target, IID_Formation);
- if (cmpFormation)
- return true;
-
let cmpThisPosition = Engine.QueryInterface(this.entity, IID_Position);
let cmpTargetPosition = Engine.QueryInterface(target, IID_Position);
if (!cmpThisPosition || !cmpTargetPosition || !cmpThisPosition.IsInWorld() || !cmpTargetPosition.IsInWorld())
@@ -293,14 +289,6 @@
Attack.prototype.GetBestAttackAgainst = function(target, allowCapture)
{
- let cmpFormation = Engine.QueryInterface(target, IID_Formation);
- if (cmpFormation)
- {
- // TODO: Formation against formation needs review
- let types = this.GetAttackTypes();
- return ["Ranged", "Melee", "Capture"].find(attack => types.indexOf(attack) != -1);
- }
-
let cmpIdentity = Engine.QueryInterface(target, IID_Identity);
if (!cmpIdentity)
return undefined;
Index: binaries/data/mods/public/simulation/components/Builder.js
===================================================================
--- binaries/data/mods/public/simulation/components/Builder.js
+++ binaries/data/mods/public/simulation/components/Builder.js
@@ -49,12 +49,7 @@
Builder.prototype.GetRange = function()
{
- var cmpObstruction = Engine.QueryInterface(this.entity, IID_Obstruction);
- var max = 2;
- if (cmpObstruction)
- max += cmpObstruction.GetUnitRadius();
-
- return { "max": max, "min": 0 };
+ return { "max": 1, "min": 0 };
};
/**
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
@@ -861,7 +861,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/GroupWalkManager.js
===================================================================
--- /dev/null
+++ binaries/data/mods/public/simulation/components/GroupWalkManager.js
@@ -0,0 +1,252 @@
+function GroupWalkManager() {}
+
+GroupWalkManager.prototype.Schema =
+ "";
+
+// This is a simple system component that keeps track of existing "groups"
+// when a "walk together" order is issued as well as necessary information.
+
+GroupWalkManager.prototype.Init = function()
+{
+ this.groups = new Map();
+ this.nextGroupID = 0;
+};
+
+GroupWalkManager.prototype.Serialize = function()
+{
+};
+
+GroupWalkManager.prototype.CreateGroup = function(formableEntsID, x, z, range, formationTemplate)
+{
+ let speed = 50.0; // TODO: put formation max speed?
+ for (let ent of formableEntsID)
+ {
+ let cmpUnitMotion = Engine.QueryInterface(ent, IID_UnitMotion);
+ let sp = cmpUnitMotion.GetBaseSpeed();
+ if (sp < speed)
+ speed = sp;
+ }
+ let group = {
+ "entities" : formableEntsID,
+ "x" : x,
+ "z" : z,
+ "range": range,
+ "formationTemplate":formationTemplate,
+ "maxSpeed": speed,
+ "state": "waiting",
+ "readyForNextStep" : [],
+ "entHasObstructedPath" : [],
+ "step":0,
+ "waypoints": [],
+ "offsets": {}
+ };
+ this.groups.set(this.nextGroupID++, group);
+
+ return this.nextGroupID - 1;
+};
+
+GroupWalkManager.prototype.GetMaxSpeed = function(ID)
+{
+ // undefined if undefined
+ return !!this.groups.get(ID) ? this.groups.get(ID).maxSpeed : 0;
+}
+
+GroupWalkManager.prototype.GetGroup = function(ID)
+{
+ // undefined if undefined
+ return this.groups.get(ID);
+}
+
+// An entity is telling us it won't be able to reach the intended position
+// if enough entities have told us this, skip the waypoint unless it's the last
+GroupWalkManager.prototype.SetBlockedPath = function(ID, ent)
+{
+ if (!this.groups.get(ID))
+ {
+ error("Entity " + ent + " blocked for unkown group " + ID);
+ return;
+ }
+ let group = this.groups.get(ID);
+ if (group.entities.indexOf(ent) === -1)
+ {
+ error("Entity " + ent + " blocked for group " + ID + " it is not a part of.");
+ return;
+ }
+ if (group.entHasObstructedPath.indexOf(ent) === -1)
+ group.entHasObstructedPath.push(ent);
+
+ if (group.entHasObstructedPath.length < group.entities.length * 0.1)
+ return;
+
+ if (group.waypoints.length <= 1)
+ return;
+
+ this.AdvanceGroupState(ID);
+}
+
+// An entity is telling us it's ready for the next waypoint
+// if enough entities have told us, shift the whole group.
+GroupWalkManager.prototype.SetReady = function(ID, ent)
+{
+ if (!this.groups.get(ID))
+ {
+ error("Entity " + ent + " ready for unkown group " + ID);
+ return;
+ }
+ let group = this.groups.get(ID);
+ if (group.entities.indexOf(ent) === -1)
+ {
+ error("Entity " + ent + " ready for group " + ID + " it is not a part of.");
+ return;
+ }
+ if (group.readyForNextStep.indexOf(ent) === -1)
+ group.readyForNextStep.push(ent);
+
+ if (group.state == "waiting" && group.readyForNextStep.length !== group.entities.length
+ || group.readyForNextStep.length < group.entities.length * 0.85)
+ return;
+
+ this.AdvanceGroupState(ID);
+}
+
+GroupWalkManager.prototype.AdvanceGroupState = function(ID)
+{
+ let group = this.groups.get(ID);
+ if (!this.groups.get(ID))
+ {
+ error("Trying to advance unknown group " + ID);
+ return;
+ }
+
+ group.readyForNextStep = [];
+ if (group.state == "waiting")
+ {
+ group.rallyPoint = { "x":0, "z": 0 };
+ // TODO: find the best position.
+ let x = 0;
+ let z = 0;
+ for (let ent of group.entities)
+ {
+ let cmpPosition = Engine.QueryInterface(ent, IID_Position);
+ x += cmpPosition.GetPosition2D().x;
+ z += cmpPosition.GetPosition2D().y;
+ }
+ group.rallyPoint.x = x / group.entities.length;
+ group.rallyPoint.z = z / group.entities.length;
+
+ let p1 = new Vector2D(group.rallyPoint.x, group.rallyPoint.z);
+
+ let cmpPathfinder = Engine.QueryInterface(SYSTEM_ENTITY, IID_Pathfinder);
+ let path = cmpPathfinder.ComputePath(group.rallyPoint.x, group.rallyPoint.z, group.x, group.z, "large");
+ group.waypoints = path;
+ group.step = group.waypoints.length;
+ if (group.waypoints.length > 1)
+ {
+ group.rallyPoint = { "x":group.waypoints[group.step-1].x, "z":group.waypoints[group.step-1].y };
+ group.step--;
+ }
+
+ // compute offsets.
+ let p2 = new Vector2D(group.rallyPoint.x, group.rallyPoint.z);
+ p1.sub(p2).mult(-1);
+
+ let angle = Math.atan2(p1.x, p1.y);
+ group.offsets = this.ComputeOffsetsForWaypoint(angle, p1, group.entities);
+
+ group.state = "walking";
+ }
+ else if (group.state == "walking")
+ {
+ if (group.step === 0)
+ {
+ group.state = "arrived";
+ return;
+ }
+ let p1 = new Vector2D(group.rallyPoint.x, group.rallyPoint.z);
+
+ group.rallyPoint = { "x":group.waypoints[group.step-1].x, "z":group.waypoints[group.step-1].y };
+ group.step--;
+
+ // compute offsets.
+ let p2 = new Vector2D(group.rallyPoint.x, group.rallyPoint.z);
+ p1.sub(p2).mult(-1);
+
+ let angle = Math.atan2(p1.x, p1.y);
+ group.offsets = this.ComputeOffsetsForWaypoint(angle, p1, group.entities);
+
+ group.entHasObstructedPath = [];
+
+ group.state = "walking";
+
+ for (let ent of group.entities)
+ {
+ let cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
+ cmpUnitAI.SetNextStateAlwaysEntering("WALKING");
+ }
+ }
+}
+
+GroupWalkManager.prototype.ComputeOffsetsForWaypoint = function(angle, center, entities)
+{
+ // for now we'll do a simple rectangular formations
+ // TODO: support more stuff.
+ let ret = {};
+
+ let xW = Math.round(Math.min(6, Math.max(2, entities.length/4)));
+ let y = -1;
+ let maxYOffset = 0;
+ let largeEntities = [];
+ for (let i = 0; i < entities.length; i++)
+ {
+ let ent = entities[i];
+ let cmpUnitMotion = Engine.QueryInterface(ent, IID_UnitMotion);
+ if (cmpUnitMotion.GetUnitClearance() > 1)
+ {
+ largeEntities.push(ent);
+ continue;
+ }
+ let x = i % xW;
+ if (x == 0)
+ y++;
+ let offsetX = 3 * (x-xW/2.0);
+ let offsetY = -4 * y;
+ maxYOffset = -offsetY;
+ let vector = new Vector2D(offsetX, offsetY);
+ ret[ent] = vector.rotate(angle);
+ }
+ let baseY = y * -4;
+ y = 0;
+ xW = Math.round(Math.min(3, Math.max(1, largeEntities.length/2)));
+ for (let i = 0; i < largeEntities.length; i++)
+ {
+ let ent = largeEntities[i];
+ let x = i % xW;
+ if (x == 0)
+ y++;
+ let offsetX = 9 * (x-xW/2.0);
+ let offsetY = baseY -10 * y;
+ maxYOffset = -offsetY;
+ let vector = new Vector2D(offsetX, offsetY);
+ ret[ent] = vector.rotate(angle);
+ }
+
+ for (let ent in ret)
+ ret[ent].y += maxYOffset / 2.0;
+
+ return ret;
+}
+
+GroupWalkManager.prototype.ResignFromGroup = function(ID, ent)
+{
+ let group = this.groups.get(ID);
+ if (!group)
+ return;
+ group.entities.splice(group.entities.indexOf(ent), 1);
+ if (group.readyForNextStep.indexOf(ent) !== -1)
+ group.readyForNextStep.splice(group.readyForNextStep.indexOf(ent), 1);
+
+ if (!group.entities.length)
+ this.groups.delete(ID);
+}
+
+Engine.RegisterSystemComponentType(IID_GroupWalkManager, "GroupWalkManager", GroupWalkManager);
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
@@ -377,6 +377,7 @@
let cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
if (cmpUnitAI)
ret.unitAI = {
+ "formation": cmpUnitAI.GetFormationTemplate(),
"state": cmpUnitAI.GetCurrentState(),
"orders": cmpUnitAI.GetOrders(),
"hasWorkOrders": cmpUnitAI.HasWorkOrders(),
@@ -592,8 +593,10 @@
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(),
+ "tryingToMove" : cmpUnitMotion.IsTryingToMove()
};
return ret;
@@ -820,12 +823,10 @@
GuiInterface.prototype.IsFormationSelected = function(player, data)
{
- for (let ent of data.ents)
+ for (let ent of data.entities)
{
let cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
- // GetLastFormationName is named in a strange way as it (also) is
- // the value of the current formation (see Formation.js LoadFormation)
- if (cmpUnitAI && cmpUnitAI.GetLastFormationTemplate() == data.formationTemplate)
+ if (cmpUnitAI && cmpUnitAI.GetFormationTemplate() == data.formationTemplate)
return true;
}
return false;
Index: binaries/data/mods/public/simulation/components/ProductionQueue.js
===================================================================
--- binaries/data/mods/public/simulation/components/ProductionQueue.js
+++ binaries/data/mods/public/simulation/components/ProductionQueue.js
@@ -640,7 +640,7 @@
if (spawnedEnts.length > 0 && !cmpAutoGarrison)
{
- // If a rally point is set, walk towards it (in formation) using a suitable command based on where the
+ // If a rally point is set, walk towards it using a suitable command based on where the
// rally point is placed.
if (cmpRallyPoint)
{
Index: binaries/data/mods/public/simulation/components/RallyPoint.js
===================================================================
--- binaries/data/mods/public/simulation/components/RallyPoint.js
+++ binaries/data/mods/public/simulation/components/RallyPoint.js
@@ -23,6 +23,8 @@
var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
+ var cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
+ let cmpOurPosition = Engine.QueryInterface(this.entity, IID_Position);
// We must not affect the simulation state here (modifications of the
// RallyPointRenderer are allowed though), so copy the state
@@ -49,13 +51,31 @@
if (!targetPosition)
continue;
- if (this.pos[i].x == targetPosition.x && this.pos[i].z == targetPosition.y)
+ // if the target has a static obstruction, move the rallypoint position closer to us
+ // keep this in sync with unit_actions.js
+ let position = {};
+ let entityTemplateName = cmpTemplateManager.GetCurrentTemplateName(this.data[i].target);
+ let targetTemplate = cmpTemplateManager.GetTemplate(entityTemplateName);
+ if (targetTemplate.Obstruction && targetTemplate.Obstruction.Static)
+ {
+ let ourPosition = cmpOurPosition.GetPosition2D();
+ let size = Math.min(+targetTemplate.Obstruction.Static["@width"], +targetTemplate.Obstruction.Static["@depth"]);
+ let vector = new Vector2D(targetPosition.x-ourPosition.x,targetPosition.y-ourPosition.y);
+ let pos = new Vector2D(targetPosition.x, targetPosition.y);
+ pos = pos.sub(vector.normalize().mult(size * 0.49));
+ position.x = pos.x;
+ position.y = pos.y;
+ }
+ else
+ position = targetPosition;
+
+ if (this.pos[i].x == position.x && this.pos[i].z == position.y)
continue;
- ret[i] = { "x": targetPosition.x, "z": targetPosition.y };
+ ret[i] = { "x": position.x, "z": position.y };
var cmpRallyPointRenderer = Engine.QueryInterface(this.entity, IID_RallyPointRenderer);
if (cmpRallyPointRenderer)
- cmpRallyPointRenderer.UpdatePosition(i, targetPosition);
+ cmpRallyPointRenderer.UpdatePosition(i, position);
}
return ret;
@@ -117,10 +137,6 @@
*/
RallyPoint.prototype.TargetIsAlive = function(ent)
{
- var cmpFormation = Engine.QueryInterface(ent, IID_Formation);
- if (cmpFormation)
- return true;
-
var cmpHealth = QueryMiragedInterface(ent, IID_Health);
return cmpHealth && cmpHealth.GetHitpoints() != 0;
};
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 =
@@ -15,9 +17,6 @@
"standground" +
"" +
"" +
- "" +
- "" +
- "" +
"" +
"" +
"" +
@@ -174,37 +173,6 @@
// ignore
},
- // Formation handlers:
-
- "FormationLeave": function(msg) {
- // ignore when we're not in FORMATIONMEMBER
- },
-
- // Called when being told to walk as part of a formation
- "Order.FormationWalk": function(msg) {
- // Let players move captured domestic animals around
- if (this.IsAnimal() && !this.IsDomestic() || this.IsTurret())
- {
- this.FinishOrder();
- return;
- }
-
- // For packable units:
- // 1. If packed, we can move.
- // 2. If unpacked, we first need to pack, then follow case 1.
- if (this.CanPack())
- {
- // Case 2: pack
- this.PushOrderFront("Pack", { "force": true });
- return;
- }
-
- var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
- cmpUnitMotion.MoveToFormationOffset(msg.data.target, msg.data.x, msg.data.z);
-
- this.SetNextStateAlwaysEntering("FORMATIONMEMBER.WALKING");
- },
-
// Special orders:
// (these will be overridden by various states)
@@ -217,22 +185,21 @@
this.FinishOrder();
return;
}
- // Move a tile outside the building
- let range = 4;
- if (this.MoveToTargetRangeExplicit(msg.data.target, range, range))
+ // Move outside the building. Since INDIVIDUAL.WALKING checks for a distance in [0,1], move to 0.5
+ let range = 0.5;
+ if (this.MoveToTargetRangeExplicit(msg.data.target, range))
{
// We've started walking to the given point
this.SetNextState("INDIVIDUAL.WALKING");
}
else
{
- // We are already at the target, or can't move at all
+ // We can't reach the target
this.FinishOrder();
}
},
// Individual orders:
- // (these will switch the unit out of formation mode)
"Order.Stop": function(msg) {
// We have no control over non-domestic animals.
@@ -254,6 +221,34 @@
},
+ "Order.GroupWalk": function(msg) {
+ if (this.IsAnimal() || this.IsTurret())
+ {
+ this.FinishOrder();
+ return;
+ }
+
+ // For packable units:
+ // 1. If packed, we can move.
+ // 2. If unpacked, we first need to pack, then follow case 1.
+ if (this.CanPack())
+ {
+ // Case 2: pack
+ this.PushOrderFront("Pack", { "force": true });
+ return;
+ }
+
+ let cmpGroupWalkManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_GroupWalkManager);
+ let group = cmpGroupWalkManager.GetGroup(this.order.data.groupID);
+ if (!group || group.state != "waiting")
+ {
+ this.FinishOrder();
+ return;
+ }
+
+ this.SetNextStateAlwaysEntering("INDIVIDUAL.GROUPWALKING");
+ },
+
"Order.Walk": function(msg) {
// Let players move captured domestic animals around
if (this.IsAnimal() && !this.IsDomestic() || this.IsTurret())
@@ -276,7 +271,7 @@
if (!this.order.data.max)
this.MoveToPoint(this.order.data.x, this.order.data.z);
else
- this.MoveToPointRange(this.order.data.x, this.order.data.z, this.order.data.min, this.order.data.max);
+ this.MoveToPointRange(this.order.data.x, this.order.data.z, (this.order.data.min + this.order.data.max) / 2.0);
if (this.IsAnimal())
this.SetNextState("ANIMAL.WALKING");
else
@@ -339,7 +334,7 @@
}
else
{
- // We are already at the target, or can't move at all
+ // We can't reach the target
this.StopMoving();
this.FinishOrder();
}
@@ -366,16 +361,8 @@
// TODO: what if the units are on a cliff ? the ship will go below the cliff
// and the units won't be able to garrison. Should go to the nearest (accessible) shore
- if (needToMove && this.MoveToTarget(this.order.data.target))
- {
- this.SetNextState("INDIVIDUAL.PICKUP.APPROACHING");
- }
- else
- {
- // We are already at the target, or can't move at all
- this.StopMoving();
- this.SetNextState("INDIVIDUAL.PICKUP.LOADING");
- }
+ this.MoveToTarget(this.order.data.target, true);
+ this.SetNextState("INDIVIDUAL.PICKUP");
},
"Order.Guard": function(msg) {
@@ -385,7 +372,7 @@
return;
}
- if (this.MoveToTargetRangeExplicit(this.isGuardOf, 0, this.guardRange))
+ if (this.MoveToTargetRangeExplicit(this.isGuardOf, this.guardRange, true))
this.SetNextState("INDIVIDUAL.GUARD.ESCORTING");
else
this.SetNextState("INDIVIDUAL.GUARD.GUARDING");
@@ -395,7 +382,7 @@
// We use the distance between the entities to account for ranged attacks
var distance = DistanceBetweenEntities(this.entity, this.order.data.target) + (+this.template.FleeDistance);
var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
- if (cmpUnitMotion.MoveToTargetRange(this.order.data.target, distance, -1))
+ if (cmpUnitMotion.SetNewDestinationAsEntity(this.order.data.target, distance, true))
{
// We've started fleeing from the given target
if (this.IsAnimal())
@@ -405,7 +392,7 @@
}
else
{
- // We are already at the target, or can't move at all
+ // We can't reach the target
this.StopMoving();
this.FinishOrder();
}
@@ -603,8 +590,13 @@
}
return;
}
-
- this.PushOrderFront("Attack", { "target": this.order.data.target, "force": false, "hunting": true, "allowCapture": false });
+ if (this.order.data.triedAttacking)
+ {
+ this.FinishOrder();
+ return;
+ }
+ this.order.data.triedAttacking = true;
+ this.PushOrderFront("Attack", { "target": this.order.data.target, "force": this.order.data.force, "hunting": true, "allowCapture": false });
return;
}
@@ -616,7 +608,7 @@
}
else
{
- // We are already at the target, or can't move at all,
+ // We can't reach the target.
// so try gathering it from here.
// TODO: need better handling of the can't-reach-target case
this.StopMoving();
@@ -642,7 +634,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
@@ -693,7 +685,7 @@
}
else
{
- // We are already at the target, or can't move at all,
+ // We can't reach the target.
// so try repairing it from here.
// TODO: need better handling of the can't-reach-target case
this.StopMoving();
@@ -723,16 +715,8 @@
return;
}
- if (this.MoveToGarrisonRange(this.order.data.target))
- {
- this.SetNextState("INDIVIDUAL.GARRISON.APPROACHING");
- }
- else
- {
- // We do a range check before actually garrisoning
- this.StopMoving();
- this.SetNextState("INDIVIDUAL.GARRISON.GARRISONED");
- }
+ this.MoveToGarrisonRange(this.order.data.target, true)
+ this.SetNextState("INDIVIDUAL.GARRISON.APPROACHING");
},
"Order.Autogarrison": function(msg) {
@@ -747,693 +731,59 @@
"Order.Ungarrison": function() {
this.FinishOrder();
- this.isGarrisoned = false;
- },
-
- "Order.Alert": function(msg) {
- this.alertRaiser = this.order.data.raiser;
-
- // Find a target to garrison into, if we don't already have one
- if (!this.alertGarrisoningTarget)
- this.alertGarrisoningTarget = this.FindNearbyGarrisonHolder();
-
- if (this.alertGarrisoningTarget)
- this.ReplaceOrder("Garrison", {"target": this.alertGarrisoningTarget});
- else
- {
- this.StopMoving();
- this.FinishOrder();
- }
- },
-
- "Order.Cheering": function(msg) {
- this.SetNextState("INDIVIDUAL.CHEERING");
- },
-
- "Order.Pack": function(msg) {
- if (this.CanPack())
- {
- this.StopMoving();
- this.SetNextState("INDIVIDUAL.PACKING");
- }
- },
-
- "Order.Unpack": function(msg) {
- if (this.CanUnpack())
- {
- this.StopMoving();
- this.SetNextState("INDIVIDUAL.UNPACKING");
- }
- },
-
- "Order.CancelPack": function(msg) {
- var cmpPack = Engine.QueryInterface(this.entity, IID_Pack);
- if (cmpPack && cmpPack.IsPacking() && !cmpPack.IsPacked())
- cmpPack.CancelPack();
- this.FinishOrder();
- },
-
- "Order.CancelUnpack": function(msg) {
- var cmpPack = Engine.QueryInterface(this.entity, IID_Pack);
- if (cmpPack && cmpPack.IsPacking() && cmpPack.IsPacked())
- cmpPack.CancelPack();
- this.FinishOrder();
- },
-
- // States for the special entity representing a group of units moving in formation:
- "FORMATIONCONTROLLER": {
-
- "Order.Walk": function(msg) {
- this.CallMemberFunction("SetHeldPosition", [msg.data.x, msg.data.z]);
-
- this.MoveToPoint(this.order.data.x, this.order.data.z);
- this.SetNextState("WALKING");
- },
-
- "Order.WalkAndFight": function(msg) {
- this.CallMemberFunction("SetHeldPosition", [msg.data.x, msg.data.z]);
-
- this.MoveToPoint(this.order.data.x, this.order.data.z);
- this.SetNextState("WALKINGANDFIGHTING");
- },
-
- "Order.MoveIntoFormation": function(msg) {
- this.CallMemberFunction("SetHeldPosition", [msg.data.x, msg.data.z]);
-
- this.MoveToPoint(this.order.data.x, this.order.data.z);
- this.SetNextState("FORMING");
- },
-
- // Only used by other orders to walk there in formation
- "Order.WalkToTargetRange": function(msg) {
- if (this.MoveToTargetRangeExplicit(this.order.data.target, this.order.data.min, this.order.data.max))
- this.SetNextState("WALKING");
- else
- this.FinishOrder();
- },
-
- "Order.WalkToTarget": function(msg) {
- if (this.MoveToTarget(this.order.data.target))
- this.SetNextState("WALKING");
- else
- this.FinishOrder();
- },
-
- "Order.WalkToPointRange": function(msg) {
- if (this.MoveToPointRange(this.order.data.x, this.order.data.z, this.order.data.min, this.order.data.max))
- this.SetNextState("WALKING");
- else
- this.FinishOrder();
- },
-
- "Order.Patrol": function(msg) {
- this.CallMemberFunction("SetHeldPosition", [msg.data.x, msg.data.z]);
-
- this.MoveToPoint(this.order.data.x, this.order.data.z);
- this.SetNextState("PATROL");
- },
-
- "Order.Guard": function(msg) {
- this.CallMemberFunction("Guard", [msg.data.target, false]);
- var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation);
- cmpFormation.Disband();
- },
-
- "Order.Stop": function(msg) {
- if (!this.IsAttackingAsFormation())
- this.CallMemberFunction("Stop", [false]);
- this.FinishOrder();
- },
-
- "Order.Attack": function(msg) {
- var target = msg.data.target;
- var allowCapture = msg.data.allowCapture;
- var cmpTargetUnitAI = Engine.QueryInterface(target, IID_UnitAI);
- if (cmpTargetUnitAI && cmpTargetUnitAI.IsFormationMember())
- target = cmpTargetUnitAI.GetFormationController();
-
- var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
- // Check if we are already in range, otherwise walk there
- if (!this.CheckTargetAttackRange(target, target))
- {
- if (this.TargetIsAlive(target) && this.CheckTargetVisible(target))
- {
- if (this.MoveToTargetAttackRange(target, target))
- {
- this.SetNextState("COMBAT.APPROACHING");
- return;
- }
- }
- this.FinishOrder();
- return;
- }
- this.CallMemberFunction("Attack", [target, false, allowCapture]);
- if (cmpAttack.CanAttackAsFormation())
- this.SetNextState("COMBAT.ATTACKING");
- else
- this.SetNextState("MEMBER");
- },
-
- "Order.Garrison": function(msg) {
- if (!Engine.QueryInterface(msg.data.target, IID_GarrisonHolder))
- {
- this.FinishOrder();
- return;
- }
- // Check if we are already in range, otherwise walk there
- if (!this.CheckGarrisonRange(msg.data.target))
- {
- if (!this.CheckTargetVisible(msg.data.target))
- {
- this.FinishOrder();
- return;
- }
- else
- {
- // Out of range; move there in formation
- if (this.MoveToGarrisonRange(msg.data.target))
- {
- this.SetNextState("GARRISON.APPROACHING");
- return;
- }
- }
- }
-
- this.SetNextState("GARRISON.GARRISONING");
- },
-
- "Order.Gather": function(msg) {
- if (this.MustKillGatherTarget(msg.data.target))
- {
- // The target was visible when this order was given,
- // but could now be invisible.
- if (!this.CheckTargetVisible(msg.data.target))
- {
- if (msg.data.secondTry === undefined)
- {
- msg.data.secondTry = true;
- this.PushOrderFront("Walk", msg.data.lastPos);
- }
- else
- {
- // We couldn't move there, or the target moved away
- this.FinishOrder();
- }
- return;
- }
-
- this.PushOrderFront("Attack", { "target": msg.data.target, "hunting": true, "allowCapture": false });
- return;
- }
-
- // TODO: on what should we base this range?
- // Check if we are already in range, otherwise walk there
- if (!this.CheckTargetRangeExplicit(msg.data.target, 0, 10))
- {
- if (!this.CanGather(msg.data.target) || !this.CheckTargetVisible(msg.data.target))
- // The target isn't gatherable or not visible any more.
- this.FinishOrder();
- // TODO: Should we issue a gather-near-position order
- // if the target isn't gatherable/doesn't exist anymore?
- else
- // Out of range; move there in formation
- this.PushOrderFront("WalkToTargetRange", { "target": msg.data.target, "min": 0, "max": 10 });
- return;
- }
-
- this.CallMemberFunction("Gather", [msg.data.target, false]);
-
- this.SetNextStateAlwaysEntering("MEMBER");
- },
-
- "Order.GatherNearPosition": function(msg) {
- // TODO: on what should we base this range?
- // Check if we are already in range, otherwise walk there
- if (!this.CheckPointRangeExplicit(msg.data.x, msg.data.z, 0, 20))
- {
- // Out of range; move there in formation
- this.PushOrderFront("WalkToPointRange", { "x": msg.data.x, "z": msg.data.z, "min": 0, "max": 20 });
- return;
- }
-
- this.CallMemberFunction("GatherNearPosition", [msg.data.x, msg.data.z, msg.data.type, msg.data.template, false]);
-
- this.SetNextStateAlwaysEntering("MEMBER");
- },
-
- "Order.Heal": function(msg) {
- // TODO: on what should we base this range?
- // Check if we are already in range, otherwise walk there
- if (!this.CheckTargetRangeExplicit(msg.data.target, 0, 10))
- {
- if (!this.TargetIsAlive(msg.data.target) || !this.CheckTargetVisible(msg.data.target))
- // The target was destroyed
- this.FinishOrder();
- else
- // Out of range; move there in formation
- this.PushOrderFront("WalkToTargetRange", { "target": msg.data.target, "min": 0, "max": 10 });
- return;
- }
-
- this.CallMemberFunction("Heal", [msg.data.target, false]);
-
- this.SetNextStateAlwaysEntering("MEMBER");
- },
-
- "Order.Repair": function(msg) {
- // TODO: on what should we base this range?
- // Check if we are already in range, otherwise walk there
- if (!this.CheckTargetRangeExplicit(msg.data.target, 0, 10))
- {
- if (!this.TargetIsAlive(msg.data.target) || !this.CheckTargetVisible(msg.data.target))
- // The building was finished or destroyed
- this.FinishOrder();
- else
- // Out of range move there in formation
- this.PushOrderFront("WalkToTargetRange", { "target": msg.data.target, "min": 0, "max": 10 });
- return;
- }
-
- this.CallMemberFunction("Repair", [msg.data.target, msg.data.autocontinue, false]);
-
- this.SetNextStateAlwaysEntering("MEMBER");
- },
-
- "Order.ReturnResource": function(msg) {
- // TODO: on what should we base this range?
- // Check if we are already in range, otherwise walk there
- if (!this.CheckTargetRangeExplicit(msg.data.target, 0, 10))
- {
- if (!this.TargetIsAlive(msg.data.target) || !this.CheckTargetVisible(msg.data.target))
- // The target was destroyed
- this.FinishOrder();
- else
- // Out of range; move there in formation
- this.PushOrderFront("WalkToTargetRange", { "target": msg.data.target, "min": 0, "max": 10 });
- return;
- }
-
- this.CallMemberFunction("ReturnResource", [msg.data.target, false]);
-
- this.SetNextStateAlwaysEntering("MEMBER");
- },
-
- "Order.Pack": function(msg) {
- this.CallMemberFunction("Pack", [false]);
-
- this.SetNextStateAlwaysEntering("MEMBER");
- },
-
- "Order.Unpack": function(msg) {
- this.CallMemberFunction("Unpack", [false]);
-
- this.SetNextStateAlwaysEntering("MEMBER");
- },
-
- "IDLE": {
- "enter": function(msg) {
- var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation);
- cmpFormation.SetRearrange(false);
- },
-
- "MoveStarted": function() {
- let cmpFormation = Engine.QueryInterface(this.entity, IID_Formation);
- cmpFormation.SetRearrange(true);
- cmpFormation.MoveMembersIntoFormation(true, true);
- }
- },
-
- "WALKING": {
- "MoveStarted": function(msg) {
- var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation);
- cmpFormation.SetRearrange(true);
- cmpFormation.MoveMembersIntoFormation(true, true);
- },
-
- "MoveCompleted": function(msg) {
- if (this.FinishOrder())
- this.CallMemberFunction("ResetFinishOrder", []);
- },
- },
-
- "WALKINGANDFIGHTING": {
- "enter": function(msg) {
- this.StartTimer(0, 1000);
- },
-
- "Timer": function(msg) {
- // check if there are no enemies to attack
- this.FindWalkAndFightTargets();
- },
-
- "leave": function(msg) {
- this.StopTimer();
- },
-
- "MoveStarted": function(msg) {
- var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation);
- cmpFormation.SetRearrange(true);
- cmpFormation.MoveMembersIntoFormation(true, true);
- },
-
- "MoveCompleted": function(msg) {
- if (this.FinishOrder())
- this.CallMemberFunction("ResetFinishOrder", []);
- },
- },
-
- "PATROL": {
- "enter": function(msg) {
- // Memorize the origin position in case that we want to go back
- let cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
- if (!cmpPosition || !cmpPosition.IsInWorld())
- {
- this.FinishOrder();
- return;
- }
- if (!this.patrolStartPosOrder)
- {
- this.patrolStartPosOrder = cmpPosition.GetPosition();
- this.patrolStartPosOrder.targetClasses = this.order.data.targetClasses;
- }
-
- this.StartTimer(0, 1000);
- },
-
- "Timer": function(msg) {
- // Check if there are no enemies to attack
- this.FindWalkAndFightTargets();
- },
-
- "leave": function(msg) {
- this.StopTimer();
- delete this.patrolStartPosOrder;
- },
-
- "MoveStarted": function(msg) {
- let cmpFormation = Engine.QueryInterface(this.entity, IID_Formation);
- cmpFormation.SetRearrange(true);
- cmpFormation.MoveMembersIntoFormation(true, true);
- },
-
- "MoveCompleted": function() {
- /**
- * A-B-A-B-..:
- * if the user only commands one patrol order, the patrol will be between
- * the last position and the defined waypoint
- * A-B-C-..-A-B-..:
- * otherwise, the patrol is only between the given patrol commands and the
- * last position is not included (last position = the position where the unit
- * is located at the time of the first patrol order)
- */
-
- if (this.orderQueue.length == 1)
- this.PushOrder("Patrol", this.patrolStartPosOrder);
-
- this.PushOrder(this.order.type, this.order.data);
- this.FinishOrder();
- },
- },
-
- "GARRISON":{
- "enter": function() {
- // If the garrisonholder should pickup, warn it so it can take needed action
- var cmpGarrisonHolder = Engine.QueryInterface(this.order.data.target, IID_GarrisonHolder);
- if (cmpGarrisonHolder && cmpGarrisonHolder.CanPickup(this.entity))
- {
- this.pickup = this.order.data.target; // temporary, deleted in "leave"
- Engine.PostMessage(this.pickup, MT_PickupRequested, { "entity": this.entity });
- }
- },
-
- "leave": function() {
- // If a pickup has been requested and not yet canceled, cancel it
- if (this.pickup)
- {
- Engine.PostMessage(this.pickup, MT_PickupCanceled, { "entity": this.entity });
- delete this.pickup;
- }
- },
-
-
- "APPROACHING": {
- "MoveStarted": function(msg) {
- var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation);
- cmpFormation.SetRearrange(true);
- cmpFormation.MoveMembersIntoFormation(true, true);
- },
-
- "MoveCompleted": function(msg) {
- this.SetNextState("GARRISONING");
- },
- },
-
- "GARRISONING": {
- "enter": function() {
- // If a pickup has been requested, cancel it as it will be requested by members
- if (this.pickup)
- {
- Engine.PostMessage(this.pickup, MT_PickupCanceled, { "entity": this.entity });
- delete this.pickup;
- }
- this.CallMemberFunction("Garrison", [this.order.data.target, false]);
- this.SetNextStateAlwaysEntering("MEMBER");
- },
- },
- },
-
- "FORMING": {
- "MoveStarted": function(msg) {
- var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation);
- cmpFormation.SetRearrange(true);
- cmpFormation.MoveMembersIntoFormation(true, false);
- },
-
- "MoveCompleted": function(msg) {
-
- if (this.FinishOrder())
- {
- this.CallMemberFunction("ResetFinishOrder", []);
- return;
- }
- var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation);
-
- cmpFormation.FindInPosition();
- }
- },
-
- "COMBAT": {
- "APPROACHING": {
- "MoveStarted": function(msg) {
- var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation);
- cmpFormation.SetRearrange(true);
- cmpFormation.MoveMembersIntoFormation(true, true);
- },
-
- "MoveCompleted": function(msg) {
- var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
- this.CallMemberFunction("Attack", [this.order.data.target, false, this.order.data.allowCapture]);
- if (cmpAttack.CanAttackAsFormation())
- this.SetNextState("COMBAT.ATTACKING");
- else
- this.SetNextState("MEMBER");
- },
- },
-
- "ATTACKING": {
- // Wait for individual members to finish
- "enter": function(msg) {
- var target = this.order.data.target;
- var allowCapture = this.order.data.allowCapture;
- // Check if we are already in range, otherwise walk there
- if (!this.CheckTargetAttackRange(target, target))
- {
- if (this.TargetIsAlive(target) && this.CheckTargetVisible(target))
- {
- this.FinishOrder();
- this.PushOrderFront("Attack", { "target": target, "force": false, "allowCapture": allowCapture });
- return true;
- }
- this.FinishOrder();
- return true;
- }
-
- var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation);
- // TODO fix the rearranging while attacking as formation
- cmpFormation.SetRearrange(!this.IsAttackingAsFormation());
- cmpFormation.MoveMembersIntoFormation(false, false);
- this.StartTimer(200, 200);
- return false;
- },
-
- "Timer": function(msg) {
- var target = this.order.data.target;
- var allowCapture = this.order.data.allowCapture;
- // Check if we are already in range, otherwise walk there
- if (!this.CheckTargetAttackRange(target, target))
- {
- if (this.TargetIsAlive(target) && this.CheckTargetVisible(target))
- {
- this.FinishOrder();
- this.PushOrderFront("Attack", { "target": target, "force": false, "allowCapture": allowCapture });
- return;
- }
- this.FinishOrder();
- return;
- }
- },
-
- "leave": function(msg) {
- this.StopTimer();
- var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation);
- if (cmpFormation)
- cmpFormation.SetRearrange(true);
- },
- },
- },
-
- "MEMBER": {
- // Wait for individual members to finish
- "enter": function(msg) {
- var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation);
- cmpFormation.SetRearrange(false);
- this.StartTimer(1000, 1000);
- },
-
- "Timer": function(msg) {
- // Have all members finished the task?
- if (!this.TestAllMemberFunction("HasFinishedOrder", []))
- return;
-
- this.CallMemberFunction("ResetFinishOrder", []);
-
- // Execute the next order
- if (this.FinishOrder())
- {
- // if WalkAndFight order, look for new target before moving again
- if (this.IsWalkingAndFighting())
- this.FindWalkAndFightTargets();
- return;
- }
- },
-
- "leave": function(msg) {
- this.StopTimer();
- },
- },
- },
-
-
- // States for entities moving as part of a formation:
- "FORMATIONMEMBER": {
- "FormationLeave": function(msg) {
- // We're not in a formation anymore, so no need to track this.
- this.finishedOrder = false;
-
- // Stop moving as soon as the formation disbands
- this.StopMoving();
-
- // If the controller handled an order but some members rejected it,
- // they will have no orders and be in the FORMATIONMEMBER.IDLE state.
- if (this.orderQueue.length)
- {
- // We're leaving the formation, so stop our FormationWalk order
- if (this.FinishOrder())
- return;
- }
-
- // No orders left, we're an individual now
- if (this.IsAnimal())
- this.SetNextState("ANIMAL.IDLE");
- else
- this.SetNextState("INDIVIDUAL.IDLE");
- },
-
- // Override the LeaveFoundation order since we're not doing
- // anything more important (and we might be stuck in the WALKING
- // state forever and need to get out of foundations in that case)
- "Order.LeaveFoundation": function(msg) {
- // If foundation is not ally of entity, or if entity is unpacked siege,
- // ignore the order
- if (!IsOwnedByAllyOfEntity(this.entity, msg.data.target) && !Engine.QueryInterface(SYSTEM_ENTITY, IID_CeasefireManager).IsCeasefireActive() ||
- this.IsPacking() || this.CanPack() || this.IsTurret())
- {
- this.FinishOrder();
- return;
- }
- // Move a tile outside the building
- let range = 4;
- if (this.MoveToTargetRangeExplicit(msg.data.target, range, range))
- {
- // We've started walking to the given point
- this.SetNextState("WALKINGTOPOINT");
- }
- else
- {
- // We are already at the target, or can't move at all
- this.FinishOrder();
- }
- },
+ this.isGarrisoned = false;
+ },
+ "Order.Alert": function(msg) {
+ this.alertRaiser = this.order.data.raiser;
- "IDLE": {
- "enter": function() {
- if (this.IsAnimal())
- this.SetNextState("ANIMAL.IDLE");
- else
- this.SetNextState("INDIVIDUAL.IDLE");
- return true;
- },
- },
+ // Find a target to garrison into, if we don't already have one
+ if (!this.alertGarrisoningTarget)
+ this.alertGarrisoningTarget = this.FindNearbyGarrisonHolder();
- "WALKING": {
- "enter": function () {
- var cmpFormation = Engine.QueryInterface(this.formationController, IID_Formation);
- var cmpVisual = Engine.QueryInterface(this.entity, IID_Visual);
- if (cmpFormation && cmpVisual)
- {
- cmpVisual.ReplaceMoveAnimation("walk", cmpFormation.GetFormationAnimation(this.entity, "walk"));
- cmpVisual.ReplaceMoveAnimation("run", cmpFormation.GetFormationAnimation(this.entity, "run"));
- }
- this.SelectAnimation("move");
- },
+ if (this.alertGarrisoningTarget)
+ this.ReplaceOrder("Garrison", {"target": this.alertGarrisoningTarget});
+ else
+ {
+ this.StopMoving();
+ this.FinishOrder();
+ }
+ },
- // Occurs when the unit has reached its destination and the controller
- // is done moving. The controller is notified.
- "MoveCompleted": function(msg) {
- // 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);
- if (cmpVisual)
- {
- cmpVisual.ResetMoveAnimation("walk");
- cmpVisual.ResetMoveAnimation("run");
- }
+ "Order.Cheering": function(msg) {
+ this.SetNextState("INDIVIDUAL.CHEERING");
+ },
- var cmpFormation = Engine.QueryInterface(this.formationController, IID_Formation);
- if (cmpFormation)
- cmpFormation.SetInPosition(this.entity);
- },
- },
+ "Order.Pack": function(msg) {
+ if (this.CanPack())
+ {
+ this.StopMoving();
+ this.SetNextState("INDIVIDUAL.PACKING");
+ }
+ },
- // Special case used by Order.LeaveFoundation
- "WALKINGTOPOINT": {
- "enter": function() {
- var cmpFormation = Engine.QueryInterface(this.formationController, IID_Formation);
- if (cmpFormation)
- cmpFormation.UnsetInPosition(this.entity);
- this.SelectAnimation("move");
- },
+ "Order.Unpack": function(msg) {
+ if (this.CanUnpack())
+ {
+ this.StopMoving();
+ this.SetNextState("INDIVIDUAL.UNPACKING");
+ }
+ },
- "MoveCompleted": function() {
- this.FinishOrder();
- },
- },
+ "Order.CancelPack": function(msg) {
+ var cmpPack = Engine.QueryInterface(this.entity, IID_Pack);
+ if (cmpPack && cmpPack.IsPacking() && !cmpPack.IsPacked())
+ cmpPack.CancelPack();
+ this.FinishOrder();
},
+ "Order.CancelUnpack": function(msg) {
+ var cmpPack = Engine.QueryInterface(this.entity, IID_Pack);
+ if (cmpPack && cmpPack.IsPacking() && cmpPack.IsPacked())
+ cmpPack.CancelPack();
+ this.FinishOrder();
+ },
- // States for entities not part of a formation:
"INDIVIDUAL": {
"enter": function() {
@@ -1445,6 +795,7 @@
"Attacked": function(msg) {
// Respond to attack if we always target attackers, or if we target attackers
// during passive orders (e.g. gathering/repairing are never forced)
+ // TODO: handle group-walking order.
if (this.GetStance().targetAttackersAlways || (this.GetStance().targetAttackersPassive && (!this.order || !this.order.data || !this.order.data.force)))
{
this.RespondToTargetedEntities([msg.data.attacker]);
@@ -1508,14 +859,7 @@
"enter": function() {
// Switch back to idle animation to guarantee we won't
// get stuck with an incorrect animation
- var animationName = "idle";
- if (this.IsFormationMember())
- {
- var cmpFormation = Engine.QueryInterface(this.formationController, IID_Formation);
- if (cmpFormation)
- animationName = cmpFormation.GetFormationAnimation(this.entity, animationName);
- }
- this.SelectAnimation(animationName);
+ this.SelectAnimation("idle");
// If we have some orders, it is because we are in an intermediary state
// from FinishOrder (SetNextState("IDLE") is only executed when we get
@@ -1584,7 +928,6 @@
},
"MoveStarted": function() {
- this.SelectAnimation("move");
},
"MoveCompleted": function() {
@@ -1592,6 +935,11 @@
},
"Timer": function(msg) {
+ // bit of a sanity check, but this happening would most likely mean a bug somewhere.
+ let cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
+ if (cmpUnitMotion && cmpUnitMotion.IsTryingToMove())
+ warn("Entity " + this.entity + " is in the idle state but trying to move");
+
if (!this.isIdle)
{
this.isIdle = true;
@@ -1600,23 +948,179 @@
},
},
+ "GROUPWALKING": {
+ "enter": function() {
+ this.group = this.order.data.groupID;
+ this.SetNextState("WAITING");
+ },
+
+ "leave": function() {
+ let cmpGroupWalkManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_GroupWalkManager);
+ let group = cmpGroupWalkManager.GetGroup(this.group);
+ if (group)
+ cmpGroupWalkManager.ResignFromGroup(this.group, this.entity);
+ this.group = undefined;
+ },
+
+ "WAITING" : {
+ "enter": function() {
+ let cmpGroupWalkManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_GroupWalkManager);
+ let group = cmpGroupWalkManager.GetGroup(this.order.data.groupID);
+ if (!group || group.state != "waiting")
+ {
+ this.FinishOrder();
+ return true;
+ }
+ cmpGroupWalkManager.SetReady(this.order.data.groupID, this.entity);
+ this.StartTimer(200, 400);
+ },
+
+ "leave": function() {
+ this.StopTimer();
+ },
+
+ "Timer": function() {
+ let cmpGroupWalkManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_GroupWalkManager);
+ let group = cmpGroupWalkManager.GetGroup(this.order.data.groupID);
+ if (!group || group.state == "arrived")
+ {
+ this.FinishOrder();
+ return true;
+ }
+ if (group.state == "walking")
+ this.SetNextState("WALKING");
+ },
+ },
+ "WALKING" : {
+ "enter": function() {
+ let cmpGroupWalkManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_GroupWalkManager);
+ let group = cmpGroupWalkManager.GetGroup(this.order.data.groupID);
+ if (!group || group.state != "walking")
+ {
+ this.FinishOrder();
+ return true;
+ }
+ let offset = group.offsets[this.entity];
+ this.MoveToPointRange(group.rallyPoint.x + offset.x, group.rallyPoint.z + offset.y, 0, true);
+
+ // TODO: for the first "grouping" order it'd be nice to skip this
+ let maxSpeed = cmpGroupWalkManager.GetMaxSpeed(this.order.data.groupID);
+ let cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
+ let ratio = maxSpeed / cmpUnitMotion.GetBaseSpeed();
+ cmpUnitMotion.SetSpeed(ratio);
+ this.StartTimer(200, 500);
+ this.step = group.step; // temporary, deleted in leave
+ },
+
+ "leave": function() {
+ this.StopTimer();
+ this.ready = undefined;
+ this.step = undefined;
+ this.validatedOffset = undefined;
+ let cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
+ cmpUnitMotion.SetSpeed(WALKING_SPEED);
+ },
+
+ "Timer": function() {
+ let cmpGroupWalkManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_GroupWalkManager);
+ let group = cmpGroupWalkManager.GetGroup(this.order.data.groupID);
+ if (!group)
+ {
+ this.FinishOrder();
+ return true;
+ }
+ if (group.state == "arrived")
+ {
+ // TODO: should probably handle stances and do different things depending on the order here.
+
+ let cmpObstructionManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ObstructionManager);
+ let cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
+ let offset = group.offsets[this.entity];
+ if (!cmpUnitMotion.IsActuallyMoving()
+ && cmpObstructionManager.IsInPointRange(this.entity, group.rallyPoint.x + offset.x, group.rallyPoint.z + offset.y, 0, 0))
+ this.FinishOrder();
+ return;
+ }
+ if (group.step < this.step)
+ {
+ // jump straight to the next rallypoint.
+ this.SetNextStateAlwaysEntering("WALKING");
+ return;
+ }
+ if (this.ready)
+ return;
+ let cmpObstructionManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ObstructionManager);
+ let range = group.step !== 0 ? 10 : group.range;
+ let offset = group.offsets[this.entity];
+ if (cmpObstructionManager.IsInPointRange(this.entity, group.rallyPoint.x + offset.x, group.rallyPoint.z + offset.y, 0, range))
+ {
+ this.ready = true;
+ cmpGroupWalkManager.SetReady(this.order.data.groupID, this.entity);
+ }
+ else if (!this.validatedOffset)
+ {
+ this.validatedOffset = true;
+ // check whether our reachable goal is close enough to our intended offset.
+ // TODO: ideally we'd also check the actual-path-distance to the rallypoint, and figure if we've been placed somewhere wrong.
+ let cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
+ let goal = cmpUnitMotion.GetReachableGoalPosition();
+ let distance = (goal.x - group.rallyPoint.x - offset.x) * (goal.x - group.rallyPoint.x - offset.x)
+ + (goal.y - group.rallyPoint.z - offset.y) * (goal.y - group.rallyPoint.z - offset.y)
+ if (distance > 3)
+ cmpGroupWalkManager.SetBlockedPath(this.order.data.groupID, this.entity);
+ }
+ },
+
+ "MoveCompleted": function(msg) {
+ let cmpGroupWalkManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_GroupWalkManager);
+ let group = cmpGroupWalkManager.GetGroup(this.order.data.groupID);
+ if (!group)
+ {
+ this.FinishOrder();
+ return;
+ }
+
+ if (!msg.data.error)
+ return;
+
+ // UnitMotion has told us we were unlikely to reach our destination.
+ // if we're way out of position we should exit the group
+ let cmpObstructionManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ObstructionManager);
+ let offset = group.offsets[this.entity];
+ if (!cmpObstructionManager.IsInPointRange(this.entity, group.rallyPoint.x + offset.x, group.rallyPoint.z + offset.y, 0, 60))
+ {
+ this.FinishOrder();
+ return;
+ }
+ // tell our group we're ready, it's probably just that our waypoint is impassable right now
+ this.ready = true;
+ cmpGroupWalkManager.SetReady(this.order.data.groupID, this.entity);
+ }
+ }
+ },
+
"WALKING": {
"enter": function () {
- this.SelectAnimation("move");
},
- "MoveCompleted": function() {
- this.FinishOrder();
+ "MoveCompleted": function(msg) {
+ let cmpObstructionManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ObstructionManager);
+ if (msg.data.error
+ || !this.order.data.target && cmpObstructionManager.IsInPointRange(this.entity, this.order.data.x, this.order.data.z, 0, 1)
+ || this.order.data.target && cmpObstructionManager.IsInTargetRange(this.entity, this.order.data.target, 0, 1))
+ {
+ this.StopMoving();
+ this.FinishOrder();
+ }
},
},
"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) {
@@ -1628,7 +1132,12 @@
},
"MoveCompleted": function() {
- this.FinishOrder();
+ let cmpObstructionManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ObstructionManager);
+ if (cmpObstructionManager.IsInPointRange(this.entity, this.order.data.x, this.order.data.z, 0, 1))
+ {
+ this.StopMoving();
+ this.FinishOrder();
+ }
},
},
@@ -1648,7 +1157,6 @@
}
this.StartTimer(0, 1000);
- this.SelectAnimation("move");
},
"leave": function() {
@@ -1661,6 +1169,7 @@
},
"MoveCompleted": function() {
+ this.StopMoving();
if (this.orderQueue.length == 1)
this.PushOrder("Patrol",this.patrolStartPosOrder);
@@ -1678,10 +1187,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;
},
@@ -1695,31 +1203,33 @@
return;
}
this.SetHeldPositionOnEntity(this.isGuardOf);
- },
- "leave": function(msg) {
- this.SetMoveSpeed(this.GetWalkSpeed());
- this.StopTimer();
- },
-
- "MoveStarted": function(msg) {
// Adapt the speed to the one of the target if needed
- var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
- if (cmpUnitMotion.IsInTargetRange(this.isGuardOf, 0, 3*this.guardRange))
+ let cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
+ let cmpObstructionManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ObstructionManager);
+ if (cmpObstructionManager.IsInTargetRange(this.entity, 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);
}
}
},
+ "leave": function(msg) {
+ this.SetMoveSpeed(WALKING_SPEED);
+ this.StopTimer();
+ },
+
"MoveCompleted": function() {
- this.SetMoveSpeed(this.GetWalkSpeed());
- if (!this.MoveToTargetRangeExplicit(this.isGuardOf, 0, this.guardRange))
+ this.StopMoving();
+ this.SetMoveSpeed(WALKING_SPEED);
+ if (!this.MoveToTargetRangeExplicit(this.isGuardOf, this.guardRange))
this.SetNextState("GUARDING");
},
},
@@ -1746,7 +1256,8 @@
return;
}
// then check is the target has moved
- if (this.MoveToTargetRangeExplicit(this.isGuardOf, 0, this.guardRange))
+ // TODO: this should call isInRange, not this.
+ if (this.MoveToTargetRangeExplicit(this.isGuardOf, this.guardRange))
this.SetNextState("ESCORTING");
else
{
@@ -1771,25 +1282,14 @@
"FLEEING": {
"enter": function() {
this.PlaySound("panic");
-
- // Run quickly
- var speed = this.GetRunSpeed();
- this.SelectAnimation("move");
- this.SetMoveSpeed(speed);
- },
-
- "HealthChanged": function() {
- var speed = this.GetRunSpeed();
- this.SetMoveSpeed(speed);
},
"leave": function() {
- // Reset normal speed
- this.SetMoveSpeed(this.GetWalkSpeed());
},
"MoveCompleted": function() {
// When we've run far enough, stop fleeing
+ this.StopMoving();
this.FinishOrder();
},
@@ -1810,15 +1310,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();
},
@@ -1833,10 +1332,19 @@
if (this.GetStance().respondHoldGround)
this.WalkToHeldPosition();
}
+ else if (this.CheckTargetAttackRange(this.order.data.target, this.order.data.attackType))
+ {
+ this.StopMoving();
+ // If the unit needs to unpack, do so
+ if (this.CanUnpack())
+ this.SetNextState("UNPACKING");
+ else
+ this.SetNextState("ATTACKING");
+ }
},
"MoveCompleted": function() {
-
+ this.StopMoving();
if (this.CheckTargetAttackRange(this.order.data.target, this.order.data.attackType))
{
// If the unit needs to unpack, do so
@@ -1903,14 +1411,6 @@
"ATTACKING": {
"enter": function() {
var target = this.order.data.target;
- var cmpFormation = Engine.QueryInterface(target, IID_Formation);
- // if the target is a formation, save the attacking formation, and pick a member
- if (cmpFormation)
- {
- this.order.data.formationTarget = target;
- target = cmpFormation.GetClosestMember(this.entity);
- this.order.data.target = target;
- }
// Check the target is still alive and attackable
if (this.TargetIsAlive(target) &&
this.CanAttack(target, this.order.data.forceResponse || null) &&
@@ -1943,12 +1443,6 @@
this.oldAttackType = this.order.data.attackType;
// add prefix + no capital first letter for attackType
var animationName = "attack_" + this.order.data.attackType.toLowerCase();
- if (this.IsFormationMember())
- {
- var cmpFormation = Engine.QueryInterface(this.formationController, IID_Formation);
- if (cmpFormation)
- animationName = cmpFormation.GetFormationAnimation(this.entity, animationName);
- }
this.SelectAnimation(animationName, false, 1.0, "attack");
this.SetAnimationSync(prepare, this.attackTimers.repeat);
this.StartTimer(prepare, this.attackTimers.repeat);
@@ -1969,22 +1463,11 @@
if (cmpBuildingAI)
cmpBuildingAI.SetUnitAITarget(0);
this.StopTimer();
+ this.SelectAnimation("idle");
},
"Timer": function(msg) {
var target = this.order.data.target;
- var cmpFormation = Engine.QueryInterface(target, IID_Formation);
- // if the target is a formation, save the attacking formation, and pick a member
- if (cmpFormation)
- {
- var thisObject = this;
- var filter = function(t) {
- return thisObject.TargetIsAlive(t) && thisObject.CanAttack(t, thisObject.order.data.forceResponse || null);
- };
- this.order.data.formationTarget = target;
- target = cmpFormation.GetClosestMember(this.entity, filter);
- this.order.data.target = target;
- }
// Check the target is still alive and attackable
if (this.TargetIsAlive(target) && this.CanAttack(target, this.order.data.forceResponse || null))
{
@@ -2038,16 +1521,6 @@
}
}
- // if we're targetting a formation, find a new member of that formation
- var cmpTargetFormation = Engine.QueryInterface(this.order.data.formationTarget || INVALID_ENTITY, IID_Formation);
- // if there is no target, it means previously searching for the target inside the target formation failed, so don't repeat the search
- if (target && cmpTargetFormation)
- {
- this.order.data.target = this.order.data.formationTarget;
- this.TimerHandler(msg.data, msg.lateness);
- return;
- }
-
// Can't reach it, no longer owned by enemy, or it doesn't exist any more - give up
// Except if in WalkAndFight mode where we look for more ennemies around before moving again
if (this.FinishOrder())
@@ -2095,16 +1568,10 @@
"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);
- if (cmpUnitAI && cmpUnitAI.IsFleeing())
- {
- // Run after a fleeing target
- var speed = this.GetRunSpeed();
- this.SetMoveSpeed(speed);
- }
+
this.StartTimer(1000, 1000);
},
@@ -2112,15 +1579,11 @@
var cmpUnitAI = Engine.QueryInterface(this.order.data.target, IID_UnitAI);
if (!cmpUnitAI || !cmpUnitAI.IsFleeing())
return;
- var speed = this.GetRunSpeed();
- this.SetMoveSpeed(speed);
},
"leave": function() {
- // Reset normal speed in case it was changed
- this.SetMoveSpeed(this.GetWalkSpeed());
// Show carried resources when walking.
- this.SetGathererAnimationOverride();
+ this.SetAnimationVariant();
this.StopTimer();
},
@@ -2135,9 +1598,19 @@
if (this.GetStance().respondHoldGround)
this.WalkToHeldPosition();
}
+ else if (this.CheckTargetAttackRange(this.order.data.target, this.order.data.attackType))
+ {
+ this.StopMoving();
+ // If the unit needs to unpack, do so
+ if (this.CanUnpack())
+ this.SetNextState("UNPACKING");
+ else
+ this.SetNextState("ATTACKING");
+ }
},
"MoveCompleted": function() {
+ this.StopMoving();
this.SetNextState("ATTACKING");
},
},
@@ -2146,7 +1619,6 @@
"GATHER": {
"APPROACHING": {
"enter": function() {
- this.SelectAnimation("move");
this.gatheringTarget = this.order.data.target; // temporary, deleted in "leave".
@@ -2206,12 +1678,14 @@
}
return true;
}
+ this.StartTimer(0, 500);
return false;
},
"MoveCompleted": function(msg) {
if (msg.data.error)
{
+ this.StopMoving();
// We failed to reach the target
// remove us from the list of entities gathering from Resource.
@@ -2252,12 +1726,24 @@
this.PerformGather(oldTarget, false, false);
return;
}
+ else if (this.CheckTargetRange(this.gatheringTarget, IID_ResourceGatherer))
+ {
+ this.StopMoving();
+ // We reached the target - start gathering from it now
+ this.SetNextState("GATHERING");
+ }
+ },
- // We reached the target - start gathering from it now
- this.SetNextState("GATHERING");
+ "Timer": function() {
+ if (this.CheckTargetRange(this.gatheringTarget, IID_ResourceGatherer))
+ {
+ this.StopMoving();
+ this.SetNextState("GATHERING");
+ }
},
"leave": function() {
+ this.StopTimer();
// don't use ownership because this is called after a conversion/resignation
// and the ownership would be invalid then.
var cmpSupply = Engine.QueryInterface(this.gatheringTarget, IID_ResourceSupply);
@@ -2270,10 +1756,12 @@
// Walking to a good place to gather resources near, used by GatherNearPosition
"WALKING": {
"enter": function() {
- this.SelectAnimation("move");
},
"MoveCompleted": function(msg) {
+ if (msg.data.error)
+ this.StopMoving();
+
var resourceType = this.order.data.type;
var resourceTemplate = this.order.data.template;
@@ -2305,7 +1793,6 @@
this.PushOrderFront("ReturnResource", { "target": nearby, "force": false });
return;
}
-
// No dropsites, just give up
},
},
@@ -2354,22 +1841,22 @@
return true;
}
- // Scale timing interval based on rate, and start timer
- // The offset should be at least as long as the repeat time so we use the same value for both.
- var offset = 1000/rate;
- var repeat = offset;
- this.StartTimer(offset, repeat);
-
- // We want to start the gather animation as soon as possible,
- // but only if we're actually at the target and it's still alive
- // (else it'll look like we're chopping empty air).
- // (If it's not alive, the Timer handler will deal with sending us
- // off to a different target.)
+ // Range check: if we are in-range, start the gathering animation and set the timer
+ // If we are not in-range, we'll set a different timer to avoid waiting an inordinate amount of time.
if (this.CheckTargetRange(this.gatheringTarget, IID_ResourceGatherer))
{
+ this.FaceTowardsTarget(this.gatheringTarget);
var typename = "gather_" + this.order.data.type.specific;
this.SelectAnimation(typename, false, 1.0, typename);
+
+ // Scale timing interval based on rate, and start timer
+ // The offset should be at least as long as the repeat time so we use the same value for both.
+ var offset = 1000/rate;
+ var repeat = offset;
+ this.StartTimer(offset, repeat);
}
+ else
+ this.StartTimer(0, 1000);
return false;
},
@@ -2384,7 +1871,8 @@
delete this.gatheringTarget;
// Show the carried resource, if we've gathered anything.
- this.SetGathererAnimationOverride();
+ this.SelectAnimation("idle");
+ this.SetAnimationVariant();
},
"Timer": function(msg) {
@@ -2454,10 +1942,9 @@
// the old one. So try to get close to the old resource's
// last known position
- var maxRange = 8; // get close but not too close
+ var range = 4; // get close but not too close
if (this.order.data.lastPos &&
- this.MoveToPointRange(this.order.data.lastPos.x, this.order.data.lastPos.z,
- 0, maxRange))
+ this.MoveToPointRange(this.order.data.lastPos.x, this.order.data.lastPos.z, range))
{
this.SetNextState("APPROACHING");
return;
@@ -2532,7 +2019,6 @@
"APPROACHING": {
"enter": function () {
- this.SelectAnimation("move");
this.StartTimer(1000, 1000);
},
@@ -2553,6 +2039,7 @@
},
"MoveCompleted": function() {
+ this.StopMoving();
this.SetNextState("HEALING");
},
},
@@ -2583,6 +2070,7 @@
},
"leave": function() {
+ this.SelectAnimation("idle");
this.StopTimer();
},
@@ -2633,7 +2121,6 @@
},
"CHASING": {
"enter": function () {
- this.SelectAnimation("move");
this.StartTimer(1000, 1000);
},
@@ -2652,6 +2139,7 @@
}
},
"MoveCompleted": function () {
+ this.StopMoving();
this.SetNextState("HEALING");
},
},
@@ -2661,19 +2149,41 @@
"RETURNRESOURCE": {
"APPROACHING": {
"enter": function () {
- this.SelectAnimation("move");
+ this.StartTimer(0, 1000);
},
- "MoveCompleted": function() {
- // Switch back to idle animation to guarantee we won't
- // get stuck with the carry animation after stopping moving
- this.SelectAnimation("idle");
+ "leave": function() {
+ this.StopTimer();
+ },
+ "Timer": function() {
// Check the dropsite is in range and we can return our resource there
// (we didn't get stopped before reaching it)
- if (this.CheckTargetRange(this.order.data.target, IID_ResourceGatherer) && this.CanReturnResource(this.order.data.target, true))
+ if (!this.CanReturnResource(this.order.data.target, true))
+ {
+ this.StopMoving();
+ // The dropsite was destroyed, or we couldn't reach it, or ownership changed
+ // Look for a new one.
+
+ var cmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer);
+ var genericType = cmpResourceGatherer.GetMainCarryingType();
+ var nearby = this.FindNearestDropsite(genericType);
+ if (nearby)
+ {
+ this.FinishOrder();
+ this.PushOrderFront("ReturnResource", { "target": nearby, "force": false });
+ return;
+ }
+
+ // Oh no, couldn't find any drop sites. Give up on returning.
+ this.FinishOrder();
+ return;
+ }
+ if (this.CheckTargetRange(this.order.data.target, IID_ResourceGatherer))
{
+ this.StopMoving();
var cmpResourceDropsite = Engine.QueryInterface(this.order.data.target, IID_ResourceDropsite);
+ // this ought to be redundant with the above check.
if (cmpResourceDropsite)
{
// Dump any resources we can
@@ -2683,7 +2193,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
@@ -2691,23 +2201,9 @@
return;
}
}
-
- // The dropsite was destroyed, or we couldn't reach it, or ownership changed
- // Look for a new one.
-
- var cmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer);
- var genericType = cmpResourceGatherer.GetMainCarryingType();
- var nearby = this.FindNearestDropsite(genericType);
- if (nearby)
- {
- this.FinishOrder();
- this.PushOrderFront("ReturnResource", { "target": nearby, "force": false });
- return;
- }
-
- // Oh no, couldn't find any drop sites. Give up on returning.
- this.FinishOrder();
},
+
+ "MoveCompleted": "Timer",
},
},
@@ -2718,19 +2214,29 @@
},
"APPROACHINGMARKET": {
- "enter": function () {
- this.SelectAnimation("move");
+ "enter": function() {
+ this.StartTimer(1000, 1000);
},
- "MoveCompleted": function() {
- if (this.waypoints && this.waypoints.length)
+ "leave": function() {
+ this.StopTimer();
+ },
+
+ "Timer": function() {
+ if (this.CheckTargetRange(this.order.data.target, IID_Trader))
{
- if (!this.MoveToMarket(this.order.data.target))
- this.StopTrading();
+ this.StopMoving();
+ if (this.waypoints && this.waypoints.length)
+ {
+ if (!this.MoveToMarket(this.order.data.target))
+ this.StopTrading();
+ }
+ else
+ this.PerformTradeAndMoveToNextMarket(this.order.data.target);
}
- else
- this.PerformTradeAndMoveToNextMarket(this.order.data.target);
},
+
+ "MoveCompleted": "Timer",
},
"TradingCanceled": function(msg) {
@@ -2740,17 +2246,30 @@
let otherMarket = cmpTrader && cmpTrader.GetFirstMarket();
this.StopTrading();
if (otherMarket)
- this.WalkToTarget(otherMarket);
+ this.g(otherMarket);
},
},
"REPAIR": {
"APPROACHING": {
"enter": function () {
- this.SelectAnimation("move");
+ this.StartTimer(0, 1000);
},
- "MoveCompleted": function() {
+ "leave": function() {
+ this.StopTimer();
+ },
+
+ "Timer": function() {
+ if (this.CheckTargetRange(this.order.data.target, IID_Builder))
+ {
+ this.StopMoving();
+ this.SetNextState("REPAIRING");
+ }
+ },
+ // TODO: clean this up when MoveCompleted becomes MoveSuccesHint and MoveFailure or something
+ "MoveCompleted": function(msg) {
+ this.StopMoving();
this.SetNextState("REPAIRING");
},
},
@@ -2795,6 +2314,8 @@
if (cmpBuilderList)
cmpBuilderList.AddBuilder(this.entity);
+ this.FaceTowardsTarget(this.repairTarget);
+
this.SelectAnimation("build", false, 1.0, "build");
this.StartTimer(1000, 1000);
return false;
@@ -2805,6 +2326,7 @@
if (cmpBuilderList)
cmpBuilderList.RemoveBuilder(this.entity);
delete this.repairTarget;
+ this.SelectAnimation("idle");
this.StopTimer();
},
@@ -2824,10 +2346,13 @@
// in that case, the repairTarget is deleted, and we can just return
if (!this.repairTarget)
return;
- if (this.MoveToTargetRange(this.repairTarget, IID_Builder))
- this.SetNextState("APPROACHING");
- else if (!this.CheckTargetRange(this.repairTarget, IID_Builder))
- this.FinishOrder(); //can't approach and isn't in reach
+ if (!this.CheckTargetRange(this.repairTarget, IID_Builder))
+ {
+ if (this.MoveToTargetRange(this.repairTarget, IID_Builder))
+ this.SetNextState("APPROACHING");
+ else
+ this.FinishOrder(); //can't approach and isn't in reach
+ }
},
},
@@ -2851,7 +2376,7 @@
{
let dropsiteTypes = cmpResourceDropsite.GetTypes();
cmpResourceGatherer.CommitResources(dropsiteTypes);
- this.SetGathererAnimationOverride();
+ this.SetAnimationVariant();
}
// We finished building it.
@@ -2860,7 +2385,7 @@
{
if (this.CanReturnResource(msg.data.newentity, true))
{
- this.SetGathererAnimationOverride();
+ this.SetAnimationVariant();
this.PushOrderFront("ReturnResource", { "target": msg.data.newentity, "force": false });
}
return;
@@ -2879,7 +2404,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);
@@ -2944,10 +2469,14 @@
"APPROACHING": {
"enter": function() {
- this.SelectAnimation("move");
+ this.StartTimer(1000,1000);
},
- "MoveCompleted": function() {
+ "leave": function() {
+ this.StopTimer();
+ },
+
+ "Timer": function() {
if (this.IsUnderAlert() && this.alertGarrisoningTarget)
{
// check that we can garrison in the building we're supposed to garrison in
@@ -2963,13 +2492,16 @@
}
else
this.FinishOrder();
+ return;
}
- else
- this.SetNextState("GARRISONED");
}
- else
+ if (this.order.data.target && this.CheckGarrisonRange(this.order.data.target))
+ this.SetNextState("GARRISONED");
+ else if (this.alertGarrisoningTarget && this.CheckGarrisonRange(this.alertGarrisoningTarget))
this.SetNextState("GARRISONED");
},
+
+ "MoveCompleted": "Timer",
},
"GARRISONED": {
@@ -3000,21 +2532,6 @@
{
this.isGarrisoned = true;
- if (this.formationController)
- {
- var cmpFormation = Engine.QueryInterface(this.formationController, IID_Formation);
- if (cmpFormation)
- {
- // disable rearrange for this removal,
- // but enable it again for the next
- // move command
- var rearrange = cmpFormation.rearrange;
- cmpFormation.SetRearrange(false);
- cmpFormation.RemoveMembers([this.entity]);
- cmpFormation.SetRearrange(rearrange);
- }
- }
-
// Check if we are garrisoned in a dropsite
var cmpResourceDropsite = Engine.QueryInterface(target, IID_ResourceDropsite);
if (cmpResourceDropsite && this.CanReturnResource(target, true))
@@ -3025,7 +2542,7 @@
if (cmpResourceGatherer)
{
cmpResourceGatherer.CommitResources(dropsiteTypes);
- this.SetGathererAnimationOverride();
+ this.SetAnimationVariant();
}
}
@@ -3060,7 +2577,7 @@
}
}
- if (this.MoveToTarget(target))
+ if (this.MoveToTarget(target, true))
{
this.SetNextState("APPROACHING");
return false;
@@ -3101,6 +2618,7 @@
this.StopTimer();
var cmpDamageReceiver = Engine.QueryInterface(this.entity, IID_DamageReceiver);
cmpDamageReceiver.SetInvulnerability(false);
+ this.SelectAnimation("idle");
},
"Timer": function(msg) {
@@ -3145,36 +2663,19 @@
},
"PICKUP": {
- "APPROACHING": {
- "enter": function() {
- this.SelectAnimation("move");
- },
-
- "MoveCompleted": function() {
- this.SetNextState("LOADING");
- },
-
- "PickupCanceled": function() {
- this.StopMoving();
+ "enter": function() {
+ var cmpGarrisonHolder = Engine.QueryInterface(this.entity, IID_GarrisonHolder);
+ if (!cmpGarrisonHolder || cmpGarrisonHolder.IsFull())
+ {
this.FinishOrder();
- },
+ return true;
+ }
+ return false;
},
- "LOADING": {
- "enter": function() {
- this.SelectAnimation("idle");
- var cmpGarrisonHolder = Engine.QueryInterface(this.entity, IID_GarrisonHolder);
- if (!cmpGarrisonHolder || cmpGarrisonHolder.IsFull())
- {
- this.FinishOrder();
- return true;
- }
- return false;
- },
-
- "PickupCanceled": function() {
- this.FinishOrder();
- },
+ "PickupCanceled": function() {
+ this.StopMoving();
+ this.FinishOrder();
},
},
},
@@ -3199,16 +2700,16 @@
},
"Order.LeaveFoundation": function(msg) {
- // Move a tile outside the building
- var range = 4;
- if (this.MoveToTargetRangeExplicit(msg.data.target, range, range))
+ // Move outside the building. Since INDIVIDUAL.WALKING checks for a distance in [0,1], move to 0.5
+ var range = 0.5;
+ if (this.MoveToTargetRangeExplicit(msg.data.target, range))
{
// We've started walking to the given point
this.SetNextState("WALKING");
}
else
{
- // We are already at the target, or can't move at all
+ // We cannot reach the target.
this.FinishOrder();
}
},
@@ -3226,16 +2727,13 @@
"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(randIntInclusive(+this.template.RoamTimeMin, +this.template.RoamTimeMax));
- this.SetFacePointAfterMove(false);
},
"leave": function() {
this.StopTimer();
- this.SetFacePointAfterMove(true);
},
"LosRangeUpdate": function(msg) {
@@ -3265,6 +2763,7 @@
},
"MoveCompleted": function() {
+ this.StopMoving();
this.MoveRandomly(+this.template.RoamDistance);
},
},
@@ -3297,7 +2796,7 @@
}
},
- "MoveCompleted": function() { },
+ "MoveCompleted": function() { this.StopMoving(); },
"Timer": function(msg) {
this.SetNextState("ROAMING");
@@ -3317,12 +2816,8 @@
{
this.orderQueue = []; // current order is at the front of the list
this.order = undefined; // always == this.orderQueue[0]
- this.formationController = INVALID_ENTITY; // entity with IID_Formation that we belong to
this.isGarrisoned = false;
this.isIdle = false;
- // For A19, keep no formations as a default to help pathfinding.
- this.lastFormationTemplate = "formations/null";
- this.finishedOrder = false; // used to find if all formation members finished the order
this.heldPosition = undefined;
@@ -3339,6 +2834,8 @@
this.lastAttacked = undefined;
this.lastHealed = undefined;
+ this.formationTemplate = "formations/null";
+
this.SetStance(this.template.DefaultStance);
};
@@ -3371,16 +2868,6 @@
return this.alertRaiser;
};
-UnitAI.prototype.IsFormationController = function()
-{
- return (this.template.FormationController == "true");
-};
-
-UnitAI.prototype.IsFormationMember = function()
-{
- return (this.formationController != INVALID_ENTITY);
-};
-
UnitAI.prototype.HasFinishedOrder = function()
{
return this.finishedOrder;
@@ -3445,12 +2932,6 @@
*/
UnitAI.prototype.IsWalkingAndFighting = function()
{
- if (this.IsFormationMember())
- {
- var cmpUnitAI = Engine.QueryInterface(this.formationController, IID_UnitAI);
- return (cmpUnitAI && cmpUnitAI.IsWalkingAndFighting());
- }
-
return (this.orderQueue.length > 0 && this.orderQueue[0].type == "WalkAndFight");
};
@@ -3458,8 +2939,6 @@
{
if (this.IsAnimal())
this.UnitFsm.Init(this, "ANIMAL.FEEDING");
- else if (this.IsFormationController())
- this.UnitFsm.Init(this, "FORMATIONCONTROLLER.IDLE");
else
this.UnitFsm.Init(this, "INDIVIDUAL.IDLE");
this.isIdle = true;
@@ -3682,6 +3161,9 @@
error("FinishOrder called for entity " + this.entity + " (" + template + ") when order queue is empty\n" + stack);
}
+ // Safety net, in general it's better if unitAI states handle this properly.
+ this.StopMoving();
+
this.orderQueue.shift();
this.order = this.orderQueue[0];
@@ -3707,21 +3189,6 @@
Engine.PostMessage(this.entity, MT_UnitAIOrderDataChanged, { "to": this.GetOrderData() });
- // Check if there are queued formation orders
- if (this.IsFormationMember())
- {
- let cmpUnitAI = Engine.QueryInterface(this.formationController, IID_UnitAI);
- if (cmpUnitAI)
- {
- // Inform the formation controller that we finished this task
- this.finishedOrder = true;
- // We don't want to carry out the default order
- // if there are still queued formation orders left
- if (cmpUnitAI.GetOrders().length > 1)
- return true;
- }
- }
-
return false;
}
};
@@ -3824,12 +3291,7 @@
{
// Remember the previous work orders to be able to go back to them later if required
if (data && data.force)
- {
- if (this.IsFormationController())
- this.CallMemberFunction("UpdateWorkOrders", [type]);
- else
- this.UpdateWorkOrders(type);
- }
+ this.UpdateWorkOrders(type);
// Special cases of orders that shouldn't be replaced:
// 1. Cheering - we're invulnerable, add order after we finish
@@ -3894,24 +3356,7 @@
if (this.workOrders.length)
return;
- // First if the unit is in a formation, get its workOrders from it
- if (this.IsFormationMember())
- {
- var cmpUnitAI = Engine.QueryInterface(this.formationController, IID_UnitAI);
- if (cmpUnitAI)
- {
- for (var i = 0; i < cmpUnitAI.orderQueue.length; ++i)
- {
- if (isWorkType(cmpUnitAI.orderQueue[i].type))
- {
- this.workOrders = cmpUnitAI.orderQueue.slice(i);
- return;
- }
- }
- }
- }
-
- // If nothing found, take the unit orders
+ // Take the unit orders
for (var i = 0; i < this.orderQueue.length; ++i)
{
if (isWorkType(this.orderQueue[i].type))
@@ -3939,14 +3384,6 @@
this.AddOrders(this.workOrders);
Engine.PostMessage(this.entity, MT_UnitAIOrderDataChanged, { "to": this.GetOrderData() });
- // And if the unit is in a formation, remove it from the formation
- if (this.IsFormationMember())
- {
- var cmpFormation = Engine.QueryInterface(this.formationController, IID_Formation);
- if (cmpFormation)
- cmpFormation.RemoveMembers([this.entity]);
- }
-
this.workOrders = [];
return true;
};
@@ -4009,12 +3446,15 @@
//// Message handlers /////
-UnitAI.prototype.OnMotionChanged = function(msg)
+UnitAI.prototype.OnMovePaused = function(msg)
+{
+ // TODO: change this. Doesn't matter if UnitAI thinks it's completed for now since anyways the states do range checks.
+ this.UnitFsm.ProcessMessage(this, { "type": "MoveCompleted", "data": { "error" : false }});
+};
+
+UnitAI.prototype.OnMoveFailure = 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": { "error" : true }});
};
UnitAI.prototype.OnGlobalConstructionFinished = function(msg)
@@ -4035,11 +3475,6 @@
changed = true;
order.data.target = msg.newentity;
}
- if (order.data && order.data.formationTarget && order.data.formationTarget == msg.entity)
- {
- changed = true;
- order.data.formationTarget = msg.newentity;
- }
}
if (changed)
Engine.PostMessage(this.entity, MT_UnitAIOrderDataChanged, { "to": this.GetOrderData() });
@@ -4075,22 +3510,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();
};
/**
@@ -4098,10 +3521,6 @@
*/
UnitAI.prototype.TargetIsAlive = function(ent)
{
- var cmpFormation = Engine.QueryInterface(ent, IID_Formation);
- if (cmpFormation)
- return true;
-
var cmpHealth = QueryMiragedInterface(ent, IID_Health);
return cmpHealth && cmpHealth.GetHitpoints() != 0;
};
@@ -4261,52 +3680,45 @@
*/
UnitAI.prototype.PlaySound = function(name)
{
- // If we're a formation controller, use the sounds from our first member
- if (this.IsFormationController())
- {
- var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation);
- var member = cmpFormation.GetPrimaryMember();
- if (member)
- PlaySound(name, member);
- }
- else
- {
- // Otherwise use our own sounds
- PlaySound(name, this.entity);
- }
+ PlaySound(name, this.entity);
};
-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)
@@ -4315,17 +3727,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)
{
@@ -4358,31 +3759,34 @@
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);
- return cmpUnitMotion.MoveToPointRange(x, z, 0, 0);
+ cmpUnitMotion.SetAbortIfStuck(30);
+ return cmpUnitMotion.SetNewDestinationAsPosition(x, z, 0, true);
};
-UnitAI.prototype.MoveToPointRange = function(x, z, rangeMin, rangeMax)
+UnitAI.prototype.MoveToPointRange = function(x, z, range, evenUnreachable = false)
{
var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
- return cmpUnitMotion.MoveToPointRange(x, z, rangeMin, rangeMax);
+ cmpUnitMotion.SetAbortIfStuck(30);
+ return cmpUnitMotion.SetNewDestinationAsPosition(x, z, range, evenUnreachable);
};
-UnitAI.prototype.MoveToTarget = function(target)
+UnitAI.prototype.MoveToTarget = function(target, evenUnreachable = false)
{
if (!this.CheckTargetVisible(target))
return false;
var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
- return cmpUnitMotion.MoveToTargetRange(target, 0, 0);
+ cmpUnitMotion.SetAbortIfStuck(5);
+ return cmpUnitMotion.SetNewDestinationAsEntity(target, 0, evenUnreachable);
};
-UnitAI.prototype.MoveToTargetRange = function(target, iid, type)
+UnitAI.prototype.MoveToTargetRange = function(target, iid, type, evenUnreachable = false)
{
if (!this.CheckTargetVisible(target) || this.IsTurret())
return false;
@@ -4393,7 +3797,10 @@
var range = cmpRanged.GetRange(type);
var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
- return cmpUnitMotion.MoveToTargetRange(target, range.min, range.max);
+ cmpUnitMotion.SetAbortIfStuck(5);
+ // generally speaking, try to aim for the middle of a range.
+ //
+ return cmpUnitMotion.SetNewDestinationAsEntity(target, (range.min + range.max)/2.0, evenUnreachable);
};
/**
@@ -4401,22 +3808,10 @@
* for melee attacks, this goes straight to the default range checks
* for ranged attacks, the parabolic range is used
*/
-UnitAI.prototype.MoveToTargetAttackRange = function(target, type)
+UnitAI.prototype.MoveToTargetAttackRange = function(target, type, evenUnreachable = false)
{
- // for formation members, the formation will take care of the range check
- if (this.IsFormationMember())
- {
- var cmpFormationUnitAI = Engine.QueryInterface(this.formationController, IID_UnitAI);
- if (cmpFormationUnitAI && cmpFormationUnitAI.IsAttackingAsFormation())
- return false;
- }
-
- var cmpFormation = Engine.QueryInterface(target, IID_Formation);
- if (cmpFormation)
- target = cmpFormation.GetClosestMember(this.entity);
-
if (type != "Ranged")
- return this.MoveToTargetRange(target, IID_Attack, type);
+ return this.MoveToTargetRange(target, IID_Attack, type, evenUnreachable);
if (!this.CheckTargetVisible(target))
return false;
@@ -4448,24 +3843,27 @@
// the parabole changes while walking, take something in the middle
var guessedMaxRange = (range.max + parabolicMaxRange)/2;
+// TODO: here we should give the desired range based on unit speed, our own desire to walk, and so on.
var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
- if (cmpUnitMotion.MoveToTargetRange(target, range.min, guessedMaxRange))
+ cmpUnitMotion.SetAbortIfStuck(9);
+ if (cmpUnitMotion.SetNewDestinationAsEntity(target, (range.min + guessedMaxRange)/2.0, false))
return true;
// if that failed, try closer
- return cmpUnitMotion.MoveToTargetRange(target, range.min, Math.min(range.max, parabolicMaxRange));
+ return cmpUnitMotion.SetNewDestinationAsEntity(target, (range.min + Math.min(range.max, parabolicMaxRange))/2.0, evenUnreachable);
};
-UnitAI.prototype.MoveToTargetRangeExplicit = function(target, min, max)
+UnitAI.prototype.MoveToTargetRangeExplicit = function(target, range, evenUnreachable = false)
{
if (!this.CheckTargetVisible(target))
return false;
var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
- return cmpUnitMotion.MoveToTargetRange(target, min, max);
+ cmpUnitMotion.SetAbortIfStuck(5);
+ return cmpUnitMotion.SetNewDestinationAsEntity(target, range, evenUnreachable);
};
-UnitAI.prototype.MoveToGarrisonRange = function(target)
+UnitAI.prototype.MoveToGarrisonRange = function(target, evenUnreachable = false)
{
if (!this.CheckTargetVisible(target))
return false;
@@ -4476,13 +3874,14 @@
var range = cmpGarrisonHolder.GetLoadingRange();
var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
- return cmpUnitMotion.MoveToTargetRange(target, range.min, range.max);
+ cmpUnitMotion.SetAbortIfStuck(5);
+ return cmpUnitMotion.SetNewDestinationAsEntity(target, (range.min + range.max)/2.0, evenUnreachable);
};
UnitAI.prototype.CheckPointRangeExplicit = function(x, z, min, max)
{
- var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
- return cmpUnitMotion.IsInPointRange(x, z, min, max);
+ let cmpObstructionManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ObstructionManager);
+ return cmpObstructionManager.IsInPointRange(this.entity, x, z, min, max);
};
UnitAI.prototype.CheckTargetRange = function(target, iid, type)
@@ -4491,9 +3890,8 @@
if (!cmpRanged)
return false;
var range = cmpRanged.GetRange(type);
-
- var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
- return cmpUnitMotion.IsInTargetRange(target, range.min, range.max);
+ let cmpObstructionManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ObstructionManager);
+ return cmpObstructionManager.IsInTargetRange(this.entity, target, range.min, range.max);
};
/**
@@ -4504,19 +3902,6 @@
*/
UnitAI.prototype.CheckTargetAttackRange = function(target, type)
{
- // for formation members, the formation will take care of the range check
- if (this.IsFormationMember())
- {
- var cmpFormationUnitAI = Engine.QueryInterface(this.formationController, IID_UnitAI);
- if (cmpFormationUnitAI && cmpFormationUnitAI.IsAttackingAsFormation()
- && cmpFormationUnitAI.order.data.target == target)
- return true;
- }
-
- var cmpFormation = Engine.QueryInterface(target, IID_Formation);
- if (cmpFormation)
- target = cmpFormation.GetClosestMember(this.entity);
-
if (type != "Ranged")
return this.CheckTargetRange(target, IID_Attack, type);
@@ -4541,14 +3926,14 @@
if (maxRangeSq < 0)
return false;
- var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
- return cmpUnitMotion.IsInTargetRange(target, range.min, Math.sqrt(maxRangeSq));
+ let cmpObstructionManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ObstructionManager);
+ return cmpObstructionManager.IsInTargetRange(this.entity, target, range.min, Math.sqrt(maxRangeSq));
};
UnitAI.prototype.CheckTargetRangeExplicit = function(target, min, max)
{
- var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
- return cmpUnitMotion.IsInTargetRange(target, min, max);
+ let cmpObstructionManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ObstructionManager);
+ return cmpObstructionManager.IsInTargetRange(this.entity, target, min, max);
};
UnitAI.prototype.CheckGarrisonRange = function(target)
@@ -4562,8 +3947,8 @@
if (cmpObstruction)
range.max += cmpObstruction.GetUnitRadius()*1.5; // multiply by something larger than sqrt(2)
- var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
- return cmpUnitMotion.IsInTargetRange(target, range.min, range.max);
+ let cmpObstructionManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ObstructionManager);
+ return cmpObstructionManager.IsInTargetRange(this.entity, target, range.min, range.max);
};
/**
@@ -4815,57 +4200,6 @@
//// External interface functions ////
-UnitAI.prototype.SetFormationController = function(ent)
-{
- this.formationController = ent;
-
- // Set obstruction group, so we can walk through members
- // of our own formation (or ourself if not in formation)
- var cmpObstruction = Engine.QueryInterface(this.entity, IID_Obstruction);
- if (cmpObstruction)
- {
- if (ent == INVALID_ENTITY)
- cmpObstruction.SetControlGroup(this.entity);
- else
- cmpObstruction.SetControlGroup(ent);
- }
-
- // If we were removed from a formation, let the FSM switch back to INDIVIDUAL
- if (ent == INVALID_ENTITY)
- this.UnitFsm.ProcessMessage(this, { "type": "FormationLeave" });
-};
-
-UnitAI.prototype.GetFormationController = function()
-{
- return this.formationController;
-};
-
-UnitAI.prototype.SetLastFormationTemplate = function(template)
-{
- this.lastFormationTemplate = template;
-};
-
-UnitAI.prototype.GetLastFormationTemplate = function()
-{
- return this.lastFormationTemplate;
-};
-
-UnitAI.prototype.MoveIntoFormation = function(cmd)
-{
- var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation);
- if (!cmpFormation)
- return;
-
- var cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
- if (!cmpPosition || !cmpPosition.IsInWorld())
- return;
-
- var pos = cmpPosition.GetPosition();
-
- // Add new order to move into formation at the current position
- this.PushOrderFront("MoveIntoFormation", { "x": pos.x, "z": pos.z, "force": true });
-};
-
UnitAI.prototype.GetTargetPositions = function()
{
var targetPositions = [];
@@ -4877,7 +4211,6 @@
case "Walk":
case "WalkAndFight":
case "WalkToPointRange":
- case "MoveIntoFormation":
case "GatherNearPosition":
case "Patrol":
targetPositions.push(new Vector2D(order.data.x, order.data.z));
@@ -5031,11 +4364,6 @@
UnitAI.prototype.CanGuard = function()
{
- // Formation controllers should always respond to commands
- // (then the individual units can make up their own minds)
- if (this.IsFormationController())
- return true;
-
// Do not let a unit already guarded to guard. This would work in principle,
// but would clutter the gui with too much buttons to take all cases into account
var cmpGuard = Engine.QueryInterface(this.entity, IID_Guard);
@@ -5046,6 +4374,28 @@
};
/**
+ * Set the preferred formation for this entity.
+ */
+UnitAI.prototype.SetFormationTemplate = function(template)
+{
+ // TODO: validate this entity accepts this?
+ this.formationTemplate = template;
+};
+
+UnitAI.prototype.GetFormationTemplate = function()
+{
+ return this.formationTemplate;
+};
+
+/**
+ * Adds group-walk order to the queue, necessarily in front.
+ */
+UnitAI.prototype.GroupWalk = function(groupID)
+{
+ this.AddOrder("GroupWalk", { "groupID": groupID }, false);
+};
+
+/**
* Adds walk order to queue, forced by the player.
*/
UnitAI.prototype.Walk = function(x, z, queued)
@@ -5121,7 +4471,7 @@
// We don't want to let healers walk to the target unit so they can be easily killed.
// Instead we just let them get into healing range.
if (this.IsHealer())
- this.MoveToTargetRange(target, IID_Heal);
+ this.MoveToTargetRange(target, IID_Heal, true);
else
this.WalkToTarget(target, queued);
return;
@@ -5221,7 +4571,7 @@
if (template.indexOf("resource|") != -1)
template = template.slice(9);
- if (this.IsFormationController() || Engine.QueryInterface(this.entity, IID_ResourceGatherer))
+ if (Engine.QueryInterface(this.entity, IID_ResourceGatherer))
this.AddOrder("GatherNearPosition", { "type": type, "template": template, "x": x, "z": z, "force": false }, queued);
else
this.AddOrder("Walk", { "x": x, "z": z, "force": false }, queued);
@@ -5289,22 +4639,11 @@
this.expectedRoute = undefined;
}
- if (this.IsFormationController())
- {
- this.CallMemberFunction("AddOrder", ["Trade", data, queued]);
- var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation);
- if (cmpFormation)
- cmpFormation.Disband();
- }
- else
- this.AddOrder("Trade", data, queued);
+ this.AddOrder("Trade", data, queued);
}
else
{
- if (this.IsFormationController())
- this.CallMemberFunction("WalkToTarget", [cmpTrader.GetFirstMarket(), queued]);
- else
- this.WalkToTarget(cmpTrader.GetFirstMarket(), queued);
+ this.WalkToTarget(cmpTrader.GetFirstMarket(), queued);
this.expectedRoute = [];
}
};
@@ -5316,9 +4655,6 @@
return false;
var marketsChanged = cmpTrader.SetTargetMarket(target, source);
- if (this.IsFormationController())
- this.CallMemberFunction("SetTargetMarket", [target, source]);
-
return marketsChanged;
};
@@ -5521,40 +4857,6 @@
UnitAI.prototype.FindWalkAndFightTargets = function()
{
- if (this.IsFormationController())
- {
- var cmpUnitAI;
- var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation);
- for (var ent of cmpFormation.members)
- {
- if (!(cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI)))
- continue;
- var targets = cmpUnitAI.GetTargetsFromUnit();
- for (var targ of targets)
- {
- if (!cmpUnitAI.CanAttack(targ))
- continue;
- if (this.order.data.targetClasses)
- {
- var cmpIdentity = Engine.QueryInterface(targ, IID_Identity);
- var targetClasses = this.order.data.targetClasses;
- if (targetClasses.attack && cmpIdentity
- && !MatchesClassList(cmpIdentity.GetClassesList(), targetClasses.attack))
- continue;
- if (targetClasses.avoid && cmpIdentity
- && MatchesClassList(cmpIdentity.GetClassesList(), targetClasses.avoid))
- continue;
- // Only used by the AIs to prevent some choices of targets
- if (targetClasses.vetoEntities && targetClasses.vetoEntities[targ])
- continue;
- }
- this.PushOrderFront("Attack", { "target": targ, "force": true, "allowCapture": true });
- return true;
- }
- }
- return false;
- }
-
var targets = this.GetTargetsFromUnit();
for (var targ of targets)
{
@@ -5691,8 +4993,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(speed);
};
UnitAI.prototype.SetHeldPosition = function(x, z)
@@ -5728,11 +5030,6 @@
UnitAI.prototype.CanAttack = function(target, forceResponse)
{
- // Formation controllers should always respond to commands
- // (then the individual units can make up their own minds)
- if (this.IsFormationController())
- return true;
-
// Verify that we're able to respond to Attack commands
var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
if (!cmpAttack)
@@ -5769,11 +5066,6 @@
UnitAI.prototype.CanGarrison = function(target)
{
- // Formation controllers should always respond to commands
- // (then the individual units can make up their own minds)
- if (this.IsFormationController())
- return true;
-
var cmpGarrisonHolder = Engine.QueryInterface(target, IID_GarrisonHolder);
if (!cmpGarrisonHolder)
return false;
@@ -5801,11 +5093,6 @@
if (!cmpResourceSupply)
return false;
- // Formation controllers should always respond to commands
- // (then the individual units can make up their own minds)
- if (this.IsFormationController())
- return true;
-
// Verify that we're able to respond to Gather commands
var cmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer);
if (!cmpResourceGatherer)
@@ -5825,11 +5112,6 @@
UnitAI.prototype.CanHeal = function(target)
{
- // Formation controllers should always respond to commands
- // (then the individual units can make up their own minds)
- if (this.IsFormationController())
- return true;
-
// Verify that we're able to respond to Heal commands
var cmpHeal = Engine.QueryInterface(this.entity, IID_Heal);
if (!cmpHeal)
@@ -5868,10 +5150,6 @@
{
if (this.IsTurret())
return false;
- // Formation controllers should always respond to commands
- // (then the individual units can make up their own minds)
- if (this.IsFormationController())
- return true;
// Verify that we're able to respond to ReturnResource commands
var cmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer);
@@ -5905,10 +5183,6 @@
{
if (this.IsTurret())
return false;
- // Formation controllers should always respond to commands
- // (then the individual units can make up their own minds)
- if (this.IsFormationController())
- return true;
// Verify that we're able to respond to Trade commands
var cmpTrader = Engine.QueryInterface(this.entity, IID_Trader);
@@ -5919,10 +5193,6 @@
{
if (this.IsTurret())
return false;
- // Formation controllers should always respond to commands
- // (then the individual units can make up their own minds)
- if (this.IsFormationController())
- return true;
// Verify that we're able to respond to Repair (Builder) commands
var cmpBuilder = Engine.QueryInterface(this.entity, IID_Builder);
@@ -5958,15 +5228,6 @@
return (cmpPack && cmpPack.IsPacking());
};
-//// Formation specific functions ////
-
-UnitAI.prototype.IsAttackingAsFormation = function()
-{
- var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
- return cmpAttack && cmpAttack.CanAttackAsFormation()
- && this.GetCurrentState() == "FORMATIONCONTROLLER.COMBAT.ATTACKING";
-};
-
//// Animal specific functions ////
UnitAI.prototype.MoveRandomly = function(distance)
@@ -5997,14 +5258,7 @@
var tz = pos.z + randFloat(-1, 1) * jitter;
var cmpMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
- cmpMotion.MoveToPointRange(tx, tz, distance, distance);
-};
-
-UnitAI.prototype.SetFacePointAfterMove = function(val)
-{
- var cmpMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
- if (cmpMotion)
- cmpMotion.SetFacePointAfterMove(val);
+ cmpMotion.SetNewDestinationAsPosition(tx, tz, distance, true);
};
UnitAI.prototype.AttackEntitiesByPreference = function(ents)
@@ -6054,37 +5308,6 @@
return this.RespondToTargetedEntities(entsWithoutPref);
};
-/**
- * Call obj.funcname(args) on UnitAI components of all formation members.
- */
-UnitAI.prototype.CallMemberFunction = function(funcname, args)
-{
- var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation);
- if (!cmpFormation)
- return;
-
- cmpFormation.GetMembers().forEach(ent => {
- var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
- cmpUnitAI[funcname].apply(cmpUnitAI, args);
- });
-};
-
-/**
- * Call obj.functname(args) on UnitAI components of all formation members,
- * and return true if all calls return true.
- */
-UnitAI.prototype.TestAllMemberFunction = function(funcname, args)
-{
- var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation);
- if (!cmpFormation)
- return false;
-
- return cmpFormation.GetMembers().every(ent => {
- var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
- return cmpUnitAI[funcname].apply(cmpUnitAI, args);
- });
-};
-
UnitAI.prototype.UnitFsm = new FSM(UnitAI.prototype.UnitFsmSpec);
Engine.RegisterComponentType(IID_UnitAI, "UnitAI", UnitAI);
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
@@ -241,33 +241,43 @@
cmpPosition.MoveTo(pos.x, pos.z);
};
-UnitMotionFlying.prototype.MoveToPointRange = function(x, z, minRange, maxRange)
+UnitMotionFlying.prototype.SetNewDestinationAsPosition = function(x, z, range)
{
this.hasTarget = true;
this.landing = false;
this.reachedTarget = false;
this.targetX = x;
this.targetZ = z;
- this.targetMinRange = minRange;
- this.targetMaxRange = maxRange;
+ this.targetMinRange = range;
+ this.targetMaxRange = range;
+
+ // 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;
};
-UnitMotionFlying.prototype.MoveToTargetRange = function(target, minRange, maxRange)
+UnitMotionFlying.prototype.SetNewDestinationAsEntity = function(target, range)
{
var cmpTargetPosition = Engine.QueryInterface(target, IID_Position);
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;
this.reachedTarget = false;
this.targetX = targetPos.x;
this.targetZ = targetPos.y;
- this.targetMinRange = minRange;
- this.targetMaxRange = maxRange;
+ this.targetMinRange = range;
+ this.targetMaxRange = range;
return 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;
};
@@ -315,17 +325,17 @@
return this.speed;
};
-UnitMotionFlying.prototype.FaceTowardsPoint = function(x, z)
+UnitMotionFlying.prototype.IsTryingToMove = function()
{
- // Ignore this - angle is controlled by the target-seeking code instead
-};
+ return false;
+}
-UnitMotionFlying.prototype.SetFacePointAfterMove = function()
+UnitMotionFlying.prototype.FaceTowardsPoint = function(x, z)
{
// 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/interfaces/GroupWalkManager.js
===================================================================
--- /dev/null
+++ binaries/data/mods/public/simulation/components/interfaces/GroupWalkManager.js
@@ -0,0 +1 @@
+Engine.RegisterInterface("GroupWalkManager");
Index: binaries/data/mods/public/simulation/components/interfaces/Messages.js
===================================================================
--- binaries/data/mods/public/simulation/components/interfaces/Messages.js
+++ binaries/data/mods/public/simulation/components/interfaces/Messages.js
@@ -2,7 +2,6 @@
* Message of the form { "entity": number, "newentity": number }
* sent when one entity is changed to another:
* - from Foundation component when a building construction is done
- * - from Formation component
* - from Health component when an entity died and should remain as a resource
* - from Promotion component when a unit is promoted
* - from Mirage component when a fogged entity is re-discovered
Index: binaries/data/mods/public/simulation/components/tests/test_Attack.js
===================================================================
--- binaries/data/mods/public/simulation/components/tests/test_Attack.js
+++ binaries/data/mods/public/simulation/components/tests/test_Attack.js
@@ -4,7 +4,6 @@
Engine.LoadComponentScript("interfaces/AuraManager.js");
Engine.LoadComponentScript("interfaces/Capturable.js");
Engine.LoadComponentScript("interfaces/TechnologyManager.js");
-Engine.LoadComponentScript("interfaces/Formation.js");
Engine.LoadComponentScript("interfaces/Attack.js");
Engine.LoadComponentScript("Attack.js");
Index: binaries/data/mods/public/simulation/components/tests/test_UnitAI.js
===================================================================
--- binaries/data/mods/public/simulation/components/tests/test_UnitAI.js
+++ binaries/data/mods/public/simulation/components/tests/test_UnitAI.js
@@ -1,319 +1,175 @@
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();
+Engine.LoadComponentScript("interfaces/Timer.js");
+Engine.LoadComponentScript("interfaces/Heal.js");
+Engine.LoadComponentScript("interfaces/Sound.js");
+Engine.LoadComponentScript("interfaces/GarrisonHolder.js");
+Engine.LoadComponentScript("interfaces/DamageReceiver.js");
+Engine.LoadComponentScript("interfaces/Pack.js");
+
+Engine.LoadHelperScript("Sound.js");
- var playerEntity = 5;
- var unit = 10;
- var enemy = 20;
- var controller = 30;
+const PLAYER_ENTITY = 2;
+const UNIT_ID = 3;
+const TARGET_ENTITY = 4;
+var lastAnimationSet = "";
+function SetupMocks()
+{
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"; },
+ GetCurrentTemplateName: function(ent) { return "units/gaul_infantry_spearman_b"; },
});
- 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_ID, IID_Sound, {
+ PlaySoundGroup: function() {},
});
- 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_ID, IID_Position, {
+ "IsInWorld" : function() { return true; },
+ "GetPosition" : function() { return new Vector2D(0,0); }
});
- AddMock(unit, IID_Vision, {
- GetRange: function() { return 10; },
+ AddMock(UNIT_ID, IID_UnitMotion, {
+ GetTopSpeedRatio : function() { return 0; },
+ SetSpeed: function() {},
});
- 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; },
+ AddMock(UNIT_ID, IID_DamageReceiver, {
+ SetInvulnerability : function() {},
});
- 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(UNIT_ID, IID_Pack, {
+ Pack : function() {},
+ Unpack : function() { },
});
- AddMock(controller, IID_UnitMotion, {
- SetSpeed: function(speed) { },
- MoveToPointRange: function(x, z, minRange, maxRange) { },
- GetPassabilityClassName: function() { return "default"; },
+ AddMock(UNIT_ID, IID_Visual, {
+ SelectAnimation : function(name) { lastAnimationSet = name; },
+ SetVariant : function(key, name) { },
});
-
- 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]; },
+ 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; },
+=======
+ GetCurrentTemplateName: function(ent) { return "units/gaul_infantry_spearman_b"; },
});
- 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);
+ AddMock(SYSTEM_ENTITY, IID_PlayerManager, {
+ GetPlayerByID: function(id) { return PLAYER_ENTITY; },
+ GetNumPlayers: function() { return 1; },
+ });*/
+}
- var unitAI = ConstructComponent(unit + i, "UnitAI", { "FormationController": "false", "DefaultStance": "aggressive" });
+// The intention of this test is to validate that unitAI states that select an animation correctly reset it when leaving
+// This tests on "unevaled" FSM state instead of trying to get every state because it's basically a nightmare to get 100% coverage in UnitAI
+// And this seems to be good enough to actually detect the bugs.
+function testAnimationsAreReset()
+{
+ ResetState();
+ SetupMocks();
- 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; },
- });
+ let cmpUnitAI = ConstructComponent(UNIT_ID, "UnitAI", { "DefaultStance": "aggressive" });
- unitAI.OnCreate();
+ cmpUnitAI.OnCreate();
+ TS_ASSERT_EQUALS(cmpUnitAI.UnitFsm.GetCurrentState(cmpUnitAI), "INDIVIDUAL.IDLE");
- unitAI.SetupRangeQuery(1);
+ cmpUnitAI.order = {"data" : { "targetClasses" : [], "target" : TARGET_ENTITY }};
- unitAIs.push(unitAI);
+ let TestForReset = function(cmpUnitAI, totest)
+ {
+ let shouldReset = false;
+ for (let fc in totest)
+ {
+ if (fc === "leave")
+ continue;
+
+ let stringified = uneval(totest[fc]);
+ let pos = stringified.search("SelectAnimation");
+ if (pos !== -1)
+ {
+ let animation = stringified.substr(pos, stringified.indexOf(")", pos) - pos) + ")";
+ if (animation.search("idle") === -1 && animation.search(", true") === -1)
+ shouldReset = true;
+ }
+ }
+ if (shouldReset)
+ {
+ if (!totest.leave)
+ {
+ TS_FAIL("No leave");
+ return false;
+ }
+
+ let doesReset = false;
+ let stringified = uneval(totest.leave);
+ let pos = stringified.search("SelectAnimation");
+ if (pos !== -1)
+ {
+ let animation = stringified.substr(pos, stringified.indexOf(")", pos) - pos) + ")";
+ if (animation.search("idle") !== -1)
+ doesReset = true;
+ }
+ if (!doesReset)
+ {
+ TS_FAIL("No reset in the leave");
+ return false;
+ }
+ }
+ return true;
}
- // 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 (var ent of unitAIs)
- TS_ASSERT_EQUALS(unitAI.fsmStateName, "INDIVIDUAL.COMBAT.ATTACKING");
-
- controllerAI.MoveIntoFormation({"name": "Circle"});
-
- // let all units be in position
- for (var ent of unitAIs)
- controllerFormation.SetInPosition(ent);
-
- for (var ent of unitAIs)
- TS_ASSERT_EQUALS(unitAI.fsmStateName, "INDIVIDUAL.COMBAT.ATTACKING");
+ for (let i in cmpUnitAI.UnitFsmSpec.INDIVIDUAL)
+ {
+ // skip the default "Enter" states and such.
+ if (typeof cmpUnitAI.UnitFsmSpec.INDIVIDUAL[i] === "function")
+ continue;
+
+ // skip IDLE because the following dumb test doesn't detect it properly.
+ if (i === "IDLE")
+ continue;
+
+ // check if this state has 2 levels or 3 levels
+ // eg INDIVIDUAL.FLEEING or INDIVIDUAL.COMBAT.SOMETHING
+ let hasChildren = false;
+ for (let child in cmpUnitAI.UnitFsmSpec.INDIVIDUAL[i])
+ if (typeof cmpUnitAI.UnitFsmSpec.INDIVIDUAL[i][child] !== "function")
+ {
+ hasChildren = true;
+ break;
+ }
+ if (hasChildren)
+ {
+ for (let child in cmpUnitAI.UnitFsmSpec.INDIVIDUAL[i])
+ {
+ if (!TestForReset(cmpUnitAI, cmpUnitAI.UnitFsmSpec.INDIVIDUAL[i][child]))
+ warn("Failed in " + i + " substate " + child);
+ }
+ }
+ else
+ if (!TestForReset(cmpUnitAI, cmpUnitAI.UnitFsmSpec.INDIVIDUAL[i]))
+ warn("Failed in " + i);
+ }
- controllerFormation.Disband();
+// TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Component/Value", 5, targetEnt), 15);
}
-TestFormationExiting(0);
-TestFormationExiting(1);
-TestFormationExiting(2);
-
-TestMoveIntoFormationWhileAttacking();
+testAnimationsAreReset();
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 }
],
"auraName": "Edict of Ashoka",
"auraDescription": "All traders in range +20% speed.",
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/data/civs/athen.json
===================================================================
--- binaries/data/mods/public/simulation/data/civs/athen.json
+++ binaries/data/mods/public/simulation/data/civs/athen.json
@@ -133,16 +133,7 @@
"Formations":
[
"formations/null",
- "formations/box",
- "formations/column_closed",
- "formations/line_closed",
- "formations/column_open",
- "formations/line_open",
- "formations/flank",
- "formations/skirmish",
- "formations/wedge",
- "formations/battle_line",
- "formations/phalanx"
+ "formations/column_open"
],
"AINames":
[
Index: binaries/data/mods/public/simulation/data/civs/brit.json
===================================================================
--- binaries/data/mods/public/simulation/data/civs/brit.json
+++ binaries/data/mods/public/simulation/data/civs/brit.json
@@ -115,15 +115,7 @@
"Formations":
[
"formations/null",
- "formations/box",
- "formations/column_closed",
- "formations/line_closed",
- "formations/column_open",
- "formations/line_open",
- "formations/flank",
- "formations/skirmish",
- "formations/wedge",
- "formations/battle_line"
+ "formations/column_open"
],
"AINames":
[
Index: binaries/data/mods/public/simulation/data/civs/cart.json
===================================================================
--- binaries/data/mods/public/simulation/data/civs/cart.json
+++ binaries/data/mods/public/simulation/data/civs/cart.json
@@ -136,15 +136,7 @@
"Formations":
[
"formations/null",
- "formations/box",
- "formations/column_closed",
- "formations/line_closed",
- "formations/column_open",
- "formations/line_open",
- "formations/flank",
- "formations/skirmish",
- "formations/wedge",
- "formations/battle_line"
+ "formations/column_open"
],
"AINames":
[
Index: binaries/data/mods/public/simulation/data/civs/gaul.json
===================================================================
--- binaries/data/mods/public/simulation/data/civs/gaul.json
+++ binaries/data/mods/public/simulation/data/civs/gaul.json
@@ -111,15 +111,7 @@
"Formations":
[
"formations/null",
- "formations/box",
- "formations/column_closed",
- "formations/line_closed",
- "formations/column_open",
- "formations/line_open",
- "formations/flank",
- "formations/skirmish",
- "formations/wedge",
- "formations/battle_line"
+ "formations/column_open"
],
"AINames":
[
Index: binaries/data/mods/public/simulation/data/civs/iber.json
===================================================================
--- binaries/data/mods/public/simulation/data/civs/iber.json
+++ binaries/data/mods/public/simulation/data/civs/iber.json
@@ -109,15 +109,7 @@
"Formations":
[
"formations/null",
- "formations/box",
- "formations/column_closed",
- "formations/line_closed",
- "formations/column_open",
- "formations/line_open",
- "formations/flank",
- "formations/skirmish",
- "formations/wedge",
- "formations/battle_line"
+ "formations/column_open"
],
"AINames":
[
Index: binaries/data/mods/public/simulation/data/civs/mace.json
===================================================================
--- binaries/data/mods/public/simulation/data/civs/mace.json
+++ binaries/data/mods/public/simulation/data/civs/mace.json
@@ -138,17 +138,7 @@
"Formations":
[
"formations/null",
- "formations/box",
- "formations/column_closed",
- "formations/line_closed",
- "formations/column_open",
- "formations/line_open",
- "formations/flank",
- "formations/skirmish",
- "formations/wedge",
- "formations/battle_line",
- "formations/phalanx",
- "formations/syntagma"
+ "formations/column_open"
],
"AINames":
[
Index: binaries/data/mods/public/simulation/data/civs/maur.json
===================================================================
--- binaries/data/mods/public/simulation/data/civs/maur.json
+++ binaries/data/mods/public/simulation/data/civs/maur.json
@@ -123,15 +123,7 @@
"Formations":
[
"formations/null",
- "formations/box",
- "formations/column_closed",
- "formations/line_closed",
- "formations/column_open",
- "formations/line_open",
- "formations/flank",
- "formations/skirmish",
- "formations/wedge",
- "formations/battle_line"
+ "formations/column_open"
],
"AINames":
[
Index: binaries/data/mods/public/simulation/data/civs/pers.json
===================================================================
--- binaries/data/mods/public/simulation/data/civs/pers.json
+++ binaries/data/mods/public/simulation/data/civs/pers.json
@@ -128,15 +128,7 @@
"Formations":
[
"formations/null",
- "formations/box",
- "formations/column_closed",
- "formations/line_closed",
- "formations/column_open",
- "formations/line_open",
- "formations/flank",
- "formations/skirmish",
- "formations/wedge",
- "formations/battle_line"
+ "formations/column_open"
],
"AINames":
[
Index: binaries/data/mods/public/simulation/data/civs/ptol.json
===================================================================
--- binaries/data/mods/public/simulation/data/civs/ptol.json
+++ binaries/data/mods/public/simulation/data/civs/ptol.json
@@ -137,17 +137,7 @@
"Formations":
[
"formations/null",
- "formations/box",
- "formations/column_closed",
- "formations/line_closed",
- "formations/column_open",
- "formations/line_open",
- "formations/flank",
- "formations/skirmish",
- "formations/wedge",
- "formations/battle_line",
- "formations/phalanx",
- "formations/syntagma"
+ "formations/column_open"
],
"AINames":
[
Index: binaries/data/mods/public/simulation/data/civs/rome.json
===================================================================
--- binaries/data/mods/public/simulation/data/civs/rome.json
+++ binaries/data/mods/public/simulation/data/civs/rome.json
@@ -113,16 +113,7 @@
"Formations":
[
"formations/null",
- "formations/box",
- "formations/column_closed",
- "formations/line_closed",
- "formations/column_open",
- "formations/line_open",
- "formations/flank",
- "formations/skirmish",
- "formations/wedge",
- "formations/battle_line",
- "formations/testudo"
+ "formations/column_open"
],
"AINames":
[
Index: binaries/data/mods/public/simulation/data/civs/sele.json
===================================================================
--- binaries/data/mods/public/simulation/data/civs/sele.json
+++ binaries/data/mods/public/simulation/data/civs/sele.json
@@ -134,17 +134,7 @@
"Formations":
[
"formations/null",
- "formations/box",
- "formations/column_closed",
- "formations/line_closed",
- "formations/column_open",
- "formations/line_open",
- "formations/flank",
- "formations/skirmish",
- "formations/wedge",
- "formations/battle_line",
- "formations/phalanx",
- "formations/syntagma"
+ "formations/column_open"
],
"AINames":
[
Index: binaries/data/mods/public/simulation/data/civs/spart.json
===================================================================
--- binaries/data/mods/public/simulation/data/civs/spart.json
+++ binaries/data/mods/public/simulation/data/civs/spart.json
@@ -129,16 +129,7 @@
"Formations":
[
"formations/null",
- "formations/box",
- "formations/column_closed",
- "formations/line_closed",
- "formations/column_open",
- "formations/line_open",
- "formations/flank",
- "formations/skirmish",
- "formations/wedge",
- "formations/battle_line",
- "formations/phalanx"
+ "formations/column_open"
],
"AINames":
[
Index: binaries/data/mods/public/simulation/data/pathfinder.xml
===================================================================
--- binaries/data/mods/public/simulation/data/pathfinder.xml
+++ binaries/data/mods/public/simulation/data/pathfinder.xml
@@ -17,7 +17,7 @@
pathfinding
2
1.0
- 4.0
+ 3.0
pathfinding
Index: binaries/data/mods/public/simulation/helpers/Commands.js
===================================================================
--- binaries/data/mods/public/simulation/helpers/Commands.js
+++ binaries/data/mods/public/simulation/helpers/Commands.js
@@ -141,9 +141,11 @@
"walk": function(player, cmd, data)
{
- GetFormationUnitAIs(data.entities, player).forEach(cmpUnitAI => {
- cmpUnitAI.Walk(cmd.x, cmd.z, cmd.queued);
- });
+ let unitAIs = CreateGroupWalkOrderIfNecessary(data.entities, cmd.x, cmd.z, 14, cmd.queued);
+ // Let non-formed entities walk, the others will group-walk to the target and stop there.
+ unitAIs[0].forEach(cmpUnitAI => {
+ cmpUnitAI.Walk(cmd.x, cmd.z, cmd.queued);
+ });
},
"walk-to-range": function(player, cmd, data)
@@ -159,9 +161,11 @@
"attack-walk": function(player, cmd, data)
{
- GetFormationUnitAIs(data.entities, player).forEach(cmpUnitAI => {
- cmpUnitAI.WalkAndFight(cmd.x, cmd.z, cmd.targetClasses, cmd.queued);
- });
+ let unitAIs = CreateGroupWalkOrderIfNecessary(data.entities, cmd.x, cmd.z, 14, cmd.queued);
+ for (let i in unitAIs)
+ unitAIs[i].forEach(cmpUnitAI => {
+ cmpUnitAI.WalkAndFight(cmd.x, cmd.z, cmd.targetClasses, cmd.queued || +i);
+ });
},
"attack": function(player, cmd, data)
@@ -171,16 +175,23 @@
let allowCapture = cmd.allowCapture || cmd.allowCapture == null;
- GetFormationUnitAIs(data.entities, player).forEach(cmpUnitAI => {
- cmpUnitAI.Attack(cmd.target, cmd.queued, allowCapture);
- });
+ let pos = Engine.QueryInterface(cmd.target, IID_Position);
+ if (!pos)
+ return;
+ let unitAIs = CreateGroupWalkOrderIfNecessary(data.entities, pos.GetPosition2D().x, pos.GetPosition2D().y, 14, cmd.queued);
+ for (let i in unitAIs)
+ unitAIs[i].forEach(cmpUnitAI => {
+ cmpUnitAI.Attack(cmd.target, cmd.queued || +i, allowCapture);
+ });
},
"patrol": function(player, cmd, data)
{
- GetFormationUnitAIs(data.entities, player).forEach(cmpUnitAI =>
- cmpUnitAI.Patrol(cmd.x, cmd.z, cmd.targetClasses, cmd.queued)
- );
+ let unitAIs = CreateGroupWalkOrderIfNecessary(data.entities, cmd.x, cmd.z, 14, cmd.queued);
+ for (let i in unitAIs)
+ unitAIs[i].forEach(cmpUnitAI => {
+ cmpUnitAI.Patrol(cmd.x, cmd.z, cmd.targetClasses, cmd.queued || +i)
+ });
},
"heal": function(player, cmd, data)
@@ -188,9 +199,14 @@
if (g_DebugCommands && !(IsOwnedByPlayer(player, cmd.target) || IsOwnedByAllyOfPlayer(player, cmd.target)))
warn("Invalid command: heal target is not owned by player "+player+" or their ally: "+uneval(cmd));
- GetFormationUnitAIs(data.entities, player).forEach(cmpUnitAI => {
- cmpUnitAI.Heal(cmd.target, cmd.queued);
- });
+ let pos = Engine.QueryInterface(cmd.target, IID_Position);
+ if (!pos)
+ return;
+ let unitAIs = CreateGroupWalkOrderIfNecessary(data.entities, pos.GetPosition2D().x, pos.GetPosition2D().y, 14, cmd.queued);
+ for (let i in unitAIs)
+ unitAIs[i].forEach(cmpUnitAI => {
+ cmpUnitAI.Heal(cmd.target, cmd.queued || +i);
+ });
},
"repair": function(player, cmd, data)
@@ -199,9 +215,14 @@
if (g_DebugCommands && !IsOwnedByAllyOfPlayer(player, cmd.target))
warn("Invalid command: repair target is not owned by ally of player "+player+": "+uneval(cmd));
- GetFormationUnitAIs(data.entities, player).forEach(cmpUnitAI => {
- cmpUnitAI.Repair(cmd.target, cmd.autocontinue, cmd.queued);
- });
+ let pos = Engine.QueryInterface(cmd.target, IID_Position);
+ if (!pos)
+ return;
+ let unitAIs = CreateGroupWalkOrderIfNecessary(data.entities, pos.GetPosition2D().x, pos.GetPosition2D().y, 14, cmd.queued);
+ for (let i in unitAIs)
+ unitAIs[i].forEach(cmpUnitAI => {
+ cmpUnitAI.Repair(cmd.target, cmd.autocontinue, cmd.queued || +i);
+ });
},
"gather": function(player, cmd, data)
@@ -209,16 +230,23 @@
if (g_DebugCommands && !(IsOwnedByPlayer(player, cmd.target) || IsOwnedByGaia(cmd.target)))
warn("Invalid command: resource is not owned by gaia or player "+player+": "+uneval(cmd));
- GetFormationUnitAIs(data.entities, player).forEach(cmpUnitAI => {
- cmpUnitAI.Gather(cmd.target, cmd.queued);
- });
+ let pos = Engine.QueryInterface(cmd.target, IID_Position);
+ if (!pos)
+ return;
+ let unitAIs = CreateGroupWalkOrderIfNecessary(data.entities, pos.GetPosition2D().x, pos.GetPosition2D().y, 14, cmd.queued);
+ for (let i in unitAIs)
+ unitAIs[i].forEach(cmpUnitAI => {
+ cmpUnitAI.Gather(cmd.target, cmd.queued || +i);
+ });
},
"gather-near-position": function(player, cmd, data)
{
- GetFormationUnitAIs(data.entities, player).forEach(cmpUnitAI => {
- cmpUnitAI.GatherNearPosition(cmd.x, cmd.z, cmd.resourceType, cmd.resourceTemplate, cmd.queued);
- });
+ let unitAIs = CreateGroupWalkOrderIfNecessary(data.entities, cmd.x, cmd.z, 14, cmd.queued);
+ for (let i in unitAIs)
+ unitAIs[i].forEach(cmpUnitAI => {
+ cmpUnitAI.GatherNearPosition(cmd.x, cmd.z, cmd.resourceType, cmd.resourceTemplate, cmd.queued || +i);
+ });
},
"returnresource": function(player, cmd, data)
@@ -226,9 +254,14 @@
if (g_DebugCommands && !IsOwnedByPlayer(player, cmd.target))
warn("Invalid command: dropsite is not owned by player "+player+": "+uneval(cmd));
- GetFormationUnitAIs(data.entities, player).forEach(cmpUnitAI => {
- cmpUnitAI.ReturnResource(cmd.target, cmd.queued);
- });
+ let pos = Engine.QueryInterface(cmd.target, IID_Position);
+ if (!pos)
+ return;
+ let unitAIs = CreateGroupWalkOrderIfNecessary(data.entities, pos.GetPosition2D().x, pos.GetPosition2D().y, 14, cmd.queued);
+ for (let i in unitAIs)
+ unitAIs[i].forEach(cmpUnitAI => {
+ cmpUnitAI.ReturnResource(cmd.target, cmd.queued || +i);
+ });
},
"back-to-work": function(player, cmd, data)
@@ -441,9 +474,14 @@
return;
}
- GetFormationUnitAIs(data.entities, player).forEach(cmpUnitAI => {
- cmpUnitAI.Garrison(cmd.target, cmd.queued);
- });
+ let pos = Engine.QueryInterface(cmd.target, IID_Position);
+ if (!pos)
+ return;
+ let unitAIs = CreateGroupWalkOrderIfNecessary(data.entities, pos.GetPosition2D().x, pos.GetPosition2D().y, 14, cmd.queued);
+ for (let i in unitAIs)
+ unitAIs[i].forEach(cmpUnitAI => {
+ cmpUnitAI.Garrison(cmd.target, cmd.queued || +i);
+ });
},
"guard": function(player, cmd, data)
@@ -456,15 +494,22 @@
return;
}
- GetFormationUnitAIs(data.entities, player).forEach(cmpUnitAI => {
- cmpUnitAI.Guard(cmd.target, cmd.queued);
- });
+ let pos = Engine.QueryInterface(cmd.target, IID_Position);
+ if (!pos)
+ return;
+ let unitAIs = CreateGroupWalkOrderIfNecessary(data.entities, pos.GetPosition2D().x, pos.GetPosition2D().y, 14, cmd.queued);
+ for (let i in unitAIs)
+ unitAIs[i].forEach(cmpUnitAI => {
+ cmpUnitAI.Guard(cmd.target, cmd.queued || +i);
+ });
},
"stop": function(player, cmd, data)
{
- GetFormationUnitAIs(data.entities, player).forEach(cmpUnitAI => {
- cmpUnitAI.Stop(cmd.queued);
+ data.entities.forEach(ent => {
+ let cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
+ if (cmpUnitAI)
+ cmpUnitAI.Stop(cmd.queued);
});
},
@@ -556,8 +601,10 @@
"formation": function(player, cmd, data)
{
- GetFormationUnitAIs(data.entities, player, cmd.name).forEach(cmpUnitAI => {
- cmpUnitAI.MoveIntoFormation(cmd);
+ data.entities.forEach(ent => {
+ let cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
+ if (cmpUnitAI)
+ cmpUnitAI.SetFormationTemplate(cmd.name);
});
},
@@ -606,8 +653,10 @@
"setup-trade-route": function(player, cmd, data)
{
- GetFormationUnitAIs(data.entities, player).forEach(cmpUnitAI => {
- cmpUnitAI.SetupTradeRoute(cmd.target, cmd.source, cmd.route, cmd.queued);
+ data.entities.forEach(ent => {
+ let cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
+ if (cmpUnitAI)
+ cmpUnitAI.SetupTradeRoute(cmd.target, cmd.source, cmd.route, cmd.queued);
});
},
@@ -835,32 +884,6 @@
}
/**
- * Get some information about the formations used by entities.
- * The entities must have a UnitAI component.
- */
-function ExtractFormations(ents)
-{
- var entities = []; // subset of ents that have UnitAI
- var members = {}; // { formationentity: [ent, ent, ...], ... }
- for (let ent of ents)
- {
- var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
- var fid = cmpUnitAI.GetFormationController();
- if (fid != INVALID_ENTITY)
- {
- if (!members[fid])
- members[fid] = [];
- members[fid].push(ent);
- }
- entities.push(ent);
- }
-
- var ids = [ id for (id in members) ];
-
- return { "entities": entities, "members": members, "ids": ids };
-}
-
-/**
* Tries to find the best angle to put a dock at a given position
* Taken from GuiInterface.js
*/
@@ -1360,226 +1383,62 @@
}
}
-/**
- * Remove the given list of entities from their current formations.
- */
-function RemoveFromFormation(ents)
-{
- var formation = ExtractFormations(ents);
- for (var fid in formation.members)
- {
- var cmpFormation = Engine.QueryInterface(+fid, IID_Formation);
- if (cmpFormation)
- cmpFormation.RemoveMembers(formation.members[fid]);
- }
-}
-
-/**
- * Returns a list of UnitAI components, each belonging either to a
- * selected unit or to a formation entity for groups of the selected units.
- */
-function GetFormationUnitAIs(ents, player, formationTemplate)
+function CreateGroupWalkOrderIfNecessary(ents, x, z, range, queued)
{
- // If an individual was selected, remove it from any formation
- // and command it individually
- if (ents.length == 1)
- {
- // Skip unit if it has no UnitAI
- var cmpUnitAI = Engine.QueryInterface(ents[0], IID_UnitAI);
- if (!cmpUnitAI)
- return [];
-
- RemoveFromFormation(ents);
-
- return [ cmpUnitAI ];
- }
-
- // Separate out the units that don't support the chosen formation
- var formedEnts = [];
- var nonformedUnitAIs = [];
+ // First, loop through units and check if all of them share a single non-null formation.
+ let formationTemplate = null;
for (let ent of ents)
{
- // Skip units with no UnitAI or no position
- var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
- var cmpPosition = Engine.QueryInterface(ent, IID_Position);
- if (!cmpUnitAI || !cmpPosition || !cmpPosition.IsInWorld())
- continue;
-
- var cmpIdentity = Engine.QueryInterface(ent, IID_Identity);
- // TODO: We only check if the formation is usable by some units
- // if we move them to it. We should check if we can use formations
- // for the other cases.
- var nullFormation = (formationTemplate || cmpUnitAI.GetLastFormationTemplate()) == "formations/null";
- if (!nullFormation && cmpIdentity && cmpIdentity.CanUseFormation(formationTemplate || "formations/null"))
- formedEnts.push(ent);
- else
+ let cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
+ if (!cmpUnitAI)
+ {
+ formationTemplate = "formations/null";
+ break;
+ }
+ if (!formationTemplate)
+ formationTemplate = cmpUnitAI.GetFormationTemplate();
+ if (formationTemplate == "formations/null")
+ break;
+ else if (cmpUnitAI.GetFormationTemplate() !== formationTemplate)
{
- if (nullFormation)
- cmpUnitAI.SetLastFormationTemplate("formations/null");
- nonformedUnitAIs.push(cmpUnitAI);
+ formationTemplate = "formations/null";
+ break;
}
}
- if (formedEnts.length == 0)
- {
- // No units support the foundation - return all the others
- return nonformedUnitAIs;
- }
+ let nonFormableUnitAIs = [];
+ let formableEntsID = [];
+ let formableEntsAI = [];
- // Find what formations the formationable selected entities are currently in
- var formation = ExtractFormations(formedEnts);
+ // don't create a walk together order if this is a queued order because that's just going to be weird
+ let createGroupOrder = queued === false && formationTemplate !== "formations/null";
- var formationUnitAIs = [];
- if (formation.ids.length == 1)
- {
- // Selected units either belong to this formation or have no formation
- // Check that all its members are selected
- var fid = formation.ids[0];
- var cmpFormation = Engine.QueryInterface(+fid, IID_Formation);
- if (cmpFormation && cmpFormation.GetMemberCount() == formation.members[fid].length
- && cmpFormation.GetMemberCount() == formation.entities.length)
- {
- cmpFormation.DeleteTwinFormations();
- // The whole formation was selected, so reuse its controller for this command
- formationUnitAIs = [Engine.QueryInterface(+fid, IID_UnitAI)];
- if (formationTemplate && CanMoveEntsIntoFormation(formation.entities, formationTemplate))
- cmpFormation.LoadFormation(formationTemplate);
- }
- }
-
- if (!formationUnitAIs.length)
+ for (let ent of ents)
{
- // We need to give the selected units a new formation controller
-
- // Remove selected units from their current formation
- for (var fid in formation.members)
- {
- var cmpFormation = Engine.QueryInterface(+fid, IID_Formation);
- if (cmpFormation)
- cmpFormation.RemoveMembers(formation.members[fid]);
- }
+ let cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
+ let cmpPosition = Engine.QueryInterface(ent, IID_Position);
+ if (!cmpUnitAI || !cmpPosition || !cmpPosition.IsInWorld())
+ continue;
- // TODO replace the fixed 60 with something sensible, based on vision range f.e.
- var formationSeparation = 60;
- var clusters = ClusterEntities(formation.entities, formationSeparation);
- var formationEnts = [];
- for (let cluster of clusters)
+ let cmpIdentity = Engine.QueryInterface(ent, IID_Identity);
+ if (createGroupOrder && cmpIdentity)// && cmpIdentity.CanUseFormation(formationTemplate))
{
- if (!formationTemplate || !CanMoveEntsIntoFormation(cluster, formationTemplate))
- {
- // get the most recently used formation, or default to line closed
- var lastFormationTemplate = undefined;
- for (let ent of cluster)
- {
- var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
- if (cmpUnitAI)
- {
- var template = cmpUnitAI.GetLastFormationTemplate();
- if (lastFormationTemplate === undefined)
- {
- lastFormationTemplate = template;
- }
- else if (lastFormationTemplate != template)
- {
- lastFormationTemplate = undefined;
- break;
- }
- }
- }
- if (lastFormationTemplate && CanMoveEntsIntoFormation(cluster, lastFormationTemplate))
- formationTemplate = lastFormationTemplate;
- else
- formationTemplate = "formations/null";
- }
-
- // Create the new controller
- var formationEnt = Engine.AddEntity(formationTemplate);
- var cmpFormation = Engine.QueryInterface(formationEnt, IID_Formation);
- formationUnitAIs.push(Engine.QueryInterface(formationEnt, IID_UnitAI));
- cmpFormation.SetFormationSeparation(formationSeparation);
- cmpFormation.SetMembers(cluster);
-
- for (let ent of formationEnts)
- cmpFormation.RegisterTwinFormation(ent);
-
- formationEnts.push(formationEnt);
- var cmpOwnership = Engine.QueryInterface(formationEnt, IID_Ownership);
- cmpOwnership.SetOwner(player);
+ formableEntsID.push(ent);
+ formableEntsAI.push(cmpUnitAI);
}
+ else
+ nonFormableUnitAIs.push(cmpUnitAI);
}
- return nonformedUnitAIs.concat(formationUnitAIs);
-}
-
-/**
- * Group a list of entities in clusters via single-links
- */
-function ClusterEntities(ents, separationDistance)
-{
- var clusters = [];
- if (!ents.length)
- return clusters;
-
- var distSq = separationDistance * separationDistance;
- var positions = [];
- // triangular matrix with the (squared) distances between the different clusters
- // the other half is not initialised
- var matrix = [];
- for (let i = 0; i < ents.length; ++i)
- {
- matrix[i] = [];
- clusters.push([ents[i]]);
- var cmpPosition = Engine.QueryInterface(ents[i], IID_Position);
- positions.push(cmpPosition.GetPosition2D());
- for (let j = 0; j < i; ++j)
- matrix[i][j] = positions[i].distanceToSquared(positions[j]);
- }
- while (clusters.length > 1)
+ // TODO: validate formation
+ if (createGroupOrder && formableEntsAI.length > 1)
{
- // search two clusters that are closer than the required distance
- var closeClusters = undefined;
-
- for (var i = matrix.length - 1; i >= 0 && !closeClusters; --i)
- for (var j = i - 1; j >= 0 && !closeClusters; --j)
- if (matrix[i][j] < distSq)
- closeClusters = [i,j];
-
- // if no more close clusters found, just return all found clusters so far
- if (!closeClusters)
- return clusters;
-
- // make a new cluster with the entities from the two found clusters
- var newCluster = clusters[closeClusters[0]].concat(clusters[closeClusters[1]]);
-
- // calculate the minimum distance between the new cluster and all other remaining
- // clusters by taking the minimum of the two distances.
- var distances = [];
- for (let i = 0; i < clusters.length; ++i)
- {
- if (i == closeClusters[1] || i == closeClusters[0])
- continue;
- var dist1 = matrix[closeClusters[1]][i] || matrix[i][closeClusters[1]];
- var dist2 = matrix[closeClusters[0]][i] || matrix[i][closeClusters[0]];
- distances.push(Math.min(dist1, dist2));
- }
- // remove the rows and columns in the matrix for the merged clusters,
- // and the clusters themselves from the cluster list
- clusters.splice(closeClusters[0],1);
- clusters.splice(closeClusters[1],1);
- matrix.splice(closeClusters[0],1);
- matrix.splice(closeClusters[1],1);
- for (let i = 0; i < matrix.length; ++i)
- {
- if (matrix[i].length > closeClusters[0])
- matrix[i].splice(closeClusters[0],1);
- if (matrix[i].length > closeClusters[1])
- matrix[i].splice(closeClusters[1],1);
- }
- // add a new row of distances to the matrix and the new cluster
- clusters.push(newCluster);
- matrix.push(distances);
+ // TODO: get position, get obstruction, that kind of stuff.
+ let cmpGroupWalkManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_GroupWalkManager);
+ let groupID = cmpGroupWalkManager.CreateGroup(formableEntsID, x, z, range, formationTemplate);
+ formableEntsAI.forEach(cmpUnitAI => { cmpUnitAI.GroupWalk(groupID); });
}
- return clusters;
+ return [nonFormableUnitAIs, formableEntsAI];
}
function GetFormationRequirements(formationTemplate)
@@ -1591,7 +1450,6 @@
return { "minCount": +template.Formation.RequiredMemberCount };
}
-
function CanMoveEntsIntoFormation(ents, formationTemplate)
{
// TODO: should check the player's civ is allowed to use this formation
Index: binaries/data/mods/public/simulation/helpers/FSM.js
===================================================================
--- binaries/data/mods/public/simulation/helpers/FSM.js
+++ binaries/data/mods/public/simulation/helpers/FSM.js
@@ -69,6 +69,10 @@
"leave": function() {
// Called when transitioning out of this state.
},
+
+ // Define a message handler that is an exact copy of another
+ // message handler defined in this Substate
+ "SomeMessageName": "AnotherMessageName",
},
// Define a new state which is an exact copy of another
@@ -194,16 +198,38 @@
{
state[key] = node[key];
}
- else if (key.match(/^[A-Z]+$/))
+ else if (typeof node[key] === "function")
+ {
+ // New message handler
+ newhandlers[key] = node[key];
+ }
+ else if (typeof node[key] === "object")
{
+ // new substate
state._refs[key] = (state._name ? state._name + "." : "") + key;
// (the rest of this will be handled later once we've grabbed
// all the event handlers)
}
- else
+ else if (typeof node[key] === "string")
{
- newhandlers[key] = node[key];
+ // this can be either a reference to a message handler, or to a state.
+ if (!node[node[key]] && !fsm.states[node[key]])
+ {
+ error("FSM node " + path.join(".") + " node " + key + " referring to unknown node/state " + node[key]);
+ return {};
+ }
+ else if (!!node[node[key]] && !!fsm.states[node[key]])
+ {
+ error("FSM node " + path.join(".") + " node " + key + " ambiguously referring to message handler or state " + node[key]);
+ return {};
+ }
+ if (!node[node[key]])
+ // new substate
+ state._refs[key] = (state._name ? state._name + "." : "") + key;
+ else
+ // New message handler
+ newhandlers[key] = node[node[key]];
}
}
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_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
@@ -39,11 +39,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
@@ -45,9 +45,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_formation.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_formation.xml
+++ binaries/data/mods/public/simulation/templates/template_formation.xml
@@ -35,11 +35,9 @@
2
aggressive
12.0
- true
true
- true
1.0
large
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
@@ -66,7 +66,7 @@
- 2.0
+ 1.0
1.0
1
@@ -100,19 +100,10 @@
2
aggressive
12.0
- false
true
- 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
@@ -55,7 +55,7 @@
150
- 2.0
+ 1.6
1.0
5
@@ -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_decorative.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_fauna_decorative.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_fauna_decorative.xml
@@ -23,7 +23,6 @@
0
passive
passive
- false
false
20
10.0
@@ -33,7 +32,6 @@
2
- false
8.0
unrestricted
default
Index: binaries/data/mods/public/simulation/templates/template_unit_fauna_hunt_defensive_elephant.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_fauna_hunt_defensive_elephant.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_fauna_hunt_defensive_elephant.xml
@@ -52,9 +52,6 @@
large
3.0
-
- 10.0
-
fauna/elephant_african_forest.xml
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
@@ -53,8 +53,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 @@
large
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
@@ -74,7 +74,6 @@
100
- 2.0
1.0
0.5
@@ -119,9 +118,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
@@ -61,9 +61,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
@@ -48,7 +48,6 @@
- 2.0
1.0
1
@@ -87,11 +86,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
@@ -49,7 +49,6 @@
1
- 2.0
1.0
0.5
@@ -85,10 +84,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_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
@@ -40,11 +40,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: libraries/win32/boost/include/boost/container/detail/flat_tree.hpp
===================================================================
--- /dev/null
+++ libraries/win32/boost/include/boost/container/detail/flat_tree.hpp
@@ -0,0 +1,982 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// (C) Copyright Ion Gaztanaga 2005-2015. Distributed under the Boost
+// Software License, Version 1.0. (See accompanying file
+// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+// See http://www.boost.org/libs/container for documentation.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#ifndef BOOST_CONTAINER_FLAT_TREE_HPP
+#define BOOST_CONTAINER_FLAT_TREE_HPP
+
+#ifndef BOOST_CONFIG_HPP
+# include
+#endif
+
+#if defined(BOOST_HAS_PRAGMA_ONCE)
+# pragma once
+#endif
+
+#include
+#include
+
+#include
+
+#include
+
+#include
+#include
+#include
+#include
+#include //algo_equal(), algo_lexicographical_compare
+#include
+#include
+#ifdef BOOST_CONTAINER_VECTOR_ITERATOR_IS_POINTER
+#include
+#endif
+#include
+#include
+#include
+#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
+#include
+#endif
+
+#include //pair
+
+namespace boost {
+namespace container {
+namespace container_detail {
+
+template
+class flat_tree_value_compare
+ : private Compare
+{
+ typedef Value first_argument_type;
+ typedef Value second_argument_type;
+ typedef bool return_type;
+ public:
+ flat_tree_value_compare()
+ : Compare()
+ {}
+
+ flat_tree_value_compare(const Compare &pred)
+ : Compare(pred)
+ {}
+
+ bool operator()(const Value& lhs, const Value& rhs) const
+ {
+ KeyOfValue key_extract;
+ return Compare::operator()(key_extract(lhs), key_extract(rhs));
+ }
+
+ const Compare &get_comp() const
+ { return *this; }
+
+ Compare &get_comp()
+ { return *this; }
+};
+
+template
+struct get_flat_tree_iterators
+{
+ #ifdef BOOST_CONTAINER_VECTOR_ITERATOR_IS_POINTER
+ typedef Pointer iterator;
+ typedef typename boost::intrusive::
+ pointer_traits::element_type iterator_element_type;
+ typedef typename boost::intrusive::
+ pointer_traits:: template
+ rebind_pointer::type const_iterator;
+ #else //BOOST_CONTAINER_VECTOR_ITERATOR_IS_POINTER
+ typedef typename boost::container::container_detail::
+ vec_iterator iterator;
+ typedef typename boost::container::container_detail::
+ vec_iterator const_iterator;
+ #endif //BOOST_CONTAINER_VECTOR_ITERATOR_IS_POINTER
+ typedef boost::container::reverse_iterator reverse_iterator;
+ typedef boost::container::reverse_iterator const_reverse_iterator;
+};
+
+template
+class flat_tree
+{
+ typedef boost::container::vector vector_t;
+ typedef Allocator allocator_t;
+ typedef allocator_traits allocator_traits_type;
+
+ public:
+ typedef flat_tree_value_compare value_compare;
+
+ private:
+ struct Data
+ //Inherit from value_compare to do EBO
+ : public value_compare
+ {
+ BOOST_COPYABLE_AND_MOVABLE(Data)
+
+ public:
+ Data()
+ : value_compare(), m_vect()
+ {}
+
+ explicit Data(const Data &d)
+ : value_compare(static_cast(d)), m_vect(d.m_vect)
+ {}
+
+ Data(BOOST_RV_REF(Data) d)
+ : value_compare(boost::move(static_cast(d))), m_vect(boost::move(d.m_vect))
+ {}
+
+ Data(const Data &d, const Allocator &a)
+ : value_compare(static_cast(d)), m_vect(d.m_vect, a)
+ {}
+
+ Data(BOOST_RV_REF(Data) d, const Allocator &a)
+ : value_compare(boost::move(static_cast(d))), m_vect(boost::move(d.m_vect), a)
+ {}
+
+ explicit Data(const Compare &comp)
+ : value_compare(comp), m_vect()
+ {}
+
+ Data(const Compare &comp, const allocator_t &alloc)
+ : value_compare(comp), m_vect(alloc)
+ {}
+
+ explicit Data(const allocator_t &alloc)
+ : value_compare(), m_vect(alloc)
+ {}
+
+ Data& operator=(BOOST_COPY_ASSIGN_REF(Data) d)
+ {
+ this->value_compare::operator=(d);
+ m_vect = d.m_vect;
+ return *this;
+ }
+
+ Data& operator=(BOOST_RV_REF(Data) d)
+ {
+ this->value_compare::operator=(boost::move(static_cast(d)));
+ m_vect = boost::move(d.m_vect);
+ return *this;
+ }
+
+ void swap(Data &d)
+ {
+ value_compare& mycomp = *this, & othercomp = d;
+ boost::adl_move_swap(mycomp, othercomp);
+ this->m_vect.swap(d.m_vect);
+ }
+
+ vector_t m_vect;
+ };
+
+ Data m_data;
+ BOOST_COPYABLE_AND_MOVABLE(flat_tree)
+
+ public:
+
+ typedef typename vector_t::value_type value_type;
+ typedef typename vector_t::pointer pointer;
+ typedef typename vector_t::const_pointer const_pointer;
+ typedef typename vector_t::reference reference;
+ typedef typename vector_t::const_reference const_reference;
+ typedef Key key_type;
+ typedef Compare key_compare;
+ typedef typename vector_t::allocator_type allocator_type;
+ typedef typename vector_t::size_type size_type;
+ typedef typename vector_t::difference_type difference_type;
+ typedef typename vector_t::iterator iterator;
+ typedef typename vector_t::const_iterator const_iterator;
+ typedef typename vector_t::reverse_iterator reverse_iterator;
+ typedef typename vector_t::const_reverse_iterator const_reverse_iterator;
+
+ //!Standard extension
+ typedef allocator_type stored_allocator_type;
+
+ private:
+ typedef allocator_traits stored_allocator_traits;
+
+ public:
+ flat_tree()
+ : m_data()
+ { }
+
+ explicit flat_tree(const Compare& comp)
+ : m_data(comp)
+ { }
+
+ flat_tree(const Compare& comp, const allocator_type& a)
+ : m_data(comp, a)
+ { }
+
+ explicit flat_tree(const allocator_type& a)
+ : m_data(a)
+ { }
+
+ flat_tree(const flat_tree& x)
+ : m_data(x.m_data)
+ { }
+
+ flat_tree(BOOST_RV_REF(flat_tree) x)
+ : m_data(boost::move(x.m_data))
+ { }
+
+ flat_tree(const flat_tree& x, const allocator_type &a)
+ : m_data(x.m_data, a)
+ { }
+
+ flat_tree(BOOST_RV_REF(flat_tree) x, const allocator_type &a)
+ : m_data(boost::move(x.m_data), a)
+ { }
+
+ template
+ flat_tree( ordered_range_t, InputIterator first, InputIterator last
+ , const Compare& comp = Compare()
+ , const allocator_type& a = allocator_type())
+ : m_data(comp, a)
+ { this->m_data.m_vect.insert(this->m_data.m_vect.end(), first, last); }
+
+ template
+ flat_tree( bool unique_insertion
+ , InputIterator first, InputIterator last
+ , const Compare& comp = Compare()
+ , const allocator_type& a = allocator_type())
+ : m_data(comp, a)
+ {
+ //Use cend() as hint to achieve linear time for
+ //ordered ranges as required by the standard
+ //for the constructor
+ //Call end() every iteration as reallocation might have invalidated iterators
+ if(unique_insertion){
+ for ( ; first != last; ++first){
+ this->insert_unique(this->cend(), *first);
+ }
+ }
+ else{
+ for ( ; first != last; ++first){
+ this->insert_equal(this->cend(), *first);
+ }
+ }
+ }
+
+ ~flat_tree()
+ {}
+
+ flat_tree& operator=(BOOST_COPY_ASSIGN_REF(flat_tree) x)
+ { m_data = x.m_data; return *this; }
+
+ flat_tree& operator=(BOOST_RV_REF(flat_tree) x)
+ BOOST_NOEXCEPT_IF( allocator_traits_type::is_always_equal::value
+ && boost::container::container_detail::is_nothrow_move_assignable::value )
+ { m_data = boost::move(x.m_data); return *this; }
+
+ public:
+ // accessors:
+ Compare key_comp() const
+ { return this->m_data.get_comp(); }
+
+ value_compare value_comp() const
+ { return this->m_data; }
+
+ allocator_type get_allocator() const
+ { return this->m_data.m_vect.get_allocator(); }
+
+ const stored_allocator_type &get_stored_allocator() const
+ { return this->m_data.m_vect.get_stored_allocator(); }
+
+ stored_allocator_type &get_stored_allocator()
+ { return this->m_data.m_vect.get_stored_allocator(); }
+
+ iterator begin()
+ { return this->m_data.m_vect.begin(); }
+
+ const_iterator begin() const
+ { return this->cbegin(); }
+
+ const_iterator cbegin() const
+ { return this->m_data.m_vect.begin(); }
+
+ iterator end()
+ { return this->m_data.m_vect.end(); }
+
+ const_iterator end() const
+ { return this->cend(); }
+
+ const_iterator cend() const
+ { return this->m_data.m_vect.end(); }
+
+ reverse_iterator rbegin()
+ { return reverse_iterator(this->end()); }
+
+ const_reverse_iterator rbegin() const
+ { return this->crbegin(); }
+
+ const_reverse_iterator crbegin() const
+ { return const_reverse_iterator(this->cend()); }
+
+ reverse_iterator rend()
+ { return reverse_iterator(this->begin()); }
+
+ const_reverse_iterator rend() const
+ { return this->crend(); }
+
+ const_reverse_iterator crend() const
+ { return const_reverse_iterator(this->cbegin()); }
+
+ bool empty() const
+ { return this->m_data.m_vect.empty(); }
+
+ size_type size() const
+ { return this->m_data.m_vect.size(); }
+
+ size_type max_size() const
+ { return this->m_data.m_vect.max_size(); }
+
+ void swap(flat_tree& other)
+ BOOST_NOEXCEPT_IF( allocator_traits_type::is_always_equal::value
+ && boost::container::container_detail::is_nothrow_swappable::value )
+ { this->m_data.swap(other.m_data); }
+
+ public:
+ // insert/erase
+ std::pair insert_unique(const value_type& val)
+ {
+ std::pair ret;
+ insert_commit_data data;
+ ret.second = this->priv_insert_unique_prepare(val, data);
+ ret.first = ret.second ? this->priv_insert_commit(data, val)
+ : iterator(vector_iterator_get_ptr(data.position));
+ return ret;
+ }
+
+ std::pair insert_unique(BOOST_RV_REF(value_type) val)
+ {
+ std::pair ret;
+ insert_commit_data data;
+ ret.second = this->priv_insert_unique_prepare(val, data);
+ ret.first = ret.second ? this->priv_insert_commit(data, boost::move(val))
+ : iterator(vector_iterator_get_ptr(data.position));
+ return ret;
+ }
+
+ iterator insert_equal(const value_type& val)
+ {
+ iterator i = this->upper_bound(KeyOfValue()(val));
+ i = this->m_data.m_vect.insert(i, val);
+ return i;
+ }
+
+ iterator insert_equal(BOOST_RV_REF(value_type) mval)
+ {
+ iterator i = this->upper_bound(KeyOfValue()(mval));
+ i = this->m_data.m_vect.insert(i, boost::move(mval));
+ return i;
+ }
+
+ iterator insert_unique(const_iterator hint, const value_type& val)
+ {
+ BOOST_ASSERT(this->priv_in_range_or_end(hint));
+ std::pair ret;
+ insert_commit_data data;
+ return this->priv_insert_unique_prepare(hint, val, data)
+ ? this->priv_insert_commit(data, val)
+ : iterator(vector_iterator_get_ptr(data.position));
+ }
+
+ iterator insert_unique(const_iterator hint, BOOST_RV_REF(value_type) val)
+ {
+ BOOST_ASSERT(this->priv_in_range_or_end(hint));
+ std::pair ret;
+ insert_commit_data data;
+ return this->priv_insert_unique_prepare(hint, val, data)
+ ? this->priv_insert_commit(data, boost::move(val))
+ : iterator(vector_iterator_get_ptr(data.position));
+ }
+
+ iterator insert_equal(const_iterator hint, const value_type& val)
+ {
+ BOOST_ASSERT(this->priv_in_range_or_end(hint));
+ insert_commit_data data;
+ this->priv_insert_equal_prepare(hint, val, data);
+ return this->priv_insert_commit(data, val);
+ }
+
+ iterator insert_equal(const_iterator hint, BOOST_RV_REF(value_type) mval)
+ {
+ BOOST_ASSERT(this->priv_in_range_or_end(hint));
+ insert_commit_data data;
+ this->priv_insert_equal_prepare(hint, mval, data);
+ return this->priv_insert_commit(data, boost::move(mval));
+ }
+
+ template
+ void insert_unique(InIt first, InIt last)
+ {
+ for ( ; first != last; ++first){
+ this->insert_unique(*first);
+ }
+ }
+
+ template
+ void insert_equal(InIt first, InIt last
+ #if !defined(BOOST_CONTAINER_DOXYGEN_INVOKED)
+ , typename container_detail::enable_if_c
+ < container_detail::is_input_iterator::value
+ >::type * = 0
+ #endif
+ )
+ { this->priv_insert_equal_loop(first, last); }
+
+ template
+ void insert_equal(InIt first, InIt last
+ #if !defined(BOOST_CONTAINER_DOXYGEN_INVOKED)
+ , typename container_detail::enable_if_c
+ < !container_detail::is_input_iterator::value
+ >::type * = 0
+ #endif
+ )
+ {
+ const size_type len = static_cast(boost::container::iterator_distance(first, last));
+ this->reserve(this->size()+len);
+ this->priv_insert_equal_loop(first, last);
+ }
+
+ //Ordered
+
+ template
+ void insert_equal(ordered_range_t, InIt first, InIt last
+ #if !defined(BOOST_CONTAINER_DOXYGEN_INVOKED)
+ , typename container_detail::enable_if_c
+ < container_detail::is_input_iterator::value
+ >::type * = 0
+ #endif
+ )
+ { this->priv_insert_equal_loop_ordered(first, last); }
+
+ template
+ void insert_equal(ordered_range_t, FwdIt first, FwdIt last
+ #if !defined(BOOST_CONTAINER_DOXYGEN_INVOKED)
+ , typename container_detail::enable_if_c
+ < !container_detail::is_input_iterator::value &&
+ container_detail::is_forward_iterator::value
+ >::type * = 0
+ #endif
+ )
+ {
+ const size_type len = static_cast(boost::container::iterator_distance(first, last));
+ this->reserve(this->size()+len);
+ this->priv_insert_equal_loop_ordered(first, last);
+ }
+
+ template
+ void insert_equal(ordered_range_t, BidirIt first, BidirIt last
+ #if !defined(BOOST_CONTAINER_DOXYGEN_INVOKED)
+ , typename container_detail::disable_if_or
+ < void
+ , container_detail::is_input_iterator
+ , container_detail::is_forward_iterator
+ >::type * = 0
+ #endif
+ )
+ { this->m_data.m_vect.merge(first, last); }
+
+ template
+ void insert_unique(ordered_unique_range_t, InIt first, InIt last
+ #if !defined(BOOST_CONTAINER_DOXYGEN_INVOKED)
+ , typename container_detail::enable_if_or
+ < void
+ , container_detail::is_input_iterator
+ , container_detail::is_forward_iterator
+ >::type * = 0
+ #endif
+ )
+ {
+ const_iterator pos(this->cend());
+ for ( ; first != last; ++first){
+ pos = this->insert_unique(pos, *first);
+ ++pos;
+ }
+ }
+
+ template
+ void insert_unique(ordered_unique_range_t, BidirIt first, BidirIt last
+ #if !defined(BOOST_CONTAINER_DOXYGEN_INVOKED)
+ , typename container_detail::enable_if_c
+ < !(container_detail::is_input_iterator::value ||
+ container_detail::is_forward_iterator::value)
+ >::type * = 0
+ #endif
+ )
+ { this->m_data.m_vect.merge_unique(first, last, value_compare()); }
+
+ #if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
+
+ template
+ std::pair emplace_unique(BOOST_FWD_REF(Args)... args)
+ {
+ typename aligned_storage::value>::type v;
+ value_type &val = *static_cast(static_cast(&v));
+ stored_allocator_type &a = this->get_stored_allocator();
+ stored_allocator_traits::construct(a, &val, ::boost::forward(args)... );
+ value_destructor d(a, val);
+ return this->insert_unique(::boost::move(val));
+ }
+
+ template
+ iterator emplace_hint_unique(const_iterator hint, BOOST_FWD_REF(Args)... args)
+ {
+ //hint checked in insert_unique
+ typename aligned_storage::value>::type v;
+ value_type &val = *static_cast(static_cast(&v));
+ stored_allocator_type &a = this->get_stored_allocator();
+ stored_allocator_traits::construct(a, &val, ::boost::forward(args)... );
+ value_destructor d(a, val);
+ return this->insert_unique(hint, ::boost::move(val));
+ }
+
+ template
+ iterator emplace_equal(BOOST_FWD_REF(Args)... args)
+ {
+ typename aligned_storage::value>::type v;
+ value_type &val = *static_cast(static_cast(&v));
+ stored_allocator_type &a = this->get_stored_allocator();
+ stored_allocator_traits::construct(a, &val, ::boost::forward(args)... );
+ value_destructor d(a, val);
+ return this->insert_equal(::boost::move(val));
+ }
+
+ template
+ iterator emplace_hint_equal(const_iterator hint, BOOST_FWD_REF(Args)... args)
+ {
+ //hint checked in insert_equal
+ typename aligned_storage::value>::type v;
+ value_type &val = *static_cast(static_cast(&v));
+ stored_allocator_type &a = this->get_stored_allocator();
+ stored_allocator_traits::construct(a, &val, ::boost::forward(args)... );
+ value_destructor d(a, val);
+ return this->insert_equal(hint, ::boost::move(val));
+ }
+
+ #else // !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
+
+ #define BOOST_CONTAINER_FLAT_TREE_EMPLACE_CODE(N) \
+ BOOST_MOVE_TMPL_LT##N BOOST_MOVE_CLASS##N BOOST_MOVE_GT##N \
+ std::pair emplace_unique(BOOST_MOVE_UREF##N)\
+ {\
+ typename aligned_storage::value>::type v;\
+ value_type &val = *static_cast(static_cast(&v));\
+ stored_allocator_type &a = this->get_stored_allocator();\
+ stored_allocator_traits::construct(a, &val BOOST_MOVE_I##N BOOST_MOVE_FWD##N);\
+ value_destructor d(a, val);\
+ return this->insert_unique(::boost::move(val));\
+ }\
+ \
+ BOOST_MOVE_TMPL_LT##N BOOST_MOVE_CLASS##N BOOST_MOVE_GT##N \
+ iterator emplace_hint_unique(const_iterator hint BOOST_MOVE_I##N BOOST_MOVE_UREF##N)\
+ {\
+ typename aligned_storage::value>::type v;\
+ value_type &val = *static_cast(static_cast(&v));\
+ stored_allocator_type &a = this->get_stored_allocator();\
+ stored_allocator_traits::construct(a, &val BOOST_MOVE_I##N BOOST_MOVE_FWD##N);\
+ value_destructor d(a, val);\
+ return this->insert_unique(hint, ::boost::move(val));\
+ }\
+ \
+ BOOST_MOVE_TMPL_LT##N BOOST_MOVE_CLASS##N BOOST_MOVE_GT##N \
+ iterator emplace_equal(BOOST_MOVE_UREF##N)\
+ {\
+ typename aligned_storage::value>::type v;\
+ value_type &val = *static_cast(static_cast(&v));\
+ stored_allocator_type &a = this->get_stored_allocator();\
+ stored_allocator_traits::construct(a, &val BOOST_MOVE_I##N BOOST_MOVE_FWD##N);\
+ value_destructor d(a, val);\
+ return this->insert_equal(::boost::move(val));\
+ }\
+ \
+ BOOST_MOVE_TMPL_LT##N BOOST_MOVE_CLASS##N BOOST_MOVE_GT##N \
+ iterator emplace_hint_equal(const_iterator hint BOOST_MOVE_I##N BOOST_MOVE_UREF##N)\
+ {\
+ typename aligned_storage ::value>::type v;\
+ value_type &val = *static_cast(static_cast(&v));\
+ stored_allocator_type &a = this->get_stored_allocator();\
+ stored_allocator_traits::construct(a, &val BOOST_MOVE_I##N BOOST_MOVE_FWD##N);\
+ value_destructor d(a, val);\
+ return this->insert_equal(hint, ::boost::move(val));\
+ }\
+ //
+ BOOST_MOVE_ITERATE_0TO9(BOOST_CONTAINER_FLAT_TREE_EMPLACE_CODE)
+ #undef BOOST_CONTAINER_FLAT_TREE_EMPLACE_CODE
+
+ #endif // !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
+
+ iterator erase(const_iterator position)
+ { return this->m_data.m_vect.erase(position); }
+
+ size_type erase(const key_type& k)
+ {
+ std::pair itp = this->equal_range(k);
+ size_type ret = static_cast(itp.second-itp.first);
+ if (ret){
+ this->m_data.m_vect.erase(itp.first, itp.second);
+ }
+ return ret;
+ }
+
+ iterator erase(const_iterator first, const_iterator last)
+ { return this->m_data.m_vect.erase(first, last); }
+
+ void clear()
+ { this->m_data.m_vect.clear(); }
+
+ //! Effects: Tries to deallocate the excess of memory created
+ // with previous allocations. The size of the vector is unchanged
+ //!
+ //! Throws: If memory allocation throws, or T's copy constructor throws.
+ //!
+ //! Complexity: Linear to size().
+ void shrink_to_fit()
+ { this->m_data.m_vect.shrink_to_fit(); }
+
+ iterator nth(size_type n) BOOST_NOEXCEPT_OR_NOTHROW
+ { return this->m_data.m_vect.nth(n); }
+
+ const_iterator nth(size_type n) const BOOST_NOEXCEPT_OR_NOTHROW
+ { return this->m_data.m_vect.nth(n); }
+
+ size_type index_of(iterator p) BOOST_NOEXCEPT_OR_NOTHROW
+ { return this->m_data.m_vect.index_of(p); }
+
+ size_type index_of(const_iterator p) const BOOST_NOEXCEPT_OR_NOTHROW
+ { return this->m_data.m_vect.index_of(p); }
+
+ // set operations:
+ iterator find(const key_type& k)
+ {
+ iterator i = this->lower_bound(k);
+ iterator end_it = this->end();
+ if (i != end_it && this->m_data.get_comp()(k, KeyOfValue()(*i))){
+ i = end_it;
+ }
+ return i;
+ }
+
+ const_iterator find(const key_type& k) const
+ {
+ const_iterator i = this->lower_bound(k);
+
+ const_iterator end_it = this->cend();
+ if (i != end_it && this->m_data.get_comp()(k, KeyOfValue()(*i))){
+ i = end_it;
+ }
+ return i;
+ }
+
+ // set operations:
+ size_type count(const key_type& k) const
+ {
+ std::pair p = this->equal_range(k);
+ size_type n = p.second - p.first;
+ return n;
+ }
+
+ iterator lower_bound(const key_type& k)
+ { return this->priv_lower_bound(this->begin(), this->end(), k); }
+
+ const_iterator lower_bound(const key_type& k) const
+ { return this->priv_lower_bound(this->cbegin(), this->cend(), k); }
+
+ iterator upper_bound(const key_type& k)
+ { return this->priv_upper_bound(this->begin(), this->end(), k); }
+
+ const_iterator upper_bound(const key_type& k) const
+ { return this->priv_upper_bound(this->cbegin(), this->cend(), k); }
+
+ std::pair equal_range(const key_type& k)
+ { return this->priv_equal_range(this->begin(), this->end(), k); }
+
+ std::pair equal_range(const key_type& k) const
+ { return this->priv_equal_range(this->cbegin(), this->cend(), k); }
+
+ std::pair lower_bound_range(const key_type& k)
+ { return this->priv_lower_bound_range(this->begin(), this->end(), k); }
+
+ std::pair lower_bound_range(const key_type& k) const
+ { return this->priv_lower_bound_range(this->cbegin(), this->cend(), k); }
+
+ size_type capacity() const
+ { return this->m_data.m_vect.capacity(); }
+
+ void reserve(size_type cnt)
+ { this->m_data.m_vect.reserve(cnt); }
+
+ friend bool operator==(const flat_tree& x, const flat_tree& y)
+ {
+ return x.size() == y.size() && ::boost::container::algo_equal(x.begin(), x.end(), y.begin());
+ }
+
+ friend bool operator<(const flat_tree& x, const flat_tree& y)
+ {
+ return ::boost::container::algo_lexicographical_compare(x.begin(), x.end(), y.begin(), y.end());
+ }
+
+ friend bool operator!=(const flat_tree& x, const flat_tree& y)
+ { return !(x == y); }
+
+ friend bool operator>(const flat_tree& x, const flat_tree& y)
+ { return y < x; }
+
+ friend bool operator<=(const flat_tree& x, const flat_tree& y)
+ { return !(y < x); }
+
+ friend bool operator>=(const flat_tree& x, const flat_tree& y)
+ { return !(x < y); }
+
+ friend void swap(flat_tree& x, flat_tree& y)
+ { x.swap(y); }
+
+ private:
+
+ bool priv_in_range_or_end(const_iterator pos) const
+ {
+ return (this->begin() <= pos) && (pos <= this->end());
+ }
+
+ struct insert_commit_data
+ {
+ const_iterator position;
+ };
+
+ // insert/erase
+ void priv_insert_equal_prepare
+ (const_iterator pos, const value_type& val, insert_commit_data &data)
+ {
+ // N1780
+ // To insert val at pos:
+ // if pos == end || val <= *pos
+ // if pos == begin || val >= *(pos-1)
+ // insert val before pos
+ // else
+ // insert val before upper_bound(val)
+ // else
+ // insert val before lower_bound(val)
+ const value_compare &val_cmp = this->m_data;
+
+ if(pos == this->cend() || !val_cmp(*pos, val)){
+ if (pos == this->cbegin() || !val_cmp(val, pos[-1])){
+ data.position = pos;
+ }
+ else{
+ data.position =
+ this->priv_upper_bound(this->cbegin(), pos, KeyOfValue()(val));
+ }
+ }
+ else{
+ data.position =
+ this->priv_lower_bound(pos, this->cend(), KeyOfValue()(val));
+ }
+ }
+
+ bool priv_insert_unique_prepare
+ (const_iterator b, const_iterator e, const value_type& val, insert_commit_data &commit_data)
+ {
+ const value_compare &val_cmp = this->m_data;
+ commit_data.position = this->priv_lower_bound(b, e, KeyOfValue()(val));
+ return commit_data.position == e || val_cmp(val, *commit_data.position);
+ }
+
+ bool priv_insert_unique_prepare
+ (const value_type& val, insert_commit_data &commit_data)
+ { return this->priv_insert_unique_prepare(this->cbegin(), this->cend(), val, commit_data); }
+
+ bool priv_insert_unique_prepare
+ (const_iterator pos, const value_type& val, insert_commit_data &commit_data)
+ {
+ //N1780. Props to Howard Hinnant!
+ //To insert val at pos:
+ //if pos == end || val <= *pos
+ // if pos == begin || val >= *(pos-1)
+ // insert val before pos
+ // else
+ // insert val before upper_bound(val)
+ //else if pos+1 == end || val <= *(pos+1)
+ // insert val after pos
+ //else
+ // insert val before lower_bound(val)
+ const value_compare &val_cmp = this->m_data;
+ const const_iterator cend_it = this->cend();
+ if(pos == cend_it || val_cmp(val, *pos)){ //Check if val should go before end
+ const const_iterator cbeg = this->cbegin();
+ commit_data.position = pos;
+ if(pos == cbeg){ //If container is empty then insert it in the beginning
+ return true;
+ }
+ const_iterator prev(pos);
+ --prev;
+ if(val_cmp(*prev, val)){ //If previous element was less, then it should go between prev and pos
+ return true;
+ }
+ else if(!val_cmp(val, *prev)){ //If previous was equal then insertion should fail
+ commit_data.position = prev;
+ return false;
+ }
+ else{ //Previous was bigger so insertion hint was pointless, dispatch to hintless insertion
+ //but reduce the search between beg and prev as prev is bigger than val
+ return this->priv_insert_unique_prepare(cbeg, prev, val, commit_data);
+ }
+ }
+ else{
+ //The hint is before the insertion position, so insert it
+ //in the remaining range [pos, end)
+ return this->priv_insert_unique_prepare(pos, cend_it, val, commit_data);
+ }
+ }
+
+ template
+ iterator priv_insert_commit
+ (insert_commit_data &commit_data, BOOST_FWD_REF(Convertible) convertible)
+ {
+ return this->m_data.m_vect.insert
+ ( commit_data.position
+ , boost::forward(convertible));
+ }
+
+ template
+ RanIt priv_lower_bound(RanIt first, const RanIt last,
+ const key_type & key) const
+ {
+ const Compare &key_cmp = this->m_data.get_comp();
+ KeyOfValue key_extract;
+ size_type len = static_cast(last - first);
+ RanIt middle;
+
+ while (len) {
+ size_type step = len >> 1;
+ middle = first;
+ middle += step;
+
+ if (key_cmp(key_extract(*middle), key)) {
+ first = ++middle;
+ len -= step + 1;
+ }
+ else{
+ len = step;
+ }
+ }
+ return first;
+ }
+
+ template
+ RanIt priv_upper_bound
+ (RanIt first, const RanIt last,const key_type & key) const
+ {
+ const Compare &key_cmp = this->m_data.get_comp();
+ KeyOfValue key_extract;
+ size_type len = static_cast(last - first);
+ RanIt middle;
+
+ while (len) {
+ size_type step = len >> 1;
+ middle = first;
+ middle += step;
+
+ if (key_cmp(key, key_extract(*middle))) {
+ len = step;
+ }
+ else{
+ first = ++middle;
+ len -= step + 1;
+ }
+ }
+ return first;
+ }
+
+ template
+ std::pair
+ priv_equal_range(RanIt first, RanIt last, const key_type& key) const
+ {
+ const Compare &key_cmp = this->m_data.get_comp();
+ KeyOfValue key_extract;
+ size_type len = static_cast(last - first);
+ RanIt middle;
+
+ while (len) {
+ size_type step = len >> 1;
+ middle = first;
+ middle += step;
+
+ if (key_cmp(key_extract(*middle), key)){
+ first = ++middle;
+ len -= step + 1;
+ }
+ else if (key_cmp(key, key_extract(*middle))){
+ len = step;
+ }
+ else {
+ //Middle is equal to key
+ last = first;
+ last += len;
+ RanIt const first_ret = this->priv_lower_bound(first, middle, key);
+ return std::pair
+ ( first_ret, this->priv_upper_bound(++middle, last, key));
+ }
+ }
+ return std::pair(first, first);
+ }
+
+ template
+ std::pair priv_lower_bound_range(RanIt first, RanIt last, const key_type& k) const
+ {
+ const Compare &key_cmp = this->m_data.get_comp();
+ KeyOfValue key_extract;
+ RanIt lb(this->priv_lower_bound(first, last, k)), ub(lb);
+ if(lb != last && static_cast(!key_cmp(k, key_extract(*lb)))){
+ ++ub;
+ }
+ return std::pair(lb, ub);
+ }
+
+ template
+ void priv_insert_equal_loop(InIt first, InIt last)
+ {
+ for ( ; first != last; ++first){
+ this->insert_equal(*first);
+ }
+ }
+
+ template
+ void priv_insert_equal_loop_ordered(InIt first, InIt last)
+ {
+ const_iterator pos(this->cend());
+ for ( ; first != last; ++first){
+ //If ordered, then try hint version
+ //to achieve constant-time complexity per insertion
+ //in some cases
+ pos = this->insert_equal(pos, *first);
+ ++pos;
+ }
+ }
+};
+
+} //namespace container_detail {
+
+} //namespace container {
+
+//!has_trivial_destructor_after_move<> == true_type
+//!specialization for optimizations
+template
+struct has_trivial_destructor_after_move >
+{
+ typedef typename ::boost::container::allocator_traits::pointer pointer;
+ static const bool value = ::boost::has_trivial_destructor_after_move::value &&
+ ::boost::has_trivial_destructor_after_move::value;
+};
+
+} //namespace boost {
+
+#include
+
+#endif // BOOST_CONTAINER_FLAT_TREE_HPP
Index: libraries/win32/boost/include/boost/container/flat_map.hpp
===================================================================
--- /dev/null
+++ libraries/win32/boost/include/boost/container/flat_map.hpp
@@ -0,0 +1,2014 @@
+//////////////////////////////////////////////////////////////////////////////
+//
+// (C) Copyright Ion Gaztanaga 2005-2013. Distributed under the Boost
+// Software License, Version 1.0. (See accompanying file
+// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+// See http://www.boost.org/libs/container for documentation.
+//
+//////////////////////////////////////////////////////////////////////////////
+#ifndef BOOST_CONTAINER_FLAT_MAP_HPP
+#define BOOST_CONTAINER_FLAT_MAP_HPP
+
+#ifndef BOOST_CONFIG_HPP
+# include
+#endif
+
+#if defined(BOOST_HAS_PRAGMA_ONCE)
+# pragma once
+#endif
+
+#include
+#include
+// container
+#include
+#include
+#include //new_allocator
+#include
+// container/detail
+#include
+#include
+#include
+#include //equal()
+// move
+#include
+#include
+// move/detail
+#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
+#include
+#endif
+#include
+// intrusive
+#include //pair
+#include //less, equal
+//others
+#include
+
+#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST)
+#include
+#endif
+
+namespace boost {
+namespace container {
+
+#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED
+
+namespace container_detail{
+
+template
+static D &force(const S &s)
+{ return *const_cast((reinterpret_cast(&s))); }
+
+template
+static D force_copy(S s)
+{
+ D *vp = reinterpret_cast(&s);
+ return D(*vp);
+}
+
+} //namespace container_detail{
+
+#endif //#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED
+
+//! A flat_map is a kind of associative container that supports unique keys (contains at
+//! most one of each key value) and provides for fast retrieval of values of another
+//! type T based on the keys. The flat_map class supports random-access iterators.
+//!
+//! A flat_map satisfies all of the requirements of a container and of a reversible
+//! container and of an associative container. A flat_map also provides
+//! most operations described for unique keys. For a
+//! flat_map the key_type is Key and the value_type is std::pair
+//! (unlike std::map which value_type is std::pair<const Key, T>).
+//!
+//! Compare is the ordering function for Keys (e.g. std::less).
+//!
+//! Allocator is the allocator to allocate the value_types
+//! (e.g. allocator< std::pair >).
+//!
+//! flat_map is similar to std::map but it's implemented like an ordered vector.
+//! This means that inserting a new element into a flat_map invalidates
+//! previous iterators and references
+//!
+//! Erasing an element invalidates iterators and references
+//! pointing to elements that come after (their keys are bigger) the erased element.
+//!
+//! This container provides random-access iterators.
+//!
+//! \tparam Key is the key_type of the map
+//! \tparam Value is the mapped_type
+//! \tparam Compare is the ordering function for Keys (e.g. std::less).
+//! \tparam Allocator is the allocator to allocate the value_type
s
+//! (e.g. allocator< std::pair > ).
+#ifdef BOOST_CONTAINER_DOXYGEN_INVOKED
+template , class Allocator = new_allocator< std::pair< Key, T> > >
+#else
+template
+#endif
+class flat_map
+{
+ #ifndef BOOST_CONTAINER_DOXYGEN_INVOKED
+ private:
+ BOOST_COPYABLE_AND_MOVABLE(flat_map)
+ //This is the tree that we should store if pair was movable
+ typedef container_detail::flat_tree,
+ container_detail::select1st< std::pair >,
+ Compare,
+ Allocator> tree_t;
+
+ //This is the real tree stored here. It's based on a movable pair
+ typedef container_detail::flat_tree,
+ container_detail::select1st >,
+ Compare,
+ typename allocator_traits::template portable_rebind_alloc
+ >::type> impl_tree_t;
+ impl_tree_t m_flat_tree; // flat tree representing flat_map
+
+ typedef typename impl_tree_t::value_type impl_value_type;
+ typedef typename impl_tree_t::const_iterator impl_const_iterator;
+ typedef typename impl_tree_t::iterator impl_iterator;
+ typedef typename impl_tree_t::allocator_type impl_allocator_type;
+ typedef container_detail::flat_tree_value_compare
+ < Compare
+ , container_detail::select1st< std::pair >
+ , std::pair > value_compare_impl;
+ typedef typename container_detail::get_flat_tree_iterators
+ ::pointer>::iterator iterator_impl;
+ typedef typename container_detail::get_flat_tree_iterators
+ ::pointer>::const_iterator const_iterator_impl;
+ typedef typename container_detail::get_flat_tree_iterators
+ ::pointer>::reverse_iterator reverse_iterator_impl;
+ typedef typename container_detail::get_flat_tree_iterators
+ ::pointer>::const_reverse_iterator const_reverse_iterator_impl;
+ public:
+ typedef typename impl_tree_t::stored_allocator_type impl_stored_allocator_type;
+ private:
+ #endif //#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED
+
+ public:
+
+ //////////////////////////////////////////////
+ //
+ // types
+ //
+ //////////////////////////////////////////////
+ typedef Key key_type;
+ typedef T mapped_type;
+ typedef std::pair value_type;
+ typedef ::boost::container::allocator_traits allocator_traits_type;
+ typedef typename boost::container::allocator_traits::pointer pointer;
+ typedef typename boost::container::allocator_traits::const_pointer const_pointer;
+ typedef typename boost::container::allocator_traits::reference reference;
+ typedef typename boost::container::allocator_traits::const_reference const_reference;
+ typedef typename boost::container::allocator_traits::size_type size_type;
+ typedef typename boost::container::allocator_traits::difference_type difference_type;
+ typedef Allocator allocator_type;
+ typedef BOOST_CONTAINER_IMPDEF(Allocator) stored_allocator_type;
+ typedef BOOST_CONTAINER_IMPDEF(value_compare_impl) value_compare;
+ typedef Compare key_compare;
+ typedef BOOST_CONTAINER_IMPDEF(iterator_impl) iterator;
+ typedef BOOST_CONTAINER_IMPDEF(const_iterator_impl) const_iterator;
+ typedef BOOST_CONTAINER_IMPDEF(reverse_iterator_impl) reverse_iterator;
+ typedef BOOST_CONTAINER_IMPDEF(const_reverse_iterator_impl) const_reverse_iterator;
+ typedef BOOST_CONTAINER_IMPDEF(impl_value_type) movable_value_type;
+
+ public:
+ //////////////////////////////////////////////
+ //
+ // construct/copy/destroy
+ //
+ //////////////////////////////////////////////
+
+ //! Effects: Default constructs an empty flat_map.
+ //!
+ //! Complexity: Constant.
+ flat_map()
+ : m_flat_tree()
+ {
+ //A type must be std::pair
+ BOOST_STATIC_ASSERT((container_detail::is_same