Index: binaries/data/mods/public/simulation/components/AutoBuildable.js
===================================================================
--- binaries/data/mods/public/simulation/components/AutoBuildable.js
+++ binaries/data/mods/public/simulation/components/AutoBuildable.js
@@ -0,0 +1,37 @@
+class AutoBuildable
+{
+ Init()
+ {
+ this.rate = +this.template.Rate;
+ }
+
+ get Schema()
+ {
+ return "Defines whether the entity can be built by itself." +
+ "" +
+ "1.0" +
+ "" +
+ "" +
+ "" +
+ "";
+ }
+
+ /**
+ * @return {number} - The rate with technologies and aura modification applied.
+ */
+ GetRate()
+ {
+ return this.rate;
+ }
+}
+
+AutoBuildable.prototype.OnValueModification = function(msg)
+{
+ if (msg.component != "AutoBuildable")
+ return;
+
+ this.rate = ApplyValueModificationsToEntity("AutoBuildable/Rate", +this.template.Rate , this.entity);
+ Engine.PostMessage(this.entity, MT_AutoBuildRateChanged, undefined);
+};
+
+Engine.RegisterComponentType(IID_AutoBuildable, "AutoBuildable", AutoBuildable);
Index: binaries/data/mods/public/simulation/components/Foundation.js
===================================================================
--- binaries/data/mods/public/simulation/components/Foundation.js
+++ binaries/data/mods/public/simulation/components/Foundation.js
@@ -16,8 +16,11 @@
this.totalBuilderRate = 0; // Total amount of work the builders do each second
this.buildMultiplier = 1; // Multiplier for the amount of work builders do
this.buildTimePenalty = 0.7; // Penalty for having multiple builders
-
this.previewEntity = INVALID_ENTITY;
+
+ let cmpAutoBuildable = Engine.QueryInterface(this.entity, IID_AutoBuildable);
+ if (cmpAutoBuildable)
+ this.StartTimer();
};
Foundation.prototype.InitialiseConstruction = function(owner, template)
@@ -152,7 +155,12 @@
if (this.builders.has(builderEnt))
return false;
- let buildRate = Engine.QueryInterface(builderEnt, IID_Builder).GetRate();
+ let cmpBuilder = Engine.QueryInterface(builderEnt, IID_Builder) ||
+ Engine.QueryInterface(this.entity, IID_AutoBuildable);
+ if (!cmpBuilder)
+ return false;
+
+ let buildRate = cmpBuilder.GetRate();
this.builders.set(builderEnt, buildRate);
this.totalBuilderRate += buildRate;
@@ -304,8 +312,8 @@
}
var cmpFoundationPosition = Engine.QueryInterface(this.entity, IID_Position);
- var pos = cmpFoundationPosition.GetPosition2D();
- var rot = cmpFoundationPosition.GetRotation();
+ let pos = cmpFoundationPosition.GetPosition2D();
+ let rot = cmpFoundationPosition.GetRotation();
cmpPreviewPosition.SetYRotation(rot.y);
cmpPreviewPosition.SetXZRotation(rot.x, rot.z);
cmpPreviewPosition.JumpTo(pos.x, pos.y);
@@ -362,9 +370,9 @@
Engine.DestroyEntity(building);
return;
}
- var pos = cmpPosition.GetPosition2D();
+ let pos = cmpPosition.GetPosition2D();
cmpBuildingPosition.JumpTo(pos.x, pos.y);
- var rot = cmpPosition.GetRotation();
+ let rot = cmpPosition.GetRotation();
cmpBuildingPosition.SetYRotation(rot.y);
cmpBuildingPosition.SetXZRotation(rot.x, rot.z);
// TODO: should add a ICmpPosition::CopyFrom() instead of all this
@@ -464,5 +472,50 @@
return cmpHealth.GetMaxHitpoints() / cmpCost.GetBuildTime();
};
+Foundation.prototype.StartTimer = function()
+{
+ if (this.timer)
+ return;
+
+ this.AddBuilder(this.entity);
+ let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
+ this.timer = cmpTimer.SetInterval(this.entity, IID_Foundation, "AutoBuild", 0, 1000, undefined);
+};
+
+Foundation.prototype.CancelTimer = function()
+{
+ if (!this.timer)
+ return;
+
+ this.RemoveBuilder(this.entity);
+ let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
+ cmpTimer.CancelTimer(this.timer);
+ delete this.timer;
+};
+
+Foundation.prototype.AutoBuild = function()
+{
+ let cmpAutoBuildable = Engine.QueryInterface(this.entity, IID_AutoBuildable);
+ if (!cmpAutoBuildable)
+ {
+ this.CancelTimer();
+ return;
+ }
+ let rate = cmpAutoBuildable.GetRate();
+ if (rate != 0)
+ this.Build(this.entity, rate);
+};
+
+
+Foundation.prototype.OnAutoBuildRateChanged = function()
+{
+ if (this.timer)
+ return;
+
+ let cmpAutoBuildable = Engine.QueryInterface(this.entity, IID_AutoBuildable);
+ if (cmpAutoBuildable && cmpAutoBuildable.GetRate() != 0)
+ this.StartTimer();
+};
+
Engine.RegisterComponentType(IID_Foundation, "Foundation", Foundation);
Index: binaries/data/mods/public/simulation/components/interfaces/AutoBuildable.js
===================================================================
--- binaries/data/mods/public/simulation/components/interfaces/AutoBuildable.js
+++ binaries/data/mods/public/simulation/components/interfaces/AutoBuildable.js
@@ -0,0 +1,7 @@
+Engine.RegisterInterface("AutoBuildable");
+
+/**
+ * Message of the form { "from": number, "to": number }
+ * sent from AutoBuildable component whenever its rate changes.
+ */
+Engine.RegisterMessageType("AutoBuildRateChanged");
Index: binaries/data/mods/public/simulation/components/tests/test_AutoBuildable.js
===================================================================
--- binaries/data/mods/public/simulation/components/tests/test_AutoBuildable.js
+++ binaries/data/mods/public/simulation/components/tests/test_AutoBuildable.js
@@ -0,0 +1,15 @@
+Engine.LoadHelperScript("ValueModification.js");
+Engine.LoadComponentScript("interfaces/AutoBuildable.js");
+Engine.LoadComponentScript("interfaces/ModifiersManager.js");
+Engine.LoadComponentScript("AutoBuildable.js");
+
+const cmpBuildableAuto = ConstructComponent(10, "AutoBuildable", {
+ "Rate": "1.0"
+});
+
+TS_ASSERT_EQUALS(cmpBuildableAuto.GetRate(), 1);
+
+const cmpBuildableNoRate = ConstructComponent(12, "AutoBuildable", {
+ "Rate": "0"
+});
+TS_ASSERT_EQUALS(cmpBuildableNoRate.GetRate(), 0);
Index: binaries/data/mods/public/simulation/components/tests/test_Foundation.js
===================================================================
--- binaries/data/mods/public/simulation/components/tests/test_Foundation.js
+++ binaries/data/mods/public/simulation/components/tests/test_Foundation.js
@@ -1,24 +1,28 @@
Engine.LoadHelperScript("Player.js");
+Engine.LoadHelperScript("ValueModification.js");
+Engine.LoadComponentScript("interfaces/AutoBuildable.js");
Engine.LoadComponentScript("interfaces/Builder.js");
Engine.LoadComponentScript("interfaces/Cost.js");
Engine.LoadComponentScript("interfaces/Foundation.js");
Engine.LoadComponentScript("interfaces/Health.js");
+Engine.LoadComponentScript("interfaces/ModifiersManager.js");
Engine.LoadComponentScript("interfaces/StatisticsTracker.js");
Engine.LoadComponentScript("interfaces/TerritoryDecay.js");
Engine.LoadComponentScript("interfaces/Trigger.js");
+Engine.LoadComponentScript("interfaces/Timer.js");
Engine.LoadComponentScript("Foundation.js");
-
+Engine.LoadComponentScript("Timer.js");
let player = 1;
let playerEnt = 3;
let foundationEnt = 20;
let previewEnt = 21;
let newEnt = 22;
+let finalTemplate = "structures/athen_civil_centre.xml";
function testFoundation(...mocks)
{
ResetState();
- let finalTemplate = "structures/athen_civil_centre.xml";
let foundationHP = 1;
let maxHP = 100;
let rot = new Vector3D(1, 2, 3);
@@ -209,3 +213,50 @@
},
}]);
+// Test autobuild feature.
+const foundationEnt2 = 42;
+let turnLength = 0.2;
+let currentFoundationHP = 1;
+let cmpTimer = ConstructComponent(SYSTEM_ENTITY, "Timer");
+
+AddMock(foundationEnt2, IID_Cost, {
+ "GetBuildTime": () => 50,
+ "GetResourceCosts": () => ({ "wood": 100 }),
+});
+
+AddMock(foundationEnt2, IID_AutoBuildable, {
+ "GetRate": () => 1,
+});
+
+const cmpAutoBuildingFoundation = ConstructComponent(foundationEnt2, "Foundation", {});
+AddMock(foundationEnt2, IID_Health, {
+ "GetHitpoints": () => currentFoundationHP,
+ "GetMaxHitpoints": () => 100,
+ "Increase": hp => {
+ currentFoundationHP = Math.min(currentFoundationHP + hp, 100);
+ cmpAutoBuildingFoundation.OnHealthChanged();
+ },
+});
+cmpAutoBuildingFoundation.InitialiseConstruction(player, finalTemplate);
+
+// We start at 3 cause there is no delay on the first run.
+cmpTimer.OnUpdate({ "turnLength": turnLength });
+
+for (let i = 0; i < 10; ++i)
+{
+ if (i == 8)
+ {
+ cmpAutoBuildingFoundation.CancelTimer();
+ TS_ASSERT_EQUALS(cmpAutoBuildingFoundation.GetNumBuilders(), 0);
+ }
+
+ let currentPercentage = cmpAutoBuildingFoundation.GetBuildPercentage();
+ cmpTimer.OnUpdate({ "turnLength": turnLength * 5 });
+ let newPercentage = cmpAutoBuildingFoundation.GetBuildPercentage();
+
+ if (i >= 8)
+ TS_ASSERT_EQUALS(currentPercentage, newPercentage);
+ else
+ // Rate * Max Health / Cost.
+ TS_ASSERT_EQUALS(currentPercentage + 2, newPercentage);
+}
Index: binaries/data/mods/public/simulation/templates/special/filter/foundation.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/special/filter/foundation.xml
+++ binaries/data/mods/public/simulation/templates/special/filter/foundation.xml
@@ -2,6 +2,7 @@
+
Index: binaries/data/mods/public/simulation/templates/structures/athen_house.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/structures/athen_house.xml
+++ binaries/data/mods/public/simulation/templates/structures/athen_house.xml
@@ -4,6 +4,9 @@
athen
Oikos
+
+ 1
+
structures/hellenes/house.xml