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 @@ -971,8 +971,8 @@ delete this.patrolStartPosOrder; }, - "MovementUpdate": function() { - if (!this.CheckRange(this.order.data)) + "MovementUpdate": function(msg) { + if (!msg.error && !this.CheckRange(this.order.data)) return; /** * A-B-A-B-..: @@ -1065,7 +1065,7 @@ }, "MovementUpdate": function(msg) { - if (!this.CheckRange(this.order.data)) + if (!msg.error && !this.CheckRange(this.order.data)) return; if (this.FinishOrder()) @@ -1553,8 +1553,8 @@ this.FindWalkAndFightTargets(); }, - "MovementUpdate": function() { - if (!this.CheckRange(this.order.data)) + "MovementUpdate": function(msg) { + if (!msg.error && !this.CheckRange(this.order.data)) return; if (this.orderQueue.length == 1) @@ -1619,7 +1619,7 @@ }, "MovementUpdate": function() { - if (this.CheckTargetRangeExplicit(this.isGuardOf, 0, this.guardRange)) + if (msg.error || this.CheckTargetRangeExplicit(this.isGuardOf, 0, this.guardRange)) this.SetNextState("GUARDING"); }, }, @@ -1702,7 +1702,7 @@ "MovementUpdate": function() { // When we've run far enough, stop fleeing - if (this.CheckTargetRangeExplicit(this.order.data.target, this.order.data.distanceToFlee, -1)) + if (msg.error || this.CheckTargetRangeExplicit(this.order.data.target, this.order.data.distanceToFlee, -1)) this.FinishOrder(); }, @@ -1755,9 +1755,39 @@ } }, - "MovementUpdate": function() { + "MovementUpdate": function(msg) { + if (msg.error && this.orderQueue.length > 1) + { + this.FinishOrder(); + return; + } + + if (msg.error && !this.order.data.force) + { + // Move to Attacking and let the timer handle things. + this.SetNextState("ATTACKING"); + return; + } + else if (msg.error) + { + // Assume unitMotion exists since it's the component that sent the message. + let cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); + let lastPos = cmpUnitMotion.GetLastWaypoint(); + if (lastPos.x < 0) + { + // No known position - move to Attacking and let the timer handle things. + this.SetNextState("ATTACKING"); + return; + } + // Go to the last known position and try to find enemies there. + this.PushOrder("Walk", { "x": lastPos.x, "z": lastPos.y, "force": false }); + this.PushOrder("WalkAndFight", { "x": lastPos.x, "z": lastPos.y, "force": false }); + return; + } + if (!this.CheckTargetAttackRange(this.order.data.target, this.order.data.attackType)) return; + // If the unit needs to unpack, do so if (this.CanUnpack()) { @@ -1820,20 +1850,25 @@ if (cmpFormation) animationName = cmpFormation.GetFormationAnimation(this.entity, animationName); } + this.SetAnimationVariant("combat"); - this.SelectAnimation(animationName); - this.SetAnimationSync(prepare, this.attackTimers.repeat); this.StartTimer(prepare, this.attackTimers.repeat); - // TODO: we should probably only bother syncing projectile attacks, not melee - // If using a non-default prepare time, re-sync the animation when the timer runs. - this.resyncAnimation = (prepare != this.attackTimers.prepare) ? true : false; - - this.FaceTowardsTarget(this.order.data.target); - - var cmpBuildingAI = Engine.QueryInterface(this.entity, IID_BuildingAI); + let cmpBuildingAI = Engine.QueryInterface(this.entity, IID_BuildingAI); if (cmpBuildingAI) cmpBuildingAI.SetUnitAITarget(this.order.data.target); + + if (this.CheckTargetAttackRange(target, this.order.data.attackType)) + { + this.SelectAnimation(animationName); + this.SetAnimationSync(prepare, this.attackTimers.repeat); + // TODO: we should probably only bother syncing projectile attacks, not melee + + // If using a non-default prepare time, re-sync the animation when the timer runs. + this.resyncAnimation = (prepare != this.attackTimers.prepare) ? true : false; + + this.FaceTowardsTarget(this.order.data.target); + } }, "leave": function() { @@ -2465,8 +2500,8 @@ this.StopMoving(); }, - "MovementUpdate": function() { - if (!this.CheckTargetRange(this.order.data.target, IID_Trader)) + "MovementUpdate": function(msg) { + if (!msg.error && !this.CheckTargetRange(this.order.data.target, IID_Trader)) return; if (this.waypoints && this.waypoints.length) @@ -2505,8 +2540,7 @@ }, "MovementUpdate": function() { - if (this.CheckRange(this.order.data, IID_Builder)) - this.SetNextState("REPAIRING"); + this.SetNextState("REPAIRING"); }, }, @@ -2709,8 +2743,7 @@ }, "MovementUpdate": function() { - if (this.CheckGarrisonRange(this.order.data.target)) - this.SetNextState("GARRISONED"); + this.SetNextState("GARRISONED"); }, }, @@ -2893,8 +2926,7 @@ }, "MovementUpdate": function() { - if (this.CheckRange(this.order.data)) - this.SetNextState("LOADING"); + this.SetNextState("LOADING"); }, "PickupCanceled": function() { Index: source/simulation2/components/CCmpUnitMotion.cpp =================================================================== --- source/simulation2/components/CCmpUnitMotion.cpp +++ source/simulation2/components/CCmpUnitMotion.cpp @@ -332,6 +332,14 @@ return m_MoveRequest.m_Type != MoveRequest::NONE; } + virtual CFixedVector2D GetLastWaypoint() const + { + const std::vector& waypoints = m_LongPath.m_Waypoints.empty() ? m_ShortPath.m_Waypoints : m_LongPath.m_Waypoints; + if (waypoints.empty()) + return CFixedVector2D(fixed::FromInt(-1), fixed::FromInt(-1)); + return CFixedVector2D(waypoints.front().x, waypoints.front().z); + } + virtual fixed GetSpeedMultiplier() const { return m_SpeedMultiplier; Index: source/simulation2/components/ICmpUnitMotion.h =================================================================== --- source/simulation2/components/ICmpUnitMotion.h +++ source/simulation2/components/ICmpUnitMotion.h @@ -23,6 +23,8 @@ #include "simulation2/components/ICmpPathfinder.h" // for pass_class_t #include "simulation2/components/ICmpPosition.h" // for entity_pos_t +class CFixedVector2D; + /** * Motion interface for entities with complex movement capabilities. * (Simpler motion is handled by ICmpMotion instead.) @@ -70,6 +72,11 @@ */ virtual void StopMoving() = 0; + /** + * Returns the position of the last waypoint of our current path, or (-1, -1) if we have none. + */ + virtual CFixedVector2D GetLastWaypoint() const = 0; + /** * Get the distance travelled over the last turn. */ Index: source/simulation2/components/ICmpUnitMotion.cpp =================================================================== --- source/simulation2/components/ICmpUnitMotion.cpp +++ source/simulation2/components/ICmpUnitMotion.cpp @@ -30,6 +30,7 @@ DEFINE_INTERFACE_METHOD_0("StopMoving", void, ICmpUnitMotion, StopMoving) DEFINE_INTERFACE_METHOD_CONST_0("GetCurrentSpeed", fixed, ICmpUnitMotion, GetCurrentSpeed) DEFINE_INTERFACE_METHOD_CONST_0("IsMoving", bool, ICmpUnitMotion, IsMoving) +DEFINE_INTERFACE_METHOD_CONST_0("GetLastWaypoint", CFixedVector2D, ICmpUnitMotion, GetLastWaypoint) DEFINE_INTERFACE_METHOD_CONST_0("GetSpeed", fixed, ICmpUnitMotion, GetSpeed) DEFINE_INTERFACE_METHOD_CONST_0("GetWalkSpeed", fixed, ICmpUnitMotion, GetWalkSpeed) DEFINE_INTERFACE_METHOD_CONST_0("GetRunMultiplier", fixed, ICmpUnitMotion, GetRunMultiplier) @@ -80,6 +81,11 @@ return m_Script.Call("IsMoving"); } + virtual CFixedVector2D GetLastWaypoint() const + { + return m_Script.Call("GetLastWaypoint"); + } + virtual fixed GetSpeed() const { return m_Script.Call("GetSpeed");