Index: binaries/data/mods/public/gui/session/session.js =================================================================== --- binaries/data/mods/public/gui/session/session.js +++ binaries/data/mods/public/gui/session/session.js @@ -407,14 +407,13 @@ } /** - * Returns the entity itself except when garrisoned where it returns its garrisonHolder + * @return {number} - The entity itself except when garrisoned then its garrisonHolder. */ function getEntityOrHolder(ent) { let entState = GetEntityState(ent); - if (entState && !entState.position && entState.unitAI && entState.unitAI.orders.length && - entState.unitAI.orders[0].type == "Garrison") - return getEntityOrHolder(entState.unitAI.orders[0].data.target); + if (entState && !entState.position && entState.garrisonable && entState.garrisonable.garrisonHolder != INVALID_ENTITY) + return getEntityOrHolder(entState.garrisonable.garrisonHolder); return ent; } 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 @@ -720,7 +720,7 @@ }, "getActionInfo": function(entState, targetState) { - if (!entState.canGarrison || !targetState.garrisonHolder || + if (!entState.garrisonable || !targetState.garrisonHolder || !playerCheck(entState, targetState, ["Player", "MutualAlly"])) return false; @@ -1264,7 +1264,9 @@ "getInfo": function(entStates) { if (entStates.every(entState => { - if (!entState.unitAI || !entState.turretParent) + if (!entState.unitAI || + !entState.garrisonable || + entState.garrisonable.garrisonHolder == INVALID_ENTITY) return true; let parent = GetEntityState(entState.turretParent); return !parent || !parent.garrisonHolder || parent.garrisonHolder.entities.indexOf(entState.id) == -1; Index: binaries/data/mods/public/simulation/components/GarrisonHolder.js =================================================================== --- binaries/data/mods/public/simulation/components/GarrisonHolder.js +++ binaries/data/mods/public/simulation/components/GarrisonHolder.js @@ -150,11 +150,8 @@ return false; let cmpIdentity = Engine.QueryInterface(entity, IID_Identity); - if (!cmpIdentity) - return false; - - let entityClasses = cmpIdentity.GetClassesList(); - return MatchesClassList(entityClasses, this.allowedClasses) && !!Engine.QueryInterface(entity, IID_Garrisonable); + return cmpIdentity && + MatchesClassList(cmpIdentity.GetClassesList(), this.allowedClasses); }; /** @@ -171,6 +168,10 @@ if (!this.HasEnoughHealth()) return false; + let cmpGarrisonable = Engine.QueryInterface(entity, IID_Garrisonable); + if (!cmpGarrisonable || !cmpGarrisonable.Garrison(this.entity)) + return false; + if (!this.timer && this.GetHealRate() > 0) { let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); @@ -264,6 +265,10 @@ cmpEntPosition.SetYRotation(cmpPosition.GetPosition().horizAngleTo(pos)); } + let cmpGarrisonable = Engine.QueryInterface(entity, IID_Garrisonable); + if (cmpGarrisonable) + cmpGarrisonable.UnGarrison(); + Engine.PostMessage(this.entity, MT_GarrisonedUnitsChanged, { "added": [], "removed": [entity], Index: binaries/data/mods/public/simulation/components/Garrisonable.js =================================================================== --- binaries/data/mods/public/simulation/components/Garrisonable.js +++ binaries/data/mods/public/simulation/components/Garrisonable.js @@ -4,8 +4,36 @@ Garrisonable.prototype.Init = function() { + this.garrisonHolder = INVALID_ENTITY; }; -Garrisonable.prototype.Serialize = null; +/** + * @return {number} - The entity ID of the entity this entity is garrisoned in. + */ +Garrisonable.prototype.GetHolderID = function() +{ + return this.garrisonHolder; +}; + +/** + * @param {number} entity - The entity ID of the entity this entity is being garrisoned in. + * @return {boolean} - Whether garrisoning succeeded. + */ +Garrisonable.prototype.Garrison = function(entity) +{ + if (this.garrisonHolder != INVALID_ENTITY) + return false; + + this.garrisonHolder = entity; + return true; +}; + +/** + * Resets the garrisonHolder. + */ +Garrisonable.prototype.UnGarrison = function() +{ + this.garrisonHolder = INVALID_ENTITY; +}; Engine.RegisterComponentType(IID_Garrisonable, "Garrisonable", Garrisonable); 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 @@ -370,7 +370,11 @@ "turretPoints": cmpTurretHolder.GetTurretPoints() }; - ret.canGarrison = !!Engine.QueryInterface(ent, IID_Garrisonable); + let cmpGarrisonable = Engine.QueryInterface(ent, IID_Garrisonable); + if (cmpGarrisonable) + ret.garrisonable = { + "garrisonHolder": cmpGarrisonable.GetHolderID() + }; let cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI); if (cmpUnitAI) 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 @@ -3478,13 +3478,11 @@ UnitAI.prototype.GetGarrisonHolder = function() { - if (this.IsGarrisoned()) - { - for (let order of this.orderQueue) - if (order.type == "Garrison") - return order.data.target; - } - return INVALID_ENTITY; + if (!this.isGarrisoned) + return INVALID_ENTITY; + + let cmpGarrisonable = Engine.QueryInterface(this.entity, IID_Garrisonable); + return cmpGarrisonable ? cmpGarrisonable.GetHolderID() : INVALID_ENTITY; }; UnitAI.prototype.ShouldRespondToEndOfAlert = function() Index: binaries/data/mods/public/simulation/components/tests/test_GarrisonHolder.js =================================================================== --- binaries/data/mods/public/simulation/components/tests/test_GarrisonHolder.js +++ binaries/data/mods/public/simulation/components/tests/test_GarrisonHolder.js @@ -68,7 +68,10 @@ "GetOwner": () => friendlyPlayer }); - AddMock(i, IID_Garrisonable, {}); + AddMock(i, IID_Garrisonable, { + "Garrison": entity => true, + "UnGarrison": () => {} + }); AddMock(i, IID_Position, { "GetHeightOffset": () => 0, @@ -212,7 +215,10 @@ AddMock(siegeEngineId, IID_Ownership, { "GetOwner": () => currentSiegePlayer }); -AddMock(siegeEngineId, IID_Garrisonable, {}); +AddMock(siegeEngineId, IID_Garrisonable, { + "Garrison": entity => true, + "UnGarrison": () => {} +}); let cavalryId = 46; AddMock(cavalryId, IID_Identity, { "GetClassesList": () => ["Infantry", "Ranged"] @@ -230,7 +236,10 @@ AddMock(cavalryId, IID_Ownership, { "GetOwner": () => currentCavalryPlayer }); -AddMock(cavalryId, IID_Garrisonable, {}); +AddMock(cavalryId, IID_Garrisonable, { + "Garrison": entity => true, + "UnGarrison": () => {} +}); TS_ASSERT(cmpGarrisonHolder.Garrison(siegeEngineId)); TS_ASSERT_EQUALS(cmpGarrisonHolder.GetGarrisonedEntitiesCount(), 1); cmpGarrisonHolder.OnGlobalEntityRenamed({ Index: binaries/data/mods/public/simulation/components/tests/test_Garrisonable.js =================================================================== --- /dev/null +++ binaries/data/mods/public/simulation/components/tests/test_Garrisonable.js @@ -0,0 +1,17 @@ +Engine.LoadComponentScript("interfaces/Garrisonable.js"); +Engine.LoadComponentScript("Garrisonable.js"); + +const garrisonHolderID = 1; +const garrisonableID = 2; + +let cmpGarrisonable = ConstructComponent(garrisonableID, "Garrisonable", { +}); + +TS_ASSERT(cmpGarrisonable.Garrison(garrisonHolderID)); +TS_ASSERT_UNEVAL_EQUALS(cmpGarrisonable.GetHolderID(), garrisonHolderID); + +TS_ASSERT(!cmpGarrisonable.Garrison(garrisonHolderID)); +TS_ASSERT_UNEVAL_EQUALS(cmpGarrisonable.GetHolderID(), garrisonHolderID); + +cmpGarrisonable.UnGarrison(); +TS_ASSERT_UNEVAL_EQUALS(cmpGarrisonable.GetHolderID(), INVALID_ENTITY); 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 @@ -596,7 +596,6 @@ "needsRepair": false, "needsHeal": true, "builder": true, - "canGarrison": false, "visibility": "visible", "isBarterMarket": true, "resourceTrickle": {