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
@@ -1457,10 +1457,11 @@
if (entState.resourceSupply && entState.resourceSupply.killBeforeGather)
return translate("The entity has to be killed before it can be gathered from");
+ // Keep in sync with Controllability.js-component.
if (entState.capturePoints && entState.capturePoints[entState.player] < entState.maxCapturePoints / 2)
return translate("You cannot destroy this entity as you own less than half the capture points");
- if (!entState.identity.canDelete)
+ if (entState.controllability && !entState.controllability.Deletable)
return translate("This entity is undeletable");
return false;
Index: binaries/data/mods/public/gui/session/unit_commands.js
===================================================================
--- binaries/data/mods/public/gui/session/unit_commands.js
+++ binaries/data/mods/public/gui/session/unit_commands.js
@@ -135,7 +135,10 @@
let playerStates = GetSimState().players;
let playerState = playerStates[Engine.GetPlayerID()];
- if (g_IsObserver || entStates.every(entState => controlsPlayer(entState.player)))
+ // Always show selection.
+ setupUnitPanel("Selection", entStates, playerStates[entStates[0].player]);
+
+ if (g_IsObserver || entStates.every(entState => controlsPlayer(entState.player) && (!entState.controllability || entState.controllability.Controllable)))
{
for (let guiName of g_PanelsOrder)
{
Index: binaries/data/mods/public/simulation/components/Capturable.js
===================================================================
--- binaries/data/mods/public/simulation/components/Capturable.js
+++ binaries/data/mods/public/simulation/components/Capturable.js
@@ -160,6 +160,7 @@
Engine.PostMessage(this.entity, MT_CapturePointsChanged, { "capturePoints": this.cp });
var owner = cmpOwnership.GetOwner();
+
if (owner == INVALID_PLAYER || this.cp[owner] > 0)
return;
Index: binaries/data/mods/public/simulation/components/Controllability.js
===================================================================
--- /dev/null
+++ binaries/data/mods/public/simulation/components/Controllability.js
@@ -0,0 +1,65 @@
+class Controllability
+{
+ get Schema()
+ {
+ return "Deals with the controllability of structures and units." +
+ "" +
+ "true" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "";
+ }
+
+ /**
+ * The initialisation of the component.
+ */
+ Init()
+ {
+ this.controllability = {};
+ for (let entry in this.template)
+ this.controllability[entry] = this.template[entry] == "true";
+ }
+
+ /**
+ * Whether the entity is controllable in some way.
+ *
+ * @param {string} type - The type to check controllability for.
+ *
+ * @return {boolean} - Whether the entity can be given the order.
+ */
+ GetControllability(type)
+ {
+ return this.controllability;
+ }
+
+ /**
+ * Whether the entity is controllable in some way.
+ *
+ * @param {string[]} types - The types to set the controllability for.
+ * @param {boolean} bool - The boolean to set the controllability to.
+ */
+ SetControllability(types, bool)
+ {
+ for (let type of types)
+ this.controllability[type] = bool;
+ }
+}
+
+Controllability.prototype.OnCapturePointsChanged = function(msg)
+{
+ let cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
+ if (!cmpOwnership)
+ return;
+
+ let owner = cmpOwnership.GetOwner();
+ let totalCapturePoints = msg.capturePoints.reduce((a, b) => a + b, 0);
+
+ // Keep in sync with gui/session/unit_actions.js.
+ this.SetControllability(["Deletable"], msg.capturePoints[owner] >= totalCapturePoints / 2);
+}
+
+Engine.RegisterComponentType(IID_Controllability, "Controllability", Controllability);
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
@@ -262,8 +262,7 @@
"rank": cmpIdentity.GetRank(),
"classes": cmpIdentity.GetClassesList(),
"visibleClasses": cmpIdentity.GetVisibleClassesList(),
- "selectionGroupName": cmpIdentity.GetSelectionGroupName(),
- "canDelete": !cmpIdentity.IsUndeletable()
+ "selectionGroupName": cmpIdentity.GetSelectionGroupName()
};
let cmpPosition = Engine.QueryInterface(ent, IID_Position);
@@ -286,6 +285,12 @@
ret.maxCapturePoints = cmpCapturable.GetMaxCapturePoints();
}
+ let cmpControllability = Engine.QueryInterface(ent, IID_Controllability);
+ if (cmpControllability)
+ {
+ ret.controllability = cmpControllability.GetControllability();
+ }
+
let cmpBuilder = Engine.QueryInterface(ent, IID_Builder);
if (cmpBuilder)
ret.builder = true;
Index: binaries/data/mods/public/simulation/components/Identity.js
===================================================================
--- binaries/data/mods/public/simulation/components/Identity.js
+++ binaries/data/mods/public/simulation/components/Identity.js
@@ -89,10 +89,7 @@
"" +
"" +
"" +
- "" +
- "" +
- "" +
- "";
+ "";
Identity.prototype.Init = function()
{
@@ -174,9 +171,4 @@
return this.template.GenericName;
};
-Identity.prototype.IsUndeletable = function()
-{
- return this.template.Undeletable == "true";
-};
-
Engine.RegisterComponentType(IID_Identity, "Identity", Identity);
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
@@ -187,8 +187,7 @@
// 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())
+ if (this.IsTurret())
{
this.FinishOrder();
return;
@@ -234,13 +233,6 @@
// (these will switch the unit out of formation mode)
"Order.Stop": function(msg) {
- // We have no control over non-domestic animals.
- if (this.IsAnimal() && !this.IsDomestic())
- {
- this.FinishOrder();
- return;
- }
-
// Stop moving immediately.
this.StopMoving();
this.FinishOrder();
@@ -254,8 +246,7 @@
},
"Order.Walk": function(msg) {
- // Let players move captured domestic animals around
- if (this.IsAnimal() && !this.IsDomestic() || this.IsTurret())
+ if (this.IsTurret())
{
this.FinishOrder();
return;
@@ -280,8 +271,7 @@
},
"Order.WalkAndFight": function(msg) {
- // Let players move captured domestic animals around
- if (this.IsAnimal() && !this.IsDomestic() || this.IsTurret())
+ if (this.IsTurret())
{
this.FinishOrder();
return;
@@ -307,8 +297,7 @@
"Order.WalkToTarget": function(msg) {
- // Let players move captured domestic animals around
- if (this.IsAnimal() && !this.IsDomestic() || this.IsTurret())
+ if (this.IsTurret())
{
this.FinishOrder();
return;
@@ -455,7 +444,7 @@
},
"Order.Patrol": function(msg) {
- if (this.IsAnimal() || this.IsTurret())
+ if (this.IsTurret())
{
this.FinishOrder();
return;
Index: binaries/data/mods/public/simulation/components/interfaces/Controllability.js
===================================================================
--- /dev/null
+++ binaries/data/mods/public/simulation/components/interfaces/Controllability.js
@@ -0,0 +1,6 @@
+Engine.RegisterInterface("Controllability");
+
+/**
+ * Message sent from Controllability component.
+ */
+Engine.RegisterMessageType("ControllabilityChanged");
Index: binaries/data/mods/public/simulation/components/tests/test_Capturable.js
===================================================================
--- binaries/data/mods/public/simulation/components/tests/test_Capturable.js
+++ binaries/data/mods/public/simulation/components/tests/test_Capturable.js
@@ -2,6 +2,7 @@
Engine.LoadHelperScript("ValueModification.js");
Engine.LoadComponentScript("interfaces/Auras.js");
Engine.LoadComponentScript("interfaces/Capturable.js");
+Engine.LoadComponentScript("interfaces/Controllability.js");
Engine.LoadComponentScript("interfaces/GarrisonHolder.js");
Engine.LoadComponentScript("interfaces/StatisticsTracker.js");
Engine.LoadComponentScript("interfaces/ModifiersManager.js");
Index: binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js
===================================================================
--- binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js
+++ binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js
@@ -6,6 +6,7 @@
Engine.LoadComponentScript("interfaces/Builder.js");
Engine.LoadComponentScript("interfaces/Capturable.js");
Engine.LoadComponentScript("interfaces/CeasefireManager.js");
+Engine.LoadComponentScript("interfaces/Controllability.js");
Engine.LoadComponentScript("interfaces/Resistance.js");
Engine.LoadComponentScript("interfaces/DeathDamage.js");
Engine.LoadComponentScript("interfaces/EndGameManager.js");
@@ -543,7 +544,6 @@
GetRank: function() { return "foo"; },
GetSelectionGroupName: function() { return "Selection Group Name"; },
HasClass: function() { return true; },
- IsUndeletable: function() { return false; }
});
AddMock(10, IID_Position, {
@@ -572,8 +572,7 @@
"rank": "foo",
"classes": ["class1", "class2"],
"visibleClasses": ["class3", "class4"],
- "selectionGroupName": "Selection Group Name",
- "canDelete": true
+ "selectionGroupName": "Selection Group Name"
},
"position": {x:1, y:2, z:3},
"hitpoints": 50,
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
@@ -388,15 +388,11 @@
{
for (let ent of data.entities)
{
+ // Keep in sync with gui/session/unit_actions.js.
if (!data.controlAllUnits)
{
- let cmpIdentity = Engine.QueryInterface(ent, IID_Identity);
- if (cmpIdentity && cmpIdentity.IsUndeletable())
- continue;
-
- let cmpCapturable = QueryMiragedInterface(ent, IID_Capturable);
- if (cmpCapturable &&
- cmpCapturable.GetCapturePoints()[player] < cmpCapturable.GetMaxCapturePoints() / 2)
+ let cmpControllability = Engine.QueryInterface(ent, IID_Controllability);
+ if (cmpControllability && !cmpControllability.GetControllability("Deletable"))
continue;
let cmpResourceSupply = QueryMiragedInterface(ent, IID_ResourceSupply);
@@ -867,6 +863,27 @@
}
/**
+ * Sends a GUI notification about entities that can't be controlled.
+ * @param {number} player - The player-ID of the player that needs to receive this message.
+ */
+function notifyOrderFailure(entity, player)
+{
+ let cmpIdentity = Engine.QueryInterface(entity, IID_Identity);
+ if (!cmpIdentity)
+ return;
+
+ let cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
+ cmpGUIInterface.PushNotification({
+ "type": "text",
+ "players": [player],
+ "message": sprintf(markForTranslation("Some %(unit)s can't be controlled."), {
+ "unit": cmpIdentity.GetGenericName()
+ }),
+ "translateMessage": true
+ });
+}
+
+/**
* Get some information about the formations used by entities.
* The entities must have a UnitAI component.
*/
@@ -1655,7 +1672,13 @@
*/
function CanControlUnit(entity, player, controlAll)
{
- return IsOwnedByPlayer(player, entity) || controlAll;
+ let cmpControllability = Engine.QueryInterface(entity, IID_Controllability);
+ let CanBeControlled = IsOwnedByPlayer(player, entity) && (!cmpControllability || cmpControllability.GetControllability("Controllable")) || controlAll;
+
+ if (!CanBeControlled)
+ notifyOrderFailure(entity, player);
+
+ return CanBeControlled;
}
/**
Index: binaries/data/mods/public/simulation/templates/special/filter/mirage.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/special/filter/mirage.xml
+++ binaries/data/mods/public/simulation/templates/special/filter/mirage.xml
@@ -8,7 +8,6 @@
-
Index: binaries/data/mods/public/simulation/templates/special/filter/undeletable.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/special/filter/undeletable.xml
+++ binaries/data/mods/public/simulation/templates/special/filter/undeletable.xml
@@ -1,6 +1,6 @@
-
- true
-
+
+ false
+
Index: binaries/data/mods/public/simulation/templates/special/player/player.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/special/player/player.xml
+++ binaries/data/mods/public/simulation/templates/special/player/player.xml
@@ -63,7 +63,6 @@
Player
Player
- true
Index: binaries/data/mods/public/simulation/templates/special/spy.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/special/spy.xml
+++ binaries/data/mods/public/simulation/templates/special/spy.xml
@@ -16,7 +16,6 @@
Spy
Spy
unlock_spies
- true
false
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
@@ -3,7 +3,6 @@