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 @@ -206,6 +206,7 @@ if (visibleGarrisonPoint) { visibleGarrisonPoint.entity = entity; + // Angle of turrets: // Renamed entities (vgpEntity != undefined) should keep their angle. // Otherwise if an angle is given in the visibleGarrisonPoint, use it. @@ -224,6 +225,12 @@ let cmpUnitAI = Engine.QueryInterface(entity, IID_UnitAI); if (cmpUnitAI) cmpUnitAI.SetTurretStance(); + + // Remove the unit's obstruction to avoid interfering with pathing. + let cmpObstruction = Engine.QueryInterface(entity, IID_Obstruction); + if (cmpObstruction) + cmpObstruction.SetDisableBlockMovementPathfinding(true, true, -1); + } else cmpPosition.MoveOutOfWorld(); @@ -324,6 +331,12 @@ if (cmpEntUnitAI) cmpEntUnitAI.ResetTurretStance(); vgp.entity = null; + + // Reset the obstruction flags to template defaults. + let cmpObstruction = Engine.QueryInterface(entity, IID_Obstruction); + if (cmpObstruction) + cmpObstruction.SetDisableBlockMovementPathfinding(false, false, -1); + break; } Index: binaries/data/mods/public/simulation/components/Gate.js =================================================================== --- binaries/data/mods/public/simulation/components/Gate.js +++ binaries/data/mods/public/simulation/components/Gate.js @@ -59,6 +59,23 @@ } }; +Gate.prototype.OnGarrisonedUnitsChanged = function(msg) +{ + for (let entity of msg.removed) + { + // Ignore entities that cannot move as those won't be able to go through the gate. + let unitAI = Engine.QueryInterface(entity, IID_UnitAI); + if (!unitAI.AbleToMove()) + continue; + this.allies.push(entity); + } + + for (let entity of msg.added) + this.allies.splice(this.allies.indexOf(entity), 1); + + this.OperateGate(); +}; + /** * Setup the range query to detect units coming in & out of range */ @@ -91,7 +108,13 @@ if (msg.added.length > 0) for (let entity of msg.added) + { + // Ignore entities that cannot move as those won't be able to go through the gate. + let unitAI = Engine.QueryInterface(entity, IID_UnitAI); + if (!unitAI.AbleToMove()) + continue; this.allies.push(entity); + } if (msg.removed.length > 0) for (let entity of msg.removed) @@ -220,7 +243,7 @@ return; // The gate can't be closed if there are entities colliding with it. - var collisions = cmpObstruction.GetEntitiesBlockingConstruction(); + var collisions = cmpObstruction.GetEntitiesBlockingMovement(); if (collisions.length) { if (!this.timer) 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 @@ -213,7 +213,7 @@ // If foundation is not ally of entity, or if entity is unpacked siege, // ignore the order if (!IsOwnedByAllyOfEntity(this.entity, msg.data.target) && !Engine.QueryInterface(SYSTEM_ENTITY, IID_CeasefireManager).IsCeasefireActive() || - this.IsPacking() || this.CanPack() || this.IsTurret()) + !this.AbleToMove() || this.CanPack() || this.IsTurret()) { this.FinishOrder(); return; @@ -1228,7 +1228,7 @@ // If foundation is not ally of entity, or if entity is unpacked siege, // ignore the order if (!IsOwnedByAllyOfEntity(this.entity, msg.data.target) && !Engine.QueryInterface(SYSTEM_ENTITY, IID_CeasefireManager).IsCeasefireActive() || - this.IsPacking() || this.CanPack() || this.IsTurret()) + !this.AbleToMove() || this.CanPack() || this.IsTurret()) { this.FinishOrder(); return; @@ -2785,6 +2785,7 @@ if (cmpGarrisonHolder.Garrison(this.entity)) { this.isGarrisoned = true; + this.SetImmobile(true); if (this.formationController) { @@ -2887,16 +2888,19 @@ "PACKING": { "enter": function() { this.StopMoving(); + this.SetImmobile(true); - var cmpPack = Engine.QueryInterface(this.entity, IID_Pack); + let cmpPack = Engine.QueryInterface(this.entity, IID_Pack); cmpPack.Pack(); }, "PackFinished": function(msg) { + this.SetImmobile(false); this.FinishOrder(); }, "leave": function() { + this.SetImmobile(false); }, "Attacked": function(msg) { @@ -2907,16 +2911,19 @@ "UNPACKING": { "enter": function() { this.StopMoving(); + this.SetImmobile(true); var cmpPack = Engine.QueryInterface(this.entity, IID_Pack); cmpPack.Unpack(); }, "PackFinished": function(msg) { + this.SetImmobile(false); this.FinishOrder(); }, "leave": function() { + this.SetImmobile(false); }, "Attacked": function(msg) { @@ -3104,6 +3111,7 @@ 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, packing...) this.finishedOrder = false; // used to find if all formation members finished the order this.heldPosition = undefined; @@ -3201,6 +3209,26 @@ return !this.orderQueue.length || this.orderQueue[0].type == "Garrison"; }; +UnitAI.prototype.SetImmobile = function(immobile) +{ + this.isImmobile = immobile; +}; + +/** + * @param cmpUnitMotion - optionally pass unitMotion to avoid querying it here + * @returns true if the entity can move, i.e. has UnitMotion + * and isn't immobile. + */ +UnitAI.prototype.AbleToMove = function(cmpUnitMotion) +{ + if (!cmpUnitMotion) + cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); + + if (this.isImmobile || !cmpUnitMotion) + return false; + return true; +}; + UnitAI.prototype.IsFleeing = function() { var state = this.GetCurrentState().split(".").pop(); @@ -4115,8 +4143,9 @@ UnitAI.prototype.StopMoving = function() { - var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); - cmpUnitMotion.StopMoving(); + let cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); + if (cmpUnitMotion) + cmpUnitMotion.StopMoving(); }; /** @@ -4144,13 +4173,17 @@ UnitAI.prototype.MoveToPoint = function(x, z) { - var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); + let cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); + if (!this.AbleToMove(cmpUnitMotion)) + return false; return cmpUnitMotion.MoveToPointRange(x, z, 0, 0); }; UnitAI.prototype.MoveToPointRange = function(x, z, rangeMin, rangeMax) { - var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); + let cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); + if (!this.AbleToMove(cmpUnitMotion)) + return false; return cmpUnitMotion.MoveToPointRange(x, z, rangeMin, rangeMax); }; @@ -4159,7 +4192,10 @@ if (!this.CheckTargetVisible(target)) return false; - var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); + let cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); + if (!this.AbleToMove(cmpUnitMotion)) + return false; + return cmpUnitMotion.MoveToTargetRange(target, 0, 0); }; @@ -4173,7 +4209,10 @@ return false; var range = cmpRanged.GetRange(type); - var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); + let cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); + if (!this.AbleToMove(cmpUnitMotion)) + return false; + return cmpUnitMotion.MoveToTargetRange(target, range.min, range.max); }; @@ -4192,6 +4231,10 @@ return false; } + let cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); + if (!this.AbleToMove(cmpUnitMotion)) + return false; + let cmpFormation = Engine.QueryInterface(target, IID_Formation); if (cmpFormation) target = cmpFormation.GetClosestMember(this.entity); @@ -4227,7 +4270,6 @@ // The parabole changes while walking so be cautious: let guessedMaxRange = parabolicMaxRange > range.max ? (range.max + parabolicMaxRange) / 2 : parabolicMaxRange; - let cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); return cmpUnitMotion.MoveToTargetRange(target, range.min, guessedMaxRange); }; @@ -4236,7 +4278,10 @@ if (!this.CheckTargetVisible(target)) return false; - var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); + let cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); + if (!this.AbleToMove(cmpUnitMotion)) + return false; + return cmpUnitMotion.MoveToTargetRange(target, min, max); }; @@ -4245,12 +4290,15 @@ if (!this.CheckTargetVisible(target)) return false; - var cmpGarrisonHolder = Engine.QueryInterface(target, IID_GarrisonHolder); + let cmpGarrisonHolder = Engine.QueryInterface(target, IID_GarrisonHolder); if (!cmpGarrisonHolder) return false; - var range = cmpGarrisonHolder.GetLoadingRange(); + let range = cmpGarrisonHolder.GetLoadingRange(); + + let cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); + if (!this.AbleToMove(cmpUnitMotion)) + return false; - var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); return cmpUnitMotion.MoveToTargetRange(target, range.min, range.max); }; @@ -4904,7 +4952,7 @@ // ignore this new request so we don't end up being too indecisive // to ever actually move anywhere // Ignore also the request if we are packing - if (this.order && (this.order.type == "LeaveFoundation" || (this.order.type == "Flee" && this.order.data.target == target) || this.IsPacking())) + if (this.order && (this.order.type == "LeaveFoundation" || (this.order.type == "Flee" && this.order.data.target == target) || !this.AbleToMove())) return; this.PushOrderFront("LeaveFoundation", { "target": target, "force": true }); @@ -4958,7 +5006,10 @@ UnitAI.prototype.Ungarrison = function() { if (this.IsGarrisoned()) + { + this.SetImmobile(false); this.AddOrder("Ungarrison", null, false); + } }; /** Index: binaries/data/mods/public/simulation/templates/structures/iber_wall_gate.xml =================================================================== --- binaries/data/mods/public/simulation/templates/structures/iber_wall_gate.xml +++ binaries/data/mods/public/simulation/templates/structures/iber_wall_gate.xml @@ -4,6 +4,25 @@ 9.0 + + 3 + Ranged+Infantry + 0.1 + Unit + 0 + 2 + + + 011.50 + + + 411.50 + + + -411.50 + + + iber Biko Sarbide @@ -15,6 +34,7 @@ + structures/iberians/wall_gate.xml Index: source/simulation2/components/CCmpObstruction.cpp =================================================================== --- source/simulation2/components/CCmpObstruction.cpp +++ source/simulation2/components/CCmpObstruction.cpp @@ -645,6 +645,11 @@ return ret; } + virtual std::vector GetEntitiesBlockingMovement() const + { + return GetEntitiesByFlags(ICmpObstructionManager::FLAG_BLOCK_MOVEMENT); + } + virtual std::vector GetEntitiesBlockingConstruction() const { return GetEntitiesByFlags(ICmpObstructionManager::FLAG_BLOCK_CONSTRUCTION); Index: source/simulation2/components/ICmpObstruction.h =================================================================== --- source/simulation2/components/ICmpObstruction.h +++ source/simulation2/components/ICmpObstruction.h @@ -94,6 +94,12 @@ */ virtual std::vector GetEntitiesByFlags(ICmpObstructionManager::flags_t flags) const = 0; + /** + * Returns a list of entities that are blocking movement. + * @return vector of blocking entities + */ + virtual std::vector GetEntitiesBlockingMovement() const = 0; + /** * Returns a list of entities that are blocking construction of a foundation. * @return vector of blocking entities Index: source/simulation2/components/ICmpObstruction.cpp =================================================================== --- source/simulation2/components/ICmpObstruction.cpp +++ source/simulation2/components/ICmpObstruction.cpp @@ -50,6 +50,7 @@ DEFINE_INTERFACE_METHOD_CONST_0("CheckShorePlacement", bool, ICmpObstruction, CheckShorePlacement) DEFINE_INTERFACE_METHOD_CONST_2("CheckFoundation", std::string, ICmpObstruction, CheckFoundation_wrapper, std::string, bool) DEFINE_INTERFACE_METHOD_CONST_0("CheckDuplicateFoundation", bool, ICmpObstruction, CheckDuplicateFoundation) +DEFINE_INTERFACE_METHOD_CONST_0("GetEntitiesBlockingMovement", std::vector, ICmpObstruction, GetEntitiesBlockingMovement) DEFINE_INTERFACE_METHOD_CONST_0("GetEntitiesBlockingConstruction", std::vector, ICmpObstruction, GetEntitiesBlockingConstruction) DEFINE_INTERFACE_METHOD_CONST_0("GetEntitiesDeletedUponConstruction", std::vector, ICmpObstruction, GetEntitiesDeletedUponConstruction) DEFINE_INTERFACE_METHOD_1("SetActive", void, ICmpObstruction, SetActive, bool)