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 @@ -72,6 +72,10 @@ this.holder = entity; + let cmpUnitAI = Engine.QueryInterface(this.entity, IID_UnitAI); + if (cmpUnitAI) + cmpUnitAI.SetGarrisoned(); + let cmpProductionQueue = Engine.QueryInterface(this.entity, IID_ProductionQueue); if (cmpProductionQueue) cmpProductionQueue.PauseProduction(); 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 @@ -1857,7 +1857,7 @@ GuiInterface.prototype.IdleUnitFilter = function(unit, idleClasses, excludeUnits) { let cmpUnitAI = Engine.QueryInterface(unit, IID_UnitAI); - if (!cmpUnitAI || !cmpUnitAI.IsIdle() || cmpUnitAI.IsGarrisoned()) + if (!cmpUnitAI || !cmpUnitAI.IsIdle() || cmpUnitAI.IsTurret()) return { "idle": false }; let cmpIdentity = Engine.QueryInterface(unit, IID_Identity); Index: binaries/data/mods/public/simulation/components/Health.js =================================================================== --- binaries/data/mods/public/simulation/components/Health.js +++ binaries/data/mods/public/simulation/components/Health.js @@ -134,7 +134,8 @@ if (this.GetIdleRegenRate() != 0) { let cmpUnitAI = Engine.QueryInterface(this.entity, IID_UnitAI); - if (cmpUnitAI && (cmpUnitAI.IsIdle() || cmpUnitAI.IsGarrisoned() && !cmpUnitAI.IsTurret())) + if (cmpUnitAI && (cmpUnitAI.IsIdle() || + cmpUnitAI.GetGarrisonHolder() != INVALID_ENTITY && !cmpUnitAI.IsTurret())) regen += this.GetIdleRegenRate(); } 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 @@ -589,7 +589,7 @@ return ACCEPT_ORDER; } - if (this.IsGarrisoned()) + if (this.isGarrisoned) { this.SetNextState("INDIVIDUAL.GARRISON.GARRISONED"); return ACCEPT_ORDER; @@ -610,8 +610,11 @@ }, "Order.Ungarrison": function() { + // Note that this order MUST succeed, or we break + // the assumptions done in garrisonable/garrisonHolder, + // especially in EjectOrKill in the latter. + // ToDo: This can be fixed by not making that assumption :) this.FinishOrder(); - this.isGarrisoned = false; return ACCEPT_ORDER; }, @@ -3259,9 +3262,6 @@ let cmpGarrisonable = Engine.QueryInterface(this.entity, IID_Garrisonable); if (cmpGarrisonable.Garrison(target)) { - this.isGarrisoned = true; - this.SetImmobile(true); - if (this.formationController) { var cmpFormation = Engine.QueryInterface(this.formationController, IID_Formation); @@ -3463,9 +3463,7 @@ this.orderQueue = []; // current order is at the front of the list this.order = undefined; // always == this.orderQueue[0] this.formationController = INVALID_ENTITY; // entity with IID_Formation that we belong to - this.isGarrisoned = false; this.isIdle = false; - this.isImmobile = false; // True if the unit is currently unable to move (garrisoned,...) this.heldPosition = undefined; @@ -3539,14 +3537,11 @@ Engine.PostMessage(this.entity, MT_UnitIdleChanged, { "idle": this.isIdle }); }; -UnitAI.prototype.IsGarrisoned = function() -{ - return this.isGarrisoned; -}; - UnitAI.prototype.SetGarrisoned = function() { + // UnitAI caches its own garrisoned state for performance. this.isGarrisoned = true; + this.SetImmobile(); }; UnitAI.prototype.GetGarrisonHolder = function() @@ -3563,9 +3558,24 @@ return !this.orderQueue.length || this.orderQueue[0].type == "Garrison"; }; -UnitAI.prototype.SetImmobile = function(immobile) +UnitAI.prototype.SetImmobile = function() +{ + if (this.isImmobile) + return; + + this.isImmobile = true; + Engine.PostMessage(this.entity, MT_UnitAbleToMoveChanged, { + "entity": this.entity, + "ableToMove": this.AbleToMove() + }); +}; + +UnitAI.prototype.SetMobile = function() { - this.isImmobile = immobile; + if (!this.isImmobile) + return; + + delete this.isImmobile; Engine.PostMessage(this.entity, MT_UnitAbleToMoveChanged, { "entity": this.entity, "ableToMove": this.AbleToMove() @@ -3888,7 +3898,7 @@ this.order = this.orderQueue[0]; let cmpPosition = Engine.QueryInterface(this.entity, IID_Position); - if (this.orderQueue.length && (this.IsGarrisoned() || this.IsFormationController() || + if (this.orderQueue.length && (this.isGarrisoned || this.IsFormationController() || cmpPosition && cmpPosition.IsInWorld())) { let ret = this.UnitFsm.ProcessMessage(this, @@ -4063,7 +4073,7 @@ this.UpdateWorkOrders(type); } - let garrisonHolder = this.IsGarrisoned() && type != "Ungarrison" ? this.GetGarrisonHolder() : null; + let garrisonHolder = this.isGarrisoned && type != "Ungarrison" ? this.GetGarrisonHolder() : null; // Do not replace packing/unpacking unless it is cancel order. // TODO: maybe a better way of doing this would be to use priority levels @@ -4178,10 +4188,10 @@ if (this.workOrders.length == 0) return false; - if (this.IsGarrisoned()) + if (this.isGarrisoned) { - let cmpGarrisonHolder = Engine.QueryInterface(this.GetGarrisonHolder(), IID_GarrisonHolder); - if (!cmpGarrisonHolder || !cmpGarrisonHolder.PerformEject([this.entity], false)) + let cmpGarrisonable = Engine.QueryInterface(this.entity, IID_Garrisonable); + if (!cmpGarrisonable || !cmpGarrisonable.UnGarrison(false)) return false; } @@ -5362,7 +5372,7 @@ { // May happen if an order arrives on the same turn the unit is garrisoned // in that case, just forget the order as this will lead to an infinite loop - if (this.IsGarrisoned() && !this.IsTurret() && type != "Ungarrison") + if (this.isGarrisoned && !this.IsTurret() && type != "Ungarrison") return; this.ReplaceOrder(type, data); } @@ -5607,11 +5617,11 @@ */ UnitAI.prototype.Ungarrison = function() { - if (this.IsGarrisoned()) - { - this.SetImmobile(false); - this.AddOrder("Ungarrison", null, false); - } + if (!this.isGarrisoned) + return; + delete this.isGarrisoned; + this.SetMobile(); + this.AddOrder("Ungarrison", null, false); }; /** @@ -5619,7 +5629,6 @@ */ UnitAI.prototype.Autogarrison = function(target) { - this.isGarrisoned = true; this.PushOrderFront("Garrison", { "target": target }); }; Index: binaries/data/mods/public/simulation/helpers/Transform.js =================================================================== --- binaries/data/mods/public/simulation/helpers/Transform.js +++ binaries/data/mods/public/simulation/helpers/Transform.js @@ -67,7 +67,7 @@ cmpNewUnitAI.SetHeldPosition(pos.x, pos.z); if (cmpUnitAI.GetStanceName()) cmpNewUnitAI.SwitchToStance(cmpUnitAI.GetStanceName()); - if (cmpUnitAI.IsGarrisoned()) + if (cmpUnitAI.GetGarrisonHolder() != INVALID_ENTITY) cmpNewUnitAI.SetGarrisoned(); cmpNewUnitAI.AddOrders(cmpUnitAI.GetOrders()); if (cmpUnitAI.IsGuardOf())