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
@@ -338,11 +338,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,266 @@
+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 algo to find the initial grouping position.
+
+ let x = 0;
+ let z = 0;
+ let bestDist = Number.POSITIVE_INFINITY;
+ for (let ent of group.entities)
+ {
+ let cmpPosition = Engine.QueryInterface(ent, IID_Position);
+ let ex = cmpPosition.GetPosition2D().x;
+ let ez = cmpPosition.GetPosition2D().y;
+
+ let entPos = new Vector2D(ex, ez);
+ let goal = new Vector2D(group.x, group.z);
+
+ let dist = entPos.distanceToSquared(goal);
+ if (dist < bestDist)
+ {
+ x = ex;
+ z = ez;
+ bestDist = dist;
+ }
+ }
+ group.rallyPoint.x = x;
+ group.rallyPoint.z = z;
+
+ 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(),
@@ -589,8 +590,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;
@@ -817,12 +820,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) {
@@ -750,690 +734,56 @@
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();
- }
- },
-
+ "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,9 @@
{
// 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);
+
+ this.StopMoving();
// Special cases of orders that shouldn't be replaced:
// 1. Cheering - we're invulnerable, add order after we finish
@@ -3894,24 +3358,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 +3386,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 +3448,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 +3477,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 +3512,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 +3523,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 +3682,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 +3729,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 +3761,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 +3799,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 +3810,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 +3845,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 +3876,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 +3892,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 +3904,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 +3928,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 +3949,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 +4202,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 +4213,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 +4366,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 +4376,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 +4473,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 +4573,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 +4641,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 +4657,6 @@
return false;
var marketsChanged = cmpTrader.SetTargetMarket(target, source);
- if (this.IsFormationController())
- this.CallMemberFunction("SetTargetMarket", [target, source]);
-
return marketsChanged;
};
@@ -5521,40 +4859,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 +4995,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 +5032,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 +5068,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 +5095,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 +5114,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 +5152,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 +5185,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 +5195,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 +5230,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 +5260,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 +5310,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/helpers/Commands.js
===================================================================
--- binaries/data/mods/public/simulation/helpers/Commands.js
+++ binaries/data/mods/public/simulation/helpers/Commands.js
@@ -141,7 +141,9 @@
"walk": function(player, cmd, data)
{
- GetFormationUnitAIs(data.entities, player).forEach(cmpUnitAI => {
+ let unitAIs = CreateGroupWalkOrderIfNecessary(data.entities, cmd.x, cmd.z, 14, cmd.queued);
+ // the grouped entities will group-walk to the target, the non-grouped will just walk there.
+ unitAIs[0].forEach(cmpUnitAI => {
cmpUnitAI.Walk(cmd.x, cmd.z, cmd.queued);
});
},
@@ -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,67 @@
}
}
-/**
- * Remove the given list of entities from their current formations.
- */
-function RemoveFromFormation(ents)
+function CreateGroupWalkOrderIfNecessary(ents, x, z, range, queued)
{
- 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)
-{
- // If an individual was selected, remove it from any formation
- // and command it individually
- if (ents.length == 1)
+ // 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 unit if it has no UnitAI
- var cmpUnitAI = Engine.QueryInterface(ents[0], IID_UnitAI);
+ let cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
if (!cmpUnitAI)
- return [];
+ {
+ formationTemplate = "formations/null";
+ break;
+ }
+ if (!formationTemplate)
+ formationTemplate = cmpUnitAI.GetFormationTemplate();
+ if (formationTemplate == "formations/null")
+ break;
+ else if (cmpUnitAI.GetFormationTemplate() !== formationTemplate)
+ {
+ formationTemplate = "formations/null";
+ break;
+ }
+ }
- RemoveFromFormation(ents);
+ let nonFormableUnitAIs = [];
+ let formableEntsID = [];
+ let formableEntsAI = [];
- return [ cmpUnitAI ];
- }
+ // 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";
- // Separate out the units that don't support the chosen formation
- var formedEnts = [];
- var nonformedUnitAIs = [];
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);
+ let cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
+ let 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 cmpIdentity = Engine.QueryInterface(ent, IID_Identity);
+ if (createGroupOrder && cmpIdentity)// && cmpIdentity.CanUseFormation(formationTemplate))
{
- if (nullFormation)
- cmpUnitAI.SetLastFormationTemplate("formations/null");
- nonformedUnitAIs.push(cmpUnitAI);
+ formableEntsID.push(ent);
+ formableEntsAI.push(cmpUnitAI);
}
+ else
+ nonFormableUnitAIs.push(cmpUnitAI);
}
- if (formedEnts.length == 0)
- {
- // No units support the foundation - return all the others
- return nonformedUnitAIs;
- }
-
- // Find what formations the formationable selected entities are currently in
- var formation = ExtractFormations(formedEnts);
-
- var formationUnitAIs = [];
- if (formation.ids.length == 1)
+ // TODO: validate formation
+ if (createGroupOrder && formableEntsAI.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);
- }
+ // 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); });
}
-
- if (!formationUnitAIs.length)
+ else
{
- // 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]);
- }
-
- // 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)
- {
- 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);
- }
- }
-
- 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)
- {
- // 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);
+ nonFormableUnitAIs = nonFormableUnitAIs.concat(formableEntsAI);
+ formableEntsAI = [];
}
- return clusters;
+ return [nonFormableUnitAIs, formableEntsAI];
}
function GetFormationRequirements(formationTemplate)
@@ -1591,7 +1455,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
@@ -45,10 +45,8 @@
Women in the ancient world took on a variety of roles - from leadership (Celts) to servant (Greeks). Women are hard workers, the economic backbone of any civilization. In history, it was typical when all the males (capable of fighting) were killed for the females, children, and elderly to be sold as slaves.
FemaleCitizen
Citizen Worker
-
- 2.0
1.0
1
@@ -87,11 +85,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, typename Allocator::value_type>::value));
+ }
+
+ //!