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 @@ -610,7 +610,8 @@ this.SetNextState("IDLE"); return; } - else if (this.IsGarrisoned()) + + if (this.IsGarrisoned()) { if (this.IsAnimal()) this.SetNextState("ANIMAL.GARRISON.GARRISONED"); @@ -619,12 +620,22 @@ return; } + // Also pack when we are in range. if (this.CanPack()) { this.PushOrderFront("Pack", { "force": true }); return; } + if (this.CheckGarrisonRange(this.order.data.target)) + { + if (this.IsAnimal()) + this.SetNextState("ANIMAL.GARRISON.GARRISONED"); + else + this.SetNextState("INDIVIDUAL.GARRISON.GARRISONED"); + return; + } + if (this.IsAnimal()) this.SetNextState("ANIMAL.GARRISON.APPROACHING"); else Index: binaries/data/mods/public/simulation/components/tests/test_UnitAI.js =================================================================== --- binaries/data/mods/public/simulation/components/tests/test_UnitAI.js +++ binaries/data/mods/public/simulation/components/tests/test_UnitAI.js @@ -63,6 +63,7 @@ unitAI.AbleToMove = () => true; AddMock(target_ent, IID_GarrisonHolder, { + "GetLoadingRange": () => ({ "max": 100, "min": 0 }), "CanPickup": () => false }); Index: source/simulation2/components/CCmpUnitMotion.cpp =================================================================== --- source/simulation2/components/CCmpUnitMotion.cpp +++ source/simulation2/components/CCmpUnitMotion.cpp @@ -113,6 +113,7 @@ public: static void ClassInit(CComponentManager& componentManager) { + componentManager.SubscribeToMessageType(MT_TurnStart); componentManager.SubscribeToMessageType(MT_Update_MotionFormation); componentManager.SubscribeToMessageType(MT_Update_MotionUnit); componentManager.SubscribeToMessageType(MT_PathResult); @@ -305,6 +306,11 @@ { switch (msg.GetType()) { + case MT_TurnStart: + { + TurnStart(); + break; + } case MT_Update_MotionFormation: { if (m_FormationController) @@ -607,6 +613,12 @@ void PathResult(u32 ticket, const WaypointPath& path); /** + * TurnStart message handler. Do anything here that must be done before commands + * are processed or movement happens. + */ + void TurnStart(); + + /** * Do the per-turn movement and other updates. */ void Move(fixed dt); @@ -826,15 +838,8 @@ } } -void CCmpUnitMotion::Move(fixed dt) +void CCmpUnitMotion::TurnStart() { - PROFILE("Move"); - - // If we were idle and will still be, we can return. - // TODO: this will need to be removed if pushing is implemented. - if (m_CurSpeed == fixed::Zero() && m_MoveRequest.m_Type == MoveRequest::NONE) - return; - if (PossiblyAtDestination()) MoveSucceeded(); else if (!TargetHasValidPosition()) @@ -849,6 +854,16 @@ MoveFailed(); } +} + +void CCmpUnitMotion::Move(fixed dt) +{ + PROFILE("Move"); + + // If we were idle and will still be, we can return. + // TODO: this will need to be removed if pushing is implemented. + if (m_CurSpeed == fixed::Zero() && m_MoveRequest.m_Type == MoveRequest::NONE) + return; CmpPtr cmpPosition(GetEntityHandle()); if (!cmpPosition || !cmpPosition->IsInWorld()) @@ -979,7 +994,7 @@ target = CFixedVector2D(shortPath.m_Waypoints.back().x, shortPath.m_Waypoints.back().z); CFixedVector2D offset = target - pos; - if (turnRate > zero) + if (turnRate > zero && !offset.IsZero()) { fixed maxRotation = turnRate.Multiply(timeLeft); fixed angleDiff = angle - atan2_approx(offset.X, offset.Y);