diff --git a/binaries/data/mods/public/globalscripts/Templates.js b/binaries/data/mods/public/globalscripts/Templates.js
index 0e689af4a4..306adac175 100644
--- a/binaries/data/mods/public/globalscripts/Templates.js
+++ b/binaries/data/mods/public/globalscripts/Templates.js
@@ -90,18 +90,28 @@ function GetTemplateDataHelper(template, player)
if (template.Attack)
{
- ret.attack = {};
- for (var type in template.Attack)
+ let getAttackStat = function(type, stat)
{
- ret.attack[type] = {
- "hack": func("Attack/"+type+"/Hack", +(template.Attack[type].Hack || 0), player, template),
- "pierce": func("Attack/"+type+"/Pierce", +(template.Attack[type].Pierce || 0), player, template),
- "crush": func("Attack/"+type+"/Crush", +(template.Attack[type].Crush || 0), player, template),
- "minRange": func("Attack/"+type+"/MinRange", +(template.Attack[type].MinRange || 0), player, template),
- "maxRange": func("Attack/"+type+"/MaxRange", +template.Attack[type].MaxRange, player, template),
- "elevationBonus": func("Attack/"+type+"/ElevationBonus", +(template.Attack[type].ElevationBonus || 0), player, template),
- "repeatTime": +(template.Attack[type].RepeatTime || 0),
- };
+ return func("Attack/"+type+"/"+stat, +(template.Attack[type][stat] || 0), player, template);
+ };
+
+ ret.attack = {};
+ for (let type in template.Attack)
+ {
+ if (type == "Capture")
+ ret.attack.Capture = {
+ "value": getAttackStat(type,"Value"),
+ };
+ else
+ ret.attack[type] = {
+ "hack": getAttackStat(type, "Hack"),
+ "pierce": getAttackStat(type, "Pierce"),
+ "crush": getAttackStat(type, "Crush"),
+ "minRange": getAttackStat(type, "MinRange"),
+ "maxRange": getAttackStat(type, "MaxRange"),
+ "elevationBonus": getAttackStat(type, "ElevationBonus"),
+ };
+ ret.attack[type].repeatTime = +(template.Attack[type].RepeatTime || 0);
}
}
diff --git a/binaries/data/mods/public/gui/common/functions_utility.js b/binaries/data/mods/public/gui/common/functions_utility.js
index 536a43989c..b6370023cc 100644
--- a/binaries/data/mods/public/gui/common/functions_utility.js
+++ b/binaries/data/mods/public/gui/common/functions_utility.js
@@ -166,12 +166,16 @@ function initGameSpeeds()
// ====================================================================
// Convert integer color values to string (for use in GUI objects)
-function rgbToGuiColor(color)
+function rgbToGuiColor(color, alpha)
{
+ var ret;
if (color && ("r" in color) && ("g" in color) && ("b" in color))
- return color.r + " " + color.g + " " + color.b;
-
- return "0 0 0";
+ ret = color.r + " " + color.g + " " + color.b;
+ else
+ ret = "0 0 0";
+ if (alpha)
+ ret += " " + alpha;
+ return ret;
}
// ====================================================================
diff --git a/binaries/data/mods/public/gui/common/tooltips.js b/binaries/data/mods/public/gui/common/tooltips.js
index 278d134f3f..c7ed20d779 100644
--- a/binaries/data/mods/public/gui/common/tooltips.js
+++ b/binaries/data/mods/public/gui/common/tooltips.js
@@ -140,6 +140,7 @@ function getAttackTypeLabel(type)
if (type === "Charge") return translate("Charge Attack:");
if (type === "Melee") return translate("Melee Attack:");
if (type === "Ranged") return translate("Ranged Attack:");
+ if (type === "Capture") return translate("Capture Attack:");
warn(sprintf("Internationalization: Unexpected attack type found with code ā%(attackType)sā. This attack type must be internationalized.", { attackType: type }));
return translate("Attack:");
@@ -169,6 +170,15 @@ function getAttackTooltip(template)
});
var attackLabel = txtFormats.header[0] + getAttackTypeLabel(type) + txtFormats.header[1];
+ if (type == "Capture")
+ {
+ attacks.push(sprintf(translate("%(attackLabel)s %(details)s, %(rate)s"), {
+ attackLabel: attackLabel,
+ details: template.attack[type].value,
+ rate: rate
+ }));
+ continue;
+ }
if (type != "Ranged")
{
attacks.push(sprintf(translate("%(attackLabel)s %(details)s, %(rate)s"), {
@@ -206,7 +216,7 @@ function getAttackTooltip(template)
}));
}
- return attacks.join(translate(", "));
+ return attacks.join("\n");
}
/**
diff --git a/binaries/data/mods/public/gui/session/selection_details.js b/binaries/data/mods/public/gui/session/selection_details.js
index 880ac99ccf..ca0d135189 100644
--- a/binaries/data/mods/public/gui/session/selection_details.js
+++ b/binaries/data/mods/public/gui/session/selection_details.js
@@ -58,6 +58,7 @@ function displaySingle(entState, template)
}
// Hitpoints
+ Engine.GetGUIObjectByName("healthSection").hidden = !entState.hitpoints;
if (entState.hitpoints)
{
var unitHealthBar = Engine.GetGUIObjectByName("healthBar");
@@ -79,21 +80,44 @@ function displaySingle(entState, template)
hitpoints: Math.ceil(entState.hitpoints),
maxHitpoints: entState.maxHitpoints
});
- Engine.GetGUIObjectByName("healthSection").hidden = false;
}
- else
+
+ // CapturePoints
+ Engine.GetGUIObjectByName("captureSection").hidden = !entState.capturePoints;
+ if (entState.capturePoints)
{
- Engine.GetGUIObjectByName("healthSection").hidden = true;
+ let setCaptureBarPart = function(playerID, startSize)
+ {
+ var unitCaptureBar = Engine.GetGUIObjectByName("captureBar["+playerID+"]");
+ var sizeObj = unitCaptureBar.size;
+ sizeObj.rleft = startSize;
+
+ var size = 100*Math.max(0, Math.min(1, entState.capturePoints[playerID] / entState.maxCapturePoints));
+ sizeObj.rright = startSize + size;
+ unitCaptureBar.size = sizeObj;
+ unitCaptureBar.sprite = "color: " + rgbToGuiColor(g_Players[playerID].color, 128);
+ unitCaptureBar.hidden=false;
+ return startSize + size;
+ }
+
+ // first handle the owner's points, to keep those points on the left for clarity
+ let size = setCaptureBarPart(entState.player, 0);
+
+ for (let i in entState.capturePoints)
+ if (i != entState.player)
+ size = setCaptureBarPart(i, size);
+
+
+ Engine.GetGUIObjectByName("captureStats").caption = sprintf(translate("%(capturePoints)s / %(maxCapturePoints)s"), {
+ capturePoints: Math.ceil(entState.capturePoints[entState.player]),
+ maxCapturePoints: entState.maxCapturePoints
+ });
}
// TODO: Stamina
- var player = Engine.GetPlayerID();
- if (entState.stamina && (entState.player == player || g_DevSettings.controlAll))
- Engine.GetGUIObjectByName("staminaSection").hidden = false;
- else
- Engine.GetGUIObjectByName("staminaSection").hidden = true;
// Experience
+ Engine.GetGUIObjectByName("experience").hidden = !entState.promotion;
if (entState.promotion)
{
var experienceBar = Engine.GetGUIObjectByName("experienceBar");
@@ -112,14 +136,10 @@ function displaySingle(entState, template)
experience: "[font=\"sans-bold-13\"]" + translate("Experience:") + "[/font]",
current: Math.floor(entState.promotion.curr)
});
- Engine.GetGUIObjectByName("experience").hidden = false;
- }
- else
- {
- Engine.GetGUIObjectByName("experience").hidden = true;
}
// Resource stats
+ Engine.GetGUIObjectByName("resourceSection").hidden = !entState.resourceSupply;
if (entState.resourceSupply)
{
var resources = entState.resourceSupply.isInfinite ? translate("ā") : // Infinity symbol
@@ -136,15 +156,10 @@ function displaySingle(entState, template)
Engine.GetGUIObjectByName("resourceStats").caption = resources;
if (entState.hitpoints)
- Engine.GetGUIObjectByName("resourceSection").size = Engine.GetGUIObjectByName("staminaSection").size;
+ Engine.GetGUIObjectByName("resourceSection").size = Engine.GetGUIObjectByName("captureSection").size;
else
Engine.GetGUIObjectByName("resourceSection").size = Engine.GetGUIObjectByName("healthSection").size;
- Engine.GetGUIObjectByName("resourceSection").hidden = false;
- }
- else
- {
- Engine.GetGUIObjectByName("resourceSection").hidden = true;
}
// Resource carrying
@@ -290,20 +305,29 @@ function displayMultiple(selection, template)
{
var averageHealth = 0;
var maxHealth = 0;
+ var maxCapturePoints = 0;
+ var capturePoints = (new Array(9)).fill(0);
+ var playerID = 0;
for (var i = 0; i < selection.length; i++)
{
var entState = GetEntityState(selection[i])
- if (entState)
+ if (!entState)
+ continue;
+ playerID = entState.player; // trust that all selected entities have the same owner
+ if (entState.hitpoints)
{
- if (entState.hitpoints)
- {
- averageHealth += entState.hitpoints;
- maxHealth += entState.maxHitpoints;
- }
+ averageHealth += entState.hitpoints;
+ maxHealth += entState.maxHitpoints;
+ }
+ if (entState.capturePoints)
+ {
+ maxCapturePoints += entState.maxCapturePoints;
+ capturePoints = entState.capturePoints.map(function(v, i) { return v + capturePoints[i]; });
}
}
+ Engine.GetGUIObjectByName("healthMultiple").hidden = averageHealth <= 0;
if (averageHealth > 0)
{
var unitHealthBar = Engine.GetGUIObjectByName("healthBarMultiple");
@@ -313,13 +337,37 @@ function displayMultiple(selection, template)
var hitpointsLabel = "[font=\"sans-bold-13\"]" + translate("Hitpoints:") + "[/font]"
var hitpoints = sprintf(translate("%(label)s %(current)s / %(max)s"), { label: hitpointsLabel, current: averageHealth, max: maxHealth });
- var healthMultiple = Engine.GetGUIObjectByName("healthMultiple");
- healthMultiple.tooltip = hitpoints;
- healthMultiple.hidden = false;
+ Engine.GetGUIObjectByName("healthMultiple").tooltip = hitpoints;
}
- else
+
+ Engine.GetGUIObjectByName("captureMultiple").hidden = maxCapturePoints <= 0;
+ if (maxCapturePoints > 0)
{
- Engine.GetGUIObjectByName("healthMultiple").hidden = true;
+ let setCaptureBarPart = function(playerID, startSize)
+ {
+ var unitCaptureBar = Engine.GetGUIObjectByName("captureBarMultiple["+playerID+"]");
+ var sizeObj = unitCaptureBar.size;
+ sizeObj.rtop = startSize;
+
+ var size = 100*Math.max(0, Math.min(1, capturePoints[playerID] / maxCapturePoints));
+ sizeObj.rbottom = startSize + size;
+ unitCaptureBar.size = sizeObj;
+ unitCaptureBar.sprite = "color: " + rgbToGuiColor(g_Players[playerID].color, 128);
+ unitCaptureBar.hidden=false;
+ return startSize + size;
+ }
+
+ let size = 0;
+ for (let i in entState.capturePoints)
+ if (i != playerID)
+ size = setCaptureBarPart(i, size);
+
+ // last handle the owner's points, to keep those points on the bottom for clarity
+ setCaptureBarPart(playerID, size);
+
+ var capturePointsLabel = "[font=\"sans-bold-13\"]" + translate("Capture points:") + "[/font]"
+ var capturePointsTooltip = sprintf(translate("%(label)s %(current)s / %(max)s"), { label: capturePointsLabel, current: Math.ceil(capturePoints[playerID]), max: Math.ceil(maxCapturePoints) });
+ Engine.GetGUIObjectByName("captureMultiple").tooltip = capturePointsTooltip;
}
// TODO: Stamina
diff --git a/binaries/data/mods/public/gui/session/selection_panels_middle/multiple_details_area.xml b/binaries/data/mods/public/gui/session/selection_panels_middle/multiple_details_area.xml
index 20a84e472d..9f7dd3ecea 100644
--- a/binaries/data/mods/public/gui/session/selection_panels_middle/multiple_details_area.xml
+++ b/binaries/data/mods/public/gui/session/selection_panels_middle/multiple_details_area.xml
@@ -33,12 +33,13 @@
-
-
diff --git a/binaries/data/mods/public/gui/session/selection_panels_middle/single_details_area.xml b/binaries/data/mods/public/gui/session/selection_panels_middle/single_details_area.xml
index 990956525e..6d7a9a09c6 100644
--- a/binaries/data/mods/public/gui/session/selection_panels_middle/single_details_area.xml
+++ b/binaries/data/mods/public/gui/session/selection_panels_middle/single_details_area.xml
@@ -20,16 +20,17 @@
-
-
-
- Stamina:
+
+
+
+ Capture points:
-
-
+
+
-
-
+
+
+
diff --git a/binaries/data/mods/public/gui/session/unit_actions.js b/binaries/data/mods/public/gui/session/unit_actions.js
index ce693b0b76..08eda3b501 100644
--- a/binaries/data/mods/public/gui/session/unit_actions.js
+++ b/binaries/data/mods/public/gui/session/unit_actions.js
@@ -99,9 +99,7 @@ var unitActions =
{
if (!entState.attack || !targetState.hitpoints)
return false;
- if (playerCheck(entState, targetState, ["Neutral", "Enemy"]))
- return {"possible": Engine.GuiInterfaceCall("CanAttack", {"entity": entState.id, "target": targetState.id})};
- return false;
+ return {"possible": Engine.GuiInterfaceCall("CanAttack", {"entity": entState.id, "target": targetState.id})};
},
"hotkeyActionCheck": function(target)
{
@@ -672,6 +670,13 @@ var g_EntityCommands =
"icon": "kill_small.png"
};
+ if (entState.capturePoints && entState.capturePoints[entState.player] < entState.maxCapturePoints / 2)
+ return {
+ "tooltip": translate("You cannot destroy this entity as you own less than half the capture points"),
+ "icon": "kill_small.png"
+ };
+
+
return {
"tooltip": translate("Delete"),
"icon": "kill_small.png"
diff --git a/binaries/data/mods/public/simulation/components/AIProxy.js b/binaries/data/mods/public/simulation/components/AIProxy.js
index 008fcb3921..9b3606a1b9 100644
--- a/binaries/data/mods/public/simulation/components/AIProxy.js
+++ b/binaries/data/mods/public/simulation/components/AIProxy.js
@@ -111,6 +111,13 @@ AIProxy.prototype.OnHealthChanged = function(msg)
this.changes.hitpoints = msg.to;
};
+AIProxy.prototype.OnCapturePointsChanged = function(msg)
+{
+ if (!this.NotifyChange())
+ return;
+ this.changes.capturePoints = msg.capturePoints;
+};
+
AIProxy.prototype.OnUnitIdleChanged = function(msg)
{
if (!this.NotifyChange())
diff --git a/binaries/data/mods/public/simulation/components/Attack.js b/binaries/data/mods/public/simulation/components/Attack.js
index 491a384d7d..9a0acafe7f 100644
--- a/binaries/data/mods/public/simulation/components/Attack.js
+++ b/binaries/data/mods/public/simulation/components/Attack.js
@@ -158,6 +158,20 @@ Attack.prototype.Schema =
"" +
"" +
"" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" + // TODO: it shouldn't be stretched
+ "" +
+ "" +
+ Attack.prototype.bonusesSchema +
+ Attack.prototype.preferredClassesSchema +
+ Attack.prototype.restrictedClassesSchema +
+ "" +
+ "" +
+ "" +
"" +
"" +
"" +
@@ -198,6 +212,7 @@ Attack.prototype.GetAttackTypes = function()
if (this.template.Charge) ret.push("Charge");
if (this.template.Melee) ret.push("Melee");
if (this.template.Ranged) ret.push("Ranged");
+ if (this.template.Capture) ret.push("Capture");
return ret;
};
@@ -223,6 +238,10 @@ Attack.prototype.GetRestrictedClasses = function(type)
Attack.prototype.CanAttack = function(target)
{
+ var cmpArmour = Engine.QueryInterface(target, IID_DamageReceiver);
+ if (!cmpArmour)
+ return false;
+
var cmpFormation = Engine.QueryInterface(target, IID_Formation);
if (cmpFormation)
return true;
@@ -309,23 +328,40 @@ Attack.prototype.GetBestAttackAgainst = function(target)
if (cmpFormation)
return this.GetBestAttack();
- const cmpIdentity = Engine.QueryInterface(target, IID_Identity);
+ var cmpIdentity = Engine.QueryInterface(target, IID_Identity);
if (!cmpIdentity)
return undefined;
- const targetClasses = cmpIdentity.GetClassesList();
- const isTargetClass = function (value, i, a) { return targetClasses.indexOf(value) != -1; };
- const types = this.GetAttackTypes();
- const attack = this;
- const isAllowed = function (value, i, a) { return !attack.GetRestrictedClasses(value).some(isTargetClass); }
- const isPreferred = function (value, i, a) { return attack.GetPreferredClasses(value).some(isTargetClass); }
- const byPreference = function (a, b) { return (types.indexOf(a) + (isPreferred(a) ? types.length : 0) ) - (types.indexOf(b) + (isPreferred(b) ? types.length : 0) ); }
+
+ var targetClasses = cmpIdentity.GetClassesList();
+ var isTargetClass = function (className) { return targetClasses.indexOf(className) != -1; };
// Always slaughter domestic animals instead of using a normal attack
if (isTargetClass("Domestic") && this.template.Slaughter)
return "Slaughter";
- return types.filter(isAllowed).sort(byPreference).pop();
+ var attack = this;
+ var isAllowed = function (type) { return !attack.GetRestrictedClasses(type).some(isTargetClass); }
+
+ var types = this.GetAttackTypes().filter(isAllowed);
+
+ // check if the target is capturable
+ var captureIndex = types.indexOf("Capture")
+ if (captureIndex != -1)
+ {
+ var cmpCapturable = Engine.QueryInterface(target, IID_Capturable);
+ var cmpPlayer = QueryOwnerInterface(this.entity);
+ if (cmpPlayer && cmpCapturable && cmpCapturable.CanCapture(cmpPlayer.GetPlayerID()))
+ return "Capture";
+ // not captureable, so remove this attack
+ types.splice(captureIndex, 1);
+ }
+
+ var isPreferred = function (className) { return attack.GetPreferredClasses(className).some(isTargetClass); }
+ var byPreference = function (a, b) { return (types.indexOf(a) + (isPreferred(a) ? types.length : 0) ) - (types.indexOf(b) + (isPreferred(b) ? types.length : 0) ); }
+
+
+ return types.sort(byPreference).pop();
};
Attack.prototype.CompareEntitiesByPreference = function(a, b)
@@ -367,7 +403,10 @@ Attack.prototype.GetAttackStrengths = function(type)
{
return ApplyValueModificationsToEntity("Attack/" + type + splash + "/" + damageType, +(template[damageType] || 0), self.entity);
};
-
+
+ if (type == "Capture")
+ return {value: applyMods("Value")};
+
return {
hack: applyMods("Hack"),
pierce: applyMods("Pierce"),
@@ -517,6 +556,25 @@ Attack.prototype.PerformAttack = function(type, target)
var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
cmpTimer.SetTimeout(this.entity, IID_Attack, "MissileHit", timeToTarget*1000, {"type": type, "target": target, "position": realTargetPosition, "direction": missileDirection, "projectileId": id, "playerId":playerId});
}
+ else if (type == "Capture")
+ {
+ var multiplier = this.GetAttackBonus(type, target);
+ var cmpHealth = Engine.QueryInterface(target, IID_Health);
+ if (!cmpHealth || cmpHealth.GetHitpoints() == 0)
+ return;
+ multiplier *= cmpHealth.GetMaxHitpoints() / cmpHealth.GetHitpoints();
+
+ var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
+ if (!cmpOwnership || cmpOwnership.GetOwner() == -1)
+ return;
+ var owner = cmpOwnership.GetOwner();
+ var cmpCapturable = Engine.QueryInterface(target, IID_Capturable);
+ if (!cmpCapturable || !cmpCapturable.CanCapture(owner))
+ return;
+
+ var strength = this.GetAttackStrengths("Capture").value;
+ cmpCapturable.Reduce(strength * multiplier, owner);
+ }
else
{
// Melee attack - hurt the target immediately
@@ -585,7 +643,7 @@ Attack.prototype.MissileHit = function(data, lateness)
// If friendlyFire isn't enabled, get all player enemies to pass to "Damage.CauseSplashDamage".
if (friendlyFire == "false")
{
- var cmpPlayer = Engine.QueryInterface(Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetPlayerByID(data.playerId), IID_Player)
+ var cmpPlayer = QueryPlayerIDInterface(data.playerId);
playersToDamage = cmpPlayer.GetEnemies();
}
// Damage the units.
@@ -607,7 +665,7 @@ Attack.prototype.MissileHit = function(data, lateness)
else
{
// If we didn't hit the main target look for nearby units
- var cmpPlayer = Engine.QueryInterface(Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetPlayerByID(data.playerId), IID_Player)
+ var cmpPlayer = QueryPlayerIDInterface(data.playerId);
var ents = Damage.EntitiesNearPoint(Vector2D.from3D(data.position), targetPosition.horizDistanceTo(data.position) * 2, cmpPlayer.GetEnemies());
for (var i = 0; i < ents.length; i++)
diff --git a/binaries/data/mods/public/simulation/components/Capturable.js b/binaries/data/mods/public/simulation/components/Capturable.js
new file mode 100644
index 0000000000..2616a818cc
--- /dev/null
+++ b/binaries/data/mods/public/simulation/components/Capturable.js
@@ -0,0 +1,212 @@
+function Capturable() {}
+
+Capturable.prototype.Schema =
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "";
+
+Capturable.prototype.Init = function()
+{
+ // Cache this value
+ this.maxCp = +this.template.CapturePoints;
+ this.cp = [];
+ this.startRegenTimer();
+};
+
+//// Interface functions ////
+
+/**
+ * Returns the current capture points array
+ */
+Capturable.prototype.GetCapturePoints = function()
+{
+ return this.cp;
+};
+
+Capturable.prototype.GetMaxCapturePoints = function()
+{
+ return this.maxCp;
+};
+
+/**
+ * Set the new capture points, used for cloning entities
+ * The caller should assure that the sum of capture points
+ * matches the max.
+ */
+Capturable.prototype.SetCapturePoints = function(capturePointsArray)
+{
+ this.cp = capturePointsArray;
+};
+
+/**
+ * Reduces the amount of capture points of an entity,
+ * in favour of the player of the source
+ * Returns the number of capture points actually taken
+ */
+Capturable.prototype.Reduce = function(amount, playerID)
+{
+ var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
+ if (!cmpOwnership || cmpOwnership.GetOwner() == -1)
+ return 0;
+
+ var cmpPlayerSource = QueryPlayerIDInterface(playerID);
+ if (!cmpPlayerSource)
+ return 0;
+
+ // Before changing the value, activate Fogging if necessary to hide changes
+ var cmpFogging = Engine.QueryInterface(this.entity, IID_Fogging);
+ if (cmpFogging)
+ cmpFogging.Activate();
+
+ var enemiesFilter = function(v, i) { return v > 0 && !cmpPlayerSource.IsAlly(i); };
+ var numberOfEnemies = this.cp.filter(enemiesFilter).length;
+
+ if (numberOfEnemies == 0)
+ return 0;
+
+ // distribute the capture points over all enemies
+ var distributedAmount = amount / numberOfEnemies;
+ for (let i in this.cp)
+ {
+ if (cmpPlayerSource.IsAlly(i))
+ continue;
+ if (this.cp[i] > distributedAmount)
+ this.cp[i] -= distributedAmount;
+ else
+ this.cp[i] = 0;
+ }
+
+ // give all cp taken to the player
+ var takenCp = this.maxCp - this.cp.reduce(function(a, b) { return a + b; });
+ this.cp[playerID] += takenCp;
+
+ this.startRegenTimer();
+
+ Engine.PostMessage(this.entity, MT_CapturePointsChanged, { "capturePoints": this.cp })
+
+ if (this.cp[cmpOwnership.GetOwner()] > 0)
+ return takenCp;
+
+ // if all cp has been taken from the owner, convert it to the best player
+ var bestPlayer = 0;
+ for (let i in this.cp)
+ if (this.cp[i] >= this.cp[bestPlayer])
+ bestPlayer = +i;
+
+ cmpOwnership.SetOwner(bestPlayer);
+
+ return takenCp;
+};
+
+/**
+ * Check if the source can (re)capture points from this building
+ */
+Capturable.prototype.CanCapture = function(playerID)
+{
+ var cmpPlayerSource = QueryPlayerIDInterface(playerID);
+
+ if (!cmpPlayerSource)
+ warn(source + " has no player component defined on its owner ");
+ var cp = this.GetCapturePoints()
+ var sourceEnemyCp = 0;
+ for (let i in this.GetCapturePoints())
+ if (!cmpPlayerSource.IsAlly(i))
+ sourceEnemyCp += cp[i];
+ return sourceEnemyCp > 0;
+};
+
+//// Private functions ////
+
+Capturable.prototype.GetRegenRate = function()
+{
+ var regenRate = +this.template.RegenRate;
+ regenRate = ApplyValueModificationsToEntity("Capturable/RegenRate", regenRate, this.entity);
+
+ var cmpGarrisonHolder = Engine.QueryInterface(this.entity, IID_GarrisonHolder);
+ if (!cmpGarrisonHolder)
+ return regenRate;
+
+ var garrisonRegenRate = +this.template.GarrisonRegenRate;
+ garrisonRegenRate = ApplyValueModificationsToEntity("Capturable/GarrisonRegenRate", garrisonRegenRate, this.entity);
+ var garrisonedUnits = cmpGarrisonHolder.GetEntities().length;
+ return regenRate + garrisonedUnits * garrisonRegenRate;
+};
+
+Capturable.prototype.RegenCapturePoints = function()
+{
+ var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
+ if (!cmpOwnership || cmpOwnership.GetOwner() == -1)
+ return;
+
+ var takenCp = this.Reduce(this.GetRegenRate(), cmpOwnership.GetOwner())
+ if (takenCp > 0)
+ return;
+
+ // no capture points taken, stop the timer
+ var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
+ cmpTimer.CancelTimer(this.regenTimer);
+ this.regenTimer = 0;
+};
+
+/**
+ * Start the regeneration timer when no timer exists
+ * When nothing can be regenerated (f.e. because the
+ * rate is 0, or because it is fully regenerated),
+ * the timer stops automatically after one execution.
+ */
+Capturable.prototype.startRegenTimer = function()
+{
+ if (this.regenTimer)
+ return;
+ var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
+ this.regenTimer = cmpTimer.SetInterval(this.entity, IID_Capturable, "RegenCapturePoints", 1000, 1000, null);
+};
+
+//// Message Listeners ////
+
+Capturable.prototype.OnValueModification = function(msg)
+{
+ if (msg.component != "Capturable")
+ return;
+
+ var oldMaxCp = this.GetMaxCapturePoints();
+ this.maxCp = ApplyValueModificationsToEntity("Capturable/Max", +this.template.Max, this.entity);
+ if (oldMaxCp == this.maxCp)
+ return;
+
+ var scale = this.maxCp / oldMaxCp;
+ for (let i in this.cp)
+ this.cp[i] *= scale;
+ Engine.PostMessage(this.entity, MT_CapturePointsChanged, { "capturePoints": this.cp });
+ this.startRegenTimer();
+};
+
+Capturable.prototype.OnGarrisonedUnitsChanged = function(msg)
+{
+ this.startRegenTimer();
+};
+
+Capturable.prototype.OnOwnershipChanged = function(msg)
+{
+ this.startRegenTimer();
+
+ if (msg.from != -1)
+ return;
+
+ // initialise the capture points when created
+ this.cp = [];
+ var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
+ for (let i = 0; i < cmpPlayerManager.GetNumPlayers(); ++i)
+ if (i == msg.to)
+ this.cp[i] = this.maxCp;
+ else
+ this.cp[i] = 0;
+};
+
+Engine.RegisterComponentType(IID_Capturable, "Capturable", Capturable);
diff --git a/binaries/data/mods/public/simulation/components/Fogging.js b/binaries/data/mods/public/simulation/components/Fogging.js
index c897e7aa52..0b75d06097 100644
--- a/binaries/data/mods/public/simulation/components/Fogging.js
+++ b/binaries/data/mods/public/simulation/components/Fogging.js
@@ -125,6 +125,13 @@ Fogging.prototype.LoadMirage = function(player)
cmpHealth.IsRepairable() && (cmpHealth.GetHitpoints() < cmpHealth.GetMaxHitpoints())
);
+ var cmpCapturable = Engine.QueryInterface(this.entity, IID_Capturable);
+ if (cmpCapturable)
+ cmpMirage.CopyCapturable(
+ cmpCapturable.GetCapturePoints(),
+ cmpCapturable.GetMaxCapturePoints()
+ );
+
var cmpResourceSupply = Engine.QueryInterface(this.entity, IID_ResourceSupply);
if (cmpResourceSupply)
cmpMirage.CopyResourceSupply(
diff --git a/binaries/data/mods/public/simulation/components/GuiInterface.js b/binaries/data/mods/public/simulation/components/GuiInterface.js
index e22fc3a109..d9c33e0036 100644
--- a/binaries/data/mods/public/simulation/components/GuiInterface.js
+++ b/binaries/data/mods/public/simulation/components/GuiInterface.js
@@ -267,6 +267,18 @@ GuiInterface.prototype.GetEntityState = function(player, ent)
ret.needsRepair = cmpMirage.NeedsRepair();
}
+ var cmpCapturable = Engine.QueryInterface(ent, IID_Capturable);
+ if (cmpCapturable)
+ {
+ ret.capturePoints = cmpCapturable.GetCapturePoints();
+ ret.maxCapturePoints = cmpCapturable.GetMaxCapturePoints();
+ }
+ if (cmpMirage && cmpMirage.Capturable())
+ {
+ ret.capturePoints = cmpMirage.GetCapturePoints();
+ ret.maxCapturePoints = cmpMirage.GetMaxCapturePoints();
+ }
+
var cmpBuilder = Engine.QueryInterface(ent, IID_Builder);
if (cmpBuilder)
ret.builder = true;
@@ -1701,8 +1713,22 @@ GuiInterface.prototype.CanAttack = function(player, data)
var cmpAttack = Engine.QueryInterface(data.entity, IID_Attack);
if (!cmpAttack)
return false;
+ var cmpEntityPlayer = QueryOwnerInterface(data.entity, IID_Player);
+ var cmpTargetPlayer = QueryOwnerInterface(data.target, IID_Player);
+ if (!cmpEntityPlayer || !cmpTargetPlayer)
+ return false;
- return cmpAttack.CanAttack(data.target);
+
+ // if the owner is an enemy, it's up to the attack component to decide
+ if (!cmpEntityPlayer.IsAlly(cmpTargetPlayer.GetPlayerID()))
+ return cmpAttack.CanAttack(data.target);
+
+ // if the owner is an ally, we could still want to capture some capture points back
+ var cmpCapturable = Engine.QueryInterface(data.target, IID_Capturable);
+ if (cmpCapturable && cmpCapturable.CanCapture(cmpEntityPlayer.GetPlayerID()) && cmpAttack.GetAttackTypes().indexOf("Capture") != -1)
+ return cmpAttack.CanAttack(data.target);
+
+ return false;
};
/*
diff --git a/binaries/data/mods/public/simulation/components/Mirage.js b/binaries/data/mods/public/simulation/components/Mirage.js
index 5a08620f60..6f876356c6 100644
--- a/binaries/data/mods/public/simulation/components/Mirage.js
+++ b/binaries/data/mods/public/simulation/components/Mirage.js
@@ -21,6 +21,10 @@ Mirage.prototype.Init = function()
this.hitpoints = null;
this.needsRepair = null;
+ this.capturable = false;
+ this.capturePoints = [];
+ this.maxCapturePoints = 0;
+
this.resourceSupply = false;
this.maxAmount = null;
this.amount = null;
@@ -94,6 +98,30 @@ Mirage.prototype.NeedsRepair = function()
return this.needsRepair;
};
+// Capture data
+
+Mirage.prototype.CopyCapturable = function(capturePoints, maxCapturePoints)
+{
+ this.capturable = true;
+ this.capturePoints = capturePoints;
+ this.maxCapturePoints = maxCapturePoints;
+};
+
+Mirage.prototype.Capturable = function()
+{
+ return this.capturable;
+};
+
+Mirage.prototype.GetMaxCapturePoints = function()
+{
+ return this.maxCapturePoints;
+};
+
+Mirage.prototype.GetCapturePoints = function()
+{
+ return this.capturePoints;
+};
+
// ResourceSupply data
Mirage.prototype.CopyResourceSupply = function(maxAmount, amount, type, isInfinite)
diff --git a/binaries/data/mods/public/simulation/components/Pack.js b/binaries/data/mods/public/simulation/components/Pack.js
index 0861b46599..1be41604cd 100644
--- a/binaries/data/mods/public/simulation/components/Pack.js
+++ b/binaries/data/mods/public/simulation/components/Pack.js
@@ -148,6 +148,16 @@ Pack.prototype.PackProgress = function(data, lateness)
var cmpNewOwnership = Engine.QueryInterface(newEntity, IID_Ownership);
cmpNewOwnership.SetOwner(cmpOwnership.GetOwner());
+ // rescale capture points
+ var cmpCapturable = Engine.QueryInterface(this.entity, IID_Capturable);
+ var cmpNewCapturable = Engine.QueryInterface(newEntity, IID_Capturable);
+ if (cmpCapturable && cmpNewCapturable)
+ {
+ let scale = cmpCapturable.GetMaxCapturePoints() / cmpNewCapturable.GetMaxCapturePoints();
+ let newCp = cmpCapturable.GetCapturePoints().map(function (v) { return v / scale; });
+ cmpNewCapturable.SetCapturePoints(newCp);
+ }
+
// Maintain current health level
var cmpHealth = Engine.QueryInterface(this.entity, IID_Health);
var cmpNewHealth = Engine.QueryInterface(newEntity, IID_Health);
diff --git a/binaries/data/mods/public/simulation/components/TerritoryDecay.js b/binaries/data/mods/public/simulation/components/TerritoryDecay.js
index d0089de1e6..1f2fa2624d 100644
--- a/binaries/data/mods/public/simulation/components/TerritoryDecay.js
+++ b/binaries/data/mods/public/simulation/components/TerritoryDecay.js
@@ -1,7 +1,7 @@
function TerritoryDecay() {}
TerritoryDecay.prototype.Schema =
- "" +
+ "" +
"" +
"";
@@ -33,8 +33,6 @@ TerritoryDecay.prototype.IsConnected = function()
var tileOwner = cmpTerritoryManager.GetOwner(pos.x, pos.y);
if (tileOwner != cmpOwnership.GetOwner())
return false;
- // TODO: this should probably use the same territory restriction
- // logic as BuildRestrictions, to handle allies etc
return cmpTerritoryManager.IsConnected(pos.x, pos.y);
};
@@ -64,7 +62,7 @@ TerritoryDecay.prototype.UpdateDecayState = function()
if (connected)
var decaying = false;
else
- var decaying = (Math.round(ApplyValueModificationsToEntity("TerritoryDecay/HealthDecayRate", +this.template.HealthDecayRate, this.entity)) > 0);
+ var decaying = (Math.round(ApplyValueModificationsToEntity("TerritoryDecay/DecayRate", +this.template.DecayRate, this.entity)) > 0);
if (decaying === this.decaying)
return;
this.decaying = decaying;
@@ -88,13 +86,17 @@ TerritoryDecay.prototype.OnOwnershipChanged = function(msg)
TerritoryDecay.prototype.Decay = function()
{
- var cmpHealth = Engine.QueryInterface(this.entity, IID_Health);
- if (!cmpHealth)
+ var cmpCapturable = Engine.QueryInterface(this.entity, IID_Capturable);
+ if (!cmpCapturable)
return; // error
- var decayRate = ApplyValueModificationsToEntity("TerritoryDecay/HealthDecayRate", +this.template.HealthDecayRate, this.entity);
+ var decayRate = ApplyValueModificationsToEntity(
+ "TerritoryDecay/DecayRate",
+ +this.template.DecayRate,
+ this.entity);
- cmpHealth.Reduce(Math.round(decayRate));
+ // Reduce capture points in favour of Gaia
+ cmpCapturable.Reduce(decayRate, 0);
};
Engine.RegisterComponentType(IID_TerritoryDecay, "TerritoryDecay", TerritoryDecay);
diff --git a/binaries/data/mods/public/simulation/components/UnitAI.js b/binaries/data/mods/public/simulation/components/UnitAI.js
index 84c500e1b7..7b21541322 100644
--- a/binaries/data/mods/public/simulation/components/UnitAI.js
+++ b/binaries/data/mods/public/simulation/components/UnitAI.js
@@ -5559,20 +5559,25 @@ UnitAI.prototype.CanAttack = function(target, forceResponse)
return false;
var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
- if (!cmpOwnership)
+ if (!cmpOwnership || cmpOwnership.GetOwner() < 0)
return false;
+ var owner = cmpOwnership.GetOwner();
// Verify that the target is an attackable resource supply like a domestic animal
// or that it isn't owned by an ally of this entity's player or is responding to
// an attack.
- var owner = cmpOwnership.GetOwner();
- if (!this.MustKillGatherTarget(target)
- && !(IsOwnedByEnemyOfPlayer(owner, target)
- || IsOwnedByNeutralOfPlayer(owner, target)
- || (forceResponse && !IsOwnedByPlayer(owner, target))))
- return false;
+ if (this.MustKillGatherTarget(target))
+ return true;
- return true;
+ var cmpCapturable = Engine.QueryInterface(target, IID_Capturable);
+ if (cmpCapturable && cmpCapturable.CanCapture(owner) && cmpAttack.GetAttackTypes().indexOf("Capture") != -1)
+ return true;
+
+ if (IsOwnedByEnemyOfPlayer(owner, target) || IsOwnedByNeutralOfPlayer(owner, target))
+ return true;
+ if (forceResponse && !IsOwnedByPlayer(owner, target))
+ return true;
+ return false;
};
UnitAI.prototype.CanGarrison = function(target)
diff --git a/binaries/data/mods/public/simulation/components/interfaces/Capturable.js b/binaries/data/mods/public/simulation/components/interfaces/Capturable.js
new file mode 100644
index 0000000000..4368331103
--- /dev/null
+++ b/binaries/data/mods/public/simulation/components/interfaces/Capturable.js
@@ -0,0 +1,5 @@
+Engine.RegisterInterface("Capturable");
+
+// Message in the form of {"capturePoints": [gaia, p1, p2, ...]}
+Engine.RegisterMessageType("CapturePointsChanged");
+
diff --git a/binaries/data/mods/public/simulation/data/technologies/decay_outpost.json b/binaries/data/mods/public/simulation/data/technologies/decay_outpost.json
index 404772f8db..d79e022c99 100644
--- a/binaries/data/mods/public/simulation/data/technologies/decay_outpost.json
+++ b/binaries/data/mods/public/simulation/data/technologies/decay_outpost.json
@@ -7,7 +7,7 @@
"icon": "blocks_three.png",
"researchTime": 40,
"tooltip": "Territory decay -50% for Outposts.",
- "modifications": [{"value": "TerritoryDecay/HealthDecayRate", "multiply": 0.5}],
+ "modifications": [{"value": "TerritoryDecay/DecayRate", "multiply": 0.5}],
"affects": ["Outpost"],
"soundComplete": "interface/alarm/alarm_upgradearmory.xml"
}
diff --git a/binaries/data/mods/public/simulation/data/technologies/romans/decay_logistics.json b/binaries/data/mods/public/simulation/data/technologies/romans/decay_logistics.json
index 06ac834d9e..06ca4da19a 100644
--- a/binaries/data/mods/public/simulation/data/technologies/romans/decay_logistics.json
+++ b/binaries/data/mods/public/simulation/data/technologies/romans/decay_logistics.json
@@ -7,7 +7,7 @@
"icon": "handcart_empty.png",
"researchTime": 40,
"tooltip": "Entrenched Camps and Siege Walls decay 50% slower.",
- "modifications": [{"value": "TerritoryDecay/HealthDecayRate", "multiply": 0.5}],
+ "modifications": [{"value": "TerritoryDecay/DecayRate", "multiply": 0.5}],
"affects": ["ArmyCamp", "SiegeWall"],
"soundComplete": "interface/alarm/alarm_upgradearmory.xml"
}
diff --git a/binaries/data/mods/public/simulation/helpers/Commands.js b/binaries/data/mods/public/simulation/helpers/Commands.js
index e90b4d5e65..1caaa4ca45 100644
--- a/binaries/data/mods/public/simulation/helpers/Commands.js
+++ b/binaries/data/mods/public/simulation/helpers/Commands.js
@@ -349,8 +349,18 @@ var commands = {
"delete-entities": function(player, cmd, data)
{
- for each (var ent in data.entities)
+ for (let ent of data.entities)
{
+ // don't allow to delete entities who are half-captured
+ var cmpCapturable = Engine.QueryInterface(ent, IID_Capturable);
+ if (cmpCapturable)
+ {
+ var capturePoints = cmpCapturable.GetCapturePoints();
+ var maxCapturePoints = cmpCapturable.GetMaxCapturePoints();
+ if (capturePoints[player] < maxCapturePoints / 2)
+ return;
+ }
+ // either kill or delete the entity
var cmpHealth = Engine.QueryInterface(ent, IID_Health);
if (cmpHealth)
{
diff --git a/binaries/data/mods/public/simulation/templates/structures/merc_camp_egyptian.xml b/binaries/data/mods/public/simulation/templates/structures/merc_camp_egyptian.xml
index 25d5b2945f..2e57ac809d 100644
--- a/binaries/data/mods/public/simulation/templates/structures/merc_camp_egyptian.xml
+++ b/binaries/data/mods/public/simulation/templates/structures/merc_camp_egyptian.xml
@@ -60,7 +60,7 @@
- 1
+ 1
diff --git a/binaries/data/mods/public/simulation/templates/structures/ptol_mercenary_camp.xml b/binaries/data/mods/public/simulation/templates/structures/ptol_mercenary_camp.xml
index eb8f1e3cbe..adeef3af62 100644
--- a/binaries/data/mods/public/simulation/templates/structures/ptol_mercenary_camp.xml
+++ b/binaries/data/mods/public/simulation/templates/structures/ptol_mercenary_camp.xml
@@ -62,7 +62,7 @@
- 1
+ 1
diff --git a/binaries/data/mods/public/simulation/templates/structures/ptol_military_colony.xml b/binaries/data/mods/public/simulation/templates/structures/ptol_military_colony.xml
index 3ceee1edb7..a89f66c9c5 100644
--- a/binaries/data/mods/public/simulation/templates/structures/ptol_military_colony.xml
+++ b/binaries/data/mods/public/simulation/templates/structures/ptol_military_colony.xml
@@ -76,7 +76,7 @@
- 1
+ 1
80
diff --git a/binaries/data/mods/public/simulation/templates/structures/rome_army_camp.xml b/binaries/data/mods/public/simulation/templates/structures/rome_army_camp.xml
index ac90cde455..97fd2927e7 100644
--- a/binaries/data/mods/public/simulation/templates/structures/rome_army_camp.xml
+++ b/binaries/data/mods/public/simulation/templates/structures/rome_army_camp.xml
@@ -76,7 +76,7 @@
- 10
+ 10
diff --git a/binaries/data/mods/public/simulation/templates/structures/rome_siege_wall_gate.xml b/binaries/data/mods/public/simulation/templates/structures/rome_siege_wall_gate.xml
index 5b71e460dd..043c79fcca 100644
--- a/binaries/data/mods/public/simulation/templates/structures/rome_siege_wall_gate.xml
+++ b/binaries/data/mods/public/simulation/templates/structures/rome_siege_wall_gate.xml
@@ -44,7 +44,7 @@
- 1
+ 1
diff --git a/binaries/data/mods/public/simulation/templates/structures/rome_siege_wall_long.xml b/binaries/data/mods/public/simulation/templates/structures/rome_siege_wall_long.xml
index f04025e86d..d23385c521 100644
--- a/binaries/data/mods/public/simulation/templates/structures/rome_siege_wall_long.xml
+++ b/binaries/data/mods/public/simulation/templates/structures/rome_siege_wall_long.xml
@@ -62,7 +62,7 @@
- 1
+ 1
diff --git a/binaries/data/mods/public/simulation/templates/structures/rome_siege_wall_medium.xml b/binaries/data/mods/public/simulation/templates/structures/rome_siege_wall_medium.xml
index 80549e75e6..dcb9c96676 100644
--- a/binaries/data/mods/public/simulation/templates/structures/rome_siege_wall_medium.xml
+++ b/binaries/data/mods/public/simulation/templates/structures/rome_siege_wall_medium.xml
@@ -55,7 +55,7 @@
- 1
+ 1
diff --git a/binaries/data/mods/public/simulation/templates/structures/rome_siege_wall_short.xml b/binaries/data/mods/public/simulation/templates/structures/rome_siege_wall_short.xml
index 63f2bf7ca8..2ec4273180 100644
--- a/binaries/data/mods/public/simulation/templates/structures/rome_siege_wall_short.xml
+++ b/binaries/data/mods/public/simulation/templates/structures/rome_siege_wall_short.xml
@@ -42,7 +42,7 @@
- 1
+ 1
diff --git a/binaries/data/mods/public/simulation/templates/structures/rome_siege_wall_tower.xml b/binaries/data/mods/public/simulation/templates/structures/rome_siege_wall_tower.xml
index 90ad9e7b22..797589f314 100644
--- a/binaries/data/mods/public/simulation/templates/structures/rome_siege_wall_tower.xml
+++ b/binaries/data/mods/public/simulation/templates/structures/rome_siege_wall_tower.xml
@@ -36,7 +36,7 @@
- 1
+ 1
diff --git a/binaries/data/mods/public/simulation/templates/structures/rome_tent.xml b/binaries/data/mods/public/simulation/templates/structures/rome_tent.xml
index b8daf9e052..4478082fac 100644
--- a/binaries/data/mods/public/simulation/templates/structures/rome_tent.xml
+++ b/binaries/data/mods/public/simulation/templates/structures/rome_tent.xml
@@ -50,7 +50,7 @@
- 1
+ 1
diff --git a/binaries/data/mods/public/simulation/templates/structures/sele_military_colony.xml b/binaries/data/mods/public/simulation/templates/structures/sele_military_colony.xml
index 091ac25c15..405d433dff 100644
--- a/binaries/data/mods/public/simulation/templates/structures/sele_military_colony.xml
+++ b/binaries/data/mods/public/simulation/templates/structures/sele_military_colony.xml
@@ -75,7 +75,7 @@
- 1
+ 1
80
diff --git a/binaries/data/mods/public/simulation/templates/template_structure.xml b/binaries/data/mods/public/simulation/templates/template_structure.xml
index 8c78ce588d..9b07eefd15 100644
--- a/binaries/data/mods/public/simulation/templates/template_structure.xml
+++ b/binaries/data/mods/public/simulation/templates/template_structure.xml
@@ -20,6 +20,11 @@
land
own
+
+ 1000
+ 0
+ 3
+
0
0
@@ -100,7 +105,7 @@
12.0
- 5
+ 5
true
diff --git a/binaries/data/mods/public/simulation/templates/template_structure_civic_civil_centre.xml b/binaries/data/mods/public/simulation/templates/template_structure_civic_civil_centre.xml
index 76a3aa16cc..ba9d9c6727 100644
--- a/binaries/data/mods/public/simulation/templates/template_structure_civic_civil_centre.xml
+++ b/binaries/data/mods/public/simulation/templates/template_structure_civic_civil_centre.xml
@@ -35,6 +35,9 @@
200
+
+ 3000
+
20
500
diff --git a/binaries/data/mods/public/simulation/templates/template_structure_civic_house.xml b/binaries/data/mods/public/simulation/templates/template_structure_civic_house.xml
index 9631826a9a..69846bccb8 100644
--- a/binaries/data/mods/public/simulation/templates/template_structure_civic_house.xml
+++ b/binaries/data/mods/public/simulation/templates/template_structure_civic_house.xml
@@ -3,6 +3,9 @@
House
+
+ 300
+
5
30
diff --git a/binaries/data/mods/public/simulation/templates/template_structure_defense_outpost.xml b/binaries/data/mods/public/simulation/templates/template_structure_defense_outpost.xml
index 0e68c5484e..2ea5c125b3 100644
--- a/binaries/data/mods/public/simulation/templates/template_structure_defense_outpost.xml
+++ b/binaries/data/mods/public/simulation/templates/template_structure_defense_outpost.xml
@@ -90,7 +90,7 @@
18.0
- 2
+ 2
80
diff --git a/binaries/data/mods/public/simulation/templates/template_structure_defense_wall.xml b/binaries/data/mods/public/simulation/templates/template_structure_defense_wall.xml
index 62b3c64357..18314fe31f 100644
--- a/binaries/data/mods/public/simulation/templates/template_structure_defense_wall.xml
+++ b/binaries/data/mods/public/simulation/templates/template_structure_defense_wall.xml
@@ -4,6 +4,7 @@
land-shore
Wall
+
10
diff --git a/binaries/data/mods/public/simulation/templates/template_structure_defense_wall_gate.xml b/binaries/data/mods/public/simulation/templates/template_structure_defense_wall_gate.xml
index 4617435805..121b99afb6 100644
--- a/binaries/data/mods/public/simulation/templates/template_structure_defense_wall_gate.xml
+++ b/binaries/data/mods/public/simulation/templates/template_structure_defense_wall_gate.xml
@@ -6,6 +6,7 @@
Wall
+
0
diff --git a/binaries/data/mods/public/simulation/templates/template_structure_defense_wall_tower.xml b/binaries/data/mods/public/simulation/templates/template_structure_defense_wall_tower.xml
index e3ab986a41..1875b314d0 100644
--- a/binaries/data/mods/public/simulation/templates/template_structure_defense_wall_tower.xml
+++ b/binaries/data/mods/public/simulation/templates/template_structure_defense_wall_tower.xml
@@ -23,6 +23,7 @@
land-shore
Wall
+
120
diff --git a/binaries/data/mods/public/simulation/templates/template_structure_economic_farmstead.xml b/binaries/data/mods/public/simulation/templates/template_structure_economic_farmstead.xml
index 0875006bc4..ed4371a52c 100644
--- a/binaries/data/mods/public/simulation/templates/template_structure_economic_farmstead.xml
+++ b/binaries/data/mods/public/simulation/templates/template_structure_economic_farmstead.xml
@@ -3,6 +3,9 @@
Farmstead
+
+ 300
+
45
diff --git a/binaries/data/mods/public/simulation/templates/template_structure_economic_storehouse.xml b/binaries/data/mods/public/simulation/templates/template_structure_economic_storehouse.xml
index 55b9bbb5af..f8f658804d 100644
--- a/binaries/data/mods/public/simulation/templates/template_structure_economic_storehouse.xml
+++ b/binaries/data/mods/public/simulation/templates/template_structure_economic_storehouse.xml
@@ -3,6 +3,9 @@
Storehouse
+
+ 300
+
40
diff --git a/binaries/data/mods/public/simulation/templates/template_structure_military_fortress.xml b/binaries/data/mods/public/simulation/templates/template_structure_military_fortress.xml
index 080293410e..73cd16ffe5 100644
--- a/binaries/data/mods/public/simulation/templates/template_structure_military_fortress.xml
+++ b/binaries/data/mods/public/simulation/templates/template_structure_military_fortress.xml
@@ -26,6 +26,9 @@
80
+
+ 4000
+
10
300
diff --git a/binaries/data/mods/public/simulation/templates/template_structure_resource_field.xml b/binaries/data/mods/public/simulation/templates/template_structure_resource_field.xml
index 3c1d2f5f07..1ac6dbf7e5 100644
--- a/binaries/data/mods/public/simulation/templates/template_structure_resource_field.xml
+++ b/binaries/data/mods/public/simulation/templates/template_structure_resource_field.xml
@@ -8,6 +8,7 @@
Field
+
50
diff --git a/binaries/data/mods/public/simulation/templates/template_structure_wonder.xml b/binaries/data/mods/public/simulation/templates/template_structure_wonder.xml
index 32ef116445..c403f0c793 100644
--- a/binaries/data/mods/public/simulation/templates/template_structure_wonder.xml
+++ b/binaries/data/mods/public/simulation/templates/template_structure_wonder.xml
@@ -13,6 +13,9 @@
Wonder
+
+ 5000
+
1000
diff --git a/binaries/data/mods/public/simulation/templates/template_unit_cavalry.xml b/binaries/data/mods/public/simulation/templates/template_unit_cavalry.xml
index 8bbfb5bbcc..f799246005 100644
--- a/binaries/data/mods/public/simulation/templates/template_unit_cavalry.xml
+++ b/binaries/data/mods/public/simulation/templates/template_unit_cavalry.xml
@@ -6,6 +6,11 @@
15
+
+ 3
+ 4
+ 1000
+
100.0
0.0
diff --git a/binaries/data/mods/public/simulation/templates/template_unit_champion.xml b/binaries/data/mods/public/simulation/templates/template_unit_champion.xml
index 25df518961..0fd6b05d66 100644
--- a/binaries/data/mods/public/simulation/templates/template_unit_champion.xml
+++ b/binaries/data/mods/public/simulation/templates/template_unit_champion.xml
@@ -1,5 +1,12 @@
+
+
+ 3
+ 4
+ 1000
+
+
Champion Unit
Organic Human
diff --git a/binaries/data/mods/public/simulation/templates/template_unit_champion_elephant_melee.xml b/binaries/data/mods/public/simulation/templates/template_unit_champion_elephant_melee.xml
index 520e5d5c3e..e734460e62 100644
--- a/binaries/data/mods/public/simulation/templates/template_unit_champion_elephant_melee.xml
+++ b/binaries/data/mods/public/simulation/templates/template_unit_champion_elephant_melee.xml
@@ -1,6 +1,6 @@
-
+
20
0
diff --git a/binaries/data/mods/public/simulation/templates/template_unit_hero_elephant_melee.xml b/binaries/data/mods/public/simulation/templates/template_unit_hero_elephant_melee.xml
index df041b95aa..c9dd17d664 100644
--- a/binaries/data/mods/public/simulation/templates/template_unit_hero_elephant_melee.xml
+++ b/binaries/data/mods/public/simulation/templates/template_unit_hero_elephant_melee.xml
@@ -5,7 +5,7 @@
10
12
-
+
17.5
0
diff --git a/binaries/data/mods/public/simulation/templates/template_unit_infantry.xml b/binaries/data/mods/public/simulation/templates/template_unit_infantry.xml
index c4fb3932b1..fc53d0c7c5 100644
--- a/binaries/data/mods/public/simulation/templates/template_unit_infantry.xml
+++ b/binaries/data/mods/public/simulation/templates/template_unit_infantry.xml
@@ -6,6 +6,11 @@
15
+
+ 3
+ 4
+ 1000
+
50.0
0.0
diff --git a/binaries/data/mods/public/simulation/templates/template_unit_mechanical_siege.xml b/binaries/data/mods/public/simulation/templates/template_unit_mechanical_siege.xml
index fa4c783241..001bee2e0d 100644
--- a/binaries/data/mods/public/simulation/templates/template_unit_mechanical_siege.xml
+++ b/binaries/data/mods/public/simulation/templates/template_unit_mechanical_siege.xml
@@ -5,6 +5,11 @@
5
5
+
+ 100
+ 0
+ 1.5
+
5