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 + * Returns the entity itself except when garrisoned where it returns 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; 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 @@ -153,7 +153,7 @@ return false; let entityClasses = cmpIdentity.GetClassesList(); - return MatchesClassList(entityClasses, this.template.List._string) && !!Engine.QueryInterface(entity, IID_Garrisonable); + return MatchesClassList(entityClasses, this.template.List._string); }; /** @@ -164,6 +164,10 @@ */ GarrisonHolder.prototype.Garrison = function(entity, renamed = false) { + let cmpGarrisonable = Engine.QueryInterface(entity, IID_Garrisonable); + if (!cmpGarrisonable) + return false; + if (!this.IsAllowedToGarrison(entity)) return false; @@ -176,6 +180,7 @@ this.timer = cmpTimer.SetTimeout(this.entity, IID_GarrisonHolder, "HealTimeout", 1000, {}); } + cmpGarrisonable.Garrison(this.entity); this.entities.push(entity); this.UpdateGarrisonFlag(); let cmpProductionQueue = Engine.QueryInterface(entity, IID_ProductionQueue); @@ -263,6 +268,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,31 @@ 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.GetGarrisonHolder = function() +{ + return this.garrisonHolder; +}; + +/** + * @param {number} entity - The entity ID of the entity this entity is being garrisoned in. + */ +Garrisonable.prototype.Garrison = function(entity) +{ + this.garrisonHolder = entity; +}; + +/** + * 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 @@ -368,7 +368,11 @@ "turretPoints": cmpTurretHolder.GetTurretPoints() }; - ret.canGarrison = !!Engine.QueryInterface(ent, IID_Garrisonable); + let cmpGarrisonable = Engine.QueryInterface(ent, IID_Garrisonable); + if (cmpGarrisonable) + ret.garrisonable = { + "garrisonHolder": cmpGarrisonable.GetGarrisonHolder() + }; 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 @@ -3490,13 +3490,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.GetGarrisonHolder() : 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 => {}, + "UnGarrison": () => {} + }); AddMock(i, IID_Position, { "GetHeightOffset": () => 0, @@ -209,7 +212,10 @@ AddMock(siegeEngineId, IID_Ownership, { "GetOwner": () => currentSiegePlayer }); -AddMock(siegeEngineId, IID_Garrisonable, {}); +AddMock(siegeEngineId, IID_Garrisonable, { + "Garrison": entity => {}, + "UnGarrison": () => {} +}); let cavalryId = 46; AddMock(cavalryId, IID_Identity, { "GetClassesList": () => ["Infantry", "Ranged"] @@ -227,7 +233,10 @@ AddMock(cavalryId, IID_Ownership, { "GetOwner": () => currentCavalryPlayer }); -AddMock(cavalryId, IID_Garrisonable, {}); +AddMock(cavalryId, IID_Garrisonable, { + "Garrison": entity => {}, + "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,14 @@ +Engine.LoadComponentScript("interfaces/Garrisonable.js"); +Engine.LoadComponentScript("Garrisonable.js"); + +const garrisonHolderID = 1; +const garrisonableID = 2; + +let cmpGarrisonable = ConstructComponent(garrisonableID, "Garrisonable", { +}); + +cmpGarrisonable.Garrison(garrisonHolderID); +TS_ASSERT_UNEVAL_EQUALS(cmpGarrisonable.GetGarrisonHolder(), garrisonHolderID); + +cmpGarrisonable.UnGarrison(); +TS_ASSERT_UNEVAL_EQUALS(cmpGarrisonable.GetGarrisonHolder(), 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 @@ -589,7 +589,6 @@ "needsRepair": false, "needsHeal": true, "builder": true, - "canGarrison": false, "visibility": "visible", "isBarterMarket": true, "resourceTrickle": {