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 @@ -137,7 +137,13 @@ // Default event handlers: - "MoveCompleted": function() { + "MoveCompletedHint": function() { + // ignore spurious movement messages + // (these can happen when stopping moving at the same time + // as switching states) + }, + + "MoveFailureHint": function() { // ignore spurious movement messages // (these can happen when stopping moving at the same time // as switching states) @@ -879,8 +885,8 @@ this.StopMoving(); }, - "MoveCompleted": function(msg) { - if (this.FinishOrder()) + "MoveCompletedHint": function(msg) { + if (this.CheckRange(this.order.data) && this.FinishOrder()) this.CallMemberFunction("ResetFinishOrder", []); }, }, @@ -911,8 +917,8 @@ this.StopTimer(); }, - "MoveCompleted": function(msg) { - if (this.FinishOrder()) + "MoveCompletedHint": function(msg) { + if (this.CheckRange(this.order.data) && this.FinishOrder()) this.CallMemberFunction("ResetFinishOrder", []); }, }, @@ -956,7 +962,9 @@ delete this.patrolStartPosOrder; }, - "MoveCompleted": function() { + "MoveCompletedHint": function() { + if (!this.CheckRange(this.order.data)) + return; /** * A-B-A-B-..: * if the user only commands one patrol order, the patrol will be between @@ -1012,7 +1020,7 @@ this.StopMoving(); }, - "MoveCompleted": function(msg) { + "MoveCompletedHint": function(msg) { this.SetNextState("GARRISONING"); }, }, @@ -1047,7 +1055,9 @@ this.StopMoving(); }, - "MoveCompleted": function(msg) { + "MoveCompletedHint": function(msg) { + if (!this.CheckRange(this.order.data)) + return; if (this.FinishOrder()) { @@ -1077,7 +1087,7 @@ this.StopMoving(); }, - "MoveCompleted": function(msg) { + "MoveCompletedHint": function(msg) { var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack); this.CallMemberFunction("Attack", [this.order.data.target, this.order.data.allowCapture, false]); if (cmpAttack.CanAttackAsFormation()) @@ -1253,10 +1263,14 @@ // Occurs when the unit has reached its destination and the controller // is done moving. The controller is notified. - "MoveCompleted": function(msg) { + "MoveCompletedHint": function(msg) { // We can only finish this order if the move was really completed. - if (!msg.data.error && this.FinishOrder()) + if (!this.CheckRange(this.order.data)) return; + + if (this.FinishOrder()) + return; + var cmpVisual = Engine.QueryInterface(this.entity, IID_Visual); if (cmpVisual) { @@ -1284,7 +1298,9 @@ this.SelectAnimation("move"); }, - "MoveCompleted": function() { + "MoveCompletedHint": function() { + if (!this.CheckRange(this.order.data)) + return; this.StopMoving(); this.FinishOrder(); }, @@ -1471,8 +1487,9 @@ this.StopMoving(); }, - "MoveCompleted": function() { - this.FinishOrder(); + "MoveCompletedHint": function() { + if (this.CheckRange(this.order.data)) + this.FinishOrder(); }, }, @@ -1500,8 +1517,9 @@ this.SetDefaultAnimationVariant(); }, - "MoveCompleted": function() { - this.FinishOrder(); + "MoveCompletedHint": function() { + if (this.CheckRange(this.order.data)) + this.FinishOrder(); }, }, @@ -1539,7 +1557,10 @@ this.FindWalkAndFightTargets(); }, - "MoveCompleted": function() { + "MoveCompletedHint": function() { + if (!this.CheckRange(this.order.data)) + return; + if (this.orderQueue.length == 1) this.PushOrder("Patrol", this.patrolStartPosOrder); @@ -1602,8 +1623,7 @@ this.SetDefaultAnimationVariant(); }, - "MoveCompleted": function() { - this.ResetSpeedMultiplier(); + "MoveCompletedHint": function() { if (this.CheckTargetRangeExplicit(this.isGuardOf, 0, this.guardRange)) this.SetNextState("GUARDING"); }, @@ -1682,9 +1702,10 @@ this.StopMoving(); }, - "MoveCompleted": function() { + "MoveCompletedHint": function() { // When we've run far enough, stop fleeing - this.FinishOrder(); + if (this.CheckRange(this.order.data)) + this.FinishOrder(); }, // TODO: what if we run into more enemies while fleeing? @@ -1737,31 +1758,17 @@ } }, - "MoveCompleted": function() { - - if (this.CheckTargetAttackRange(this.order.data.target, this.order.data.attackType)) + "MoveCompletedHint": function() { + if (!this.CheckTargetAttackRange(this.order.data.target, this.order.data.attackType)) + return; + // If the unit needs to unpack, do so + if (this.CanUnpack()) { - // If the unit needs to unpack, do so - if (this.CanUnpack()) - { - this.PushOrderFront("Unpack", { "force": true }); - return; - } - else - this.SetNextState("ATTACKING"); + this.PushOrderFront("Unpack", { "force": true }); + return; } else - { - if (this.MoveToTargetAttackRange(this.order.data.target, this.order.data.attackType)) - { - this.SetNextState("APPROACHING"); - } - else - { - // Give up - this.FinishOrder(); - } - } + this.SetNextState("ATTACKING"); }, }, @@ -2003,7 +2010,7 @@ } }, - "MoveCompleted": function() { + "MoveCompletedHint": function() { this.SetNextState("ATTACKING"); }, }, @@ -2083,52 +2090,51 @@ return false; }, - "MoveCompleted": function(msg) { - if (msg.data.error) + "MoveFailureHint": function() { + // We probably failed to reach the target + + // remove us from the list of entities gathering from Resource. + var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership); + var cmpSupply = Engine.QueryInterface(this.gatheringTarget, IID_ResourceSupply); + if (cmpSupply && cmpOwnership) + cmpSupply.RemoveGatherer(this.entity, cmpOwnership.GetOwner()); + else if (cmpSupply) + cmpSupply.RemoveGatherer(this.entity); + + // Save the current order's data in case we need it later + var oldType = this.order.data.type; + var oldTarget = this.order.data.target; + var oldTemplate = this.order.data.template; + + // Try the next queued order if there is any + if (this.FinishOrder()) + return; + + // Try to find another nearby target of the same specific type + // Also don't switch to a different type of huntable animal + var nearby = this.FindNearbyResource(function(ent, type, template) { + return ( + ent != oldTarget + && ((type.generic == "treasure" && oldType.generic == "treasure") + || (type.specific == oldType.specific + && (type.specific != "meat" || oldTemplate == template))) + ); + }); + if (nearby) { - // We failed to reach the target - - // remove us from the list of entities gathering from Resource. - var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership); - var cmpSupply = Engine.QueryInterface(this.gatheringTarget, IID_ResourceSupply); - if (cmpSupply && cmpOwnership) - cmpSupply.RemoveGatherer(this.entity, cmpOwnership.GetOwner()); - else if (cmpSupply) - cmpSupply.RemoveGatherer(this.entity); - - // Save the current order's data in case we need it later - var oldType = this.order.data.type; - var oldTarget = this.order.data.target; - var oldTemplate = this.order.data.template; - - // Try the next queued order if there is any - if (this.FinishOrder()) - return; - - // Try to find another nearby target of the same specific type - // Also don't switch to a different type of huntable animal - var nearby = this.FindNearbyResource(function(ent, type, template) { - return ( - ent != oldTarget - && ((type.generic == "treasure" && oldType.generic == "treasure") - || (type.specific == oldType.specific - && (type.specific != "meat" || oldTemplate == template))) - ); - }); - if (nearby) - { - this.PerformGather(nearby, false, false); - return; - } - - // Couldn't find anything else. Just try this one again, - // maybe we'll succeed next time - this.PerformGather(oldTarget, false, false); + this.PerformGather(nearby, false, false); return; } - // We reached the target - start gathering from it now - this.SetNextState("GATHERING"); + // Couldn't find anything else. Just try this one again, + // maybe we'll succeed next time + this.PerformGather(oldTarget, false, false); + return; + }, + + "MoveCompletedHint": function() { + if (this.CheckTargetRange(this.gatheringTarget, IID_ResourceGatherer)) + this.SetNextState("GATHERING"); }, "leave": function() { @@ -2161,7 +2167,10 @@ this.StopMoving(); }, - "MoveCompleted": function(msg) { + "MoveCompletedHint": function(msg) { + if (!this.CheckRange(this.order.data)) + return; + var resourceType = this.order.data.type; var resourceTemplate = this.order.data.template; @@ -2451,7 +2460,7 @@ } }, - "MoveCompleted": function() { + "MoveCompletedHint": function() { this.SetNextState("HEALING"); }, }, @@ -2559,8 +2568,9 @@ this.SelectAnimation("idle"); }, - "MoveCompleted": function() { - this.SetNextState("DROPPING"); + "MoveCompletedHint": function() { + if (this.CheckRange(this.order.data)) + this.SetNextState("DROPPING"); }, }, @@ -2634,7 +2644,10 @@ this.StopMoving(); }, - "MoveCompleted": function() { + "MoveCompletedHint": function() { + if (!this.CheckTargetRange(this.order.data.target, IID_Trader)) + return; + if (this.waypoints && this.waypoints.length) { if (!this.MoveToMarket(this.order.data.target)) @@ -2671,8 +2684,9 @@ this.StopMoving(); }, - "MoveCompleted": function() { - this.SetNextState("REPAIRING"); + "MoveCompletedHint": function() { + if (this.CheckRange(this.order.data)) + this.SetNextState("REPAIRING"); }, }, @@ -2880,8 +2894,9 @@ this.StopMoving(); }, - "MoveCompleted": function() { - this.SetNextState("GARRISONED"); + "MoveCompletedHint": function() { + if (this.CheckRange(this.order.data)) + this.SetNextState("GARRISONED"); }, }, @@ -3070,8 +3085,9 @@ this.StopMoving(); }, - "MoveCompleted": function() { - this.SetNextState("LOADING"); + "MoveCompletedHint": function() { + if (this.CheckRange(this.order.data)) + this.SetNextState("LOADING"); }, "PickupCanceled": function() { @@ -3186,7 +3202,7 @@ this.SetNextState("FEEDING"); }, - "MoveCompleted": function() { + "MoveCompletedHint": function() { this.MoveRandomly(+this.template.RoamDistance); }, }, @@ -3219,8 +3235,6 @@ } }, - "MoveCompleted": function() { }, - "Timer": function(msg) { this.SetNextState("ROAMING"); }, @@ -3926,7 +3940,10 @@ UnitAI.prototype.OnMotionChanged = function(msg) { - this.UnitFsm.ProcessMessage(this, {"type": "MoveCompleted", "data": msg}); + if (msg.error) + this.UnitFsm.ProcessMessage(this, { "type": "MoveFailureHint" }); + else + this.UnitFsm.ProcessMessage(this, { "type": "MoveCompletedHint" }); }; UnitAI.prototype.OnGlobalConstructionFinished = function(msg) @@ -4406,6 +4423,50 @@ return cmpUnitMotion.MoveToTargetRange(target, range.min, range.max); }; +/** + * Generic dispatcher for other Check...Range functions. + * @param iid - Interface ID (optional) implementing GetRange + * @param type - Range type for the interface call + */ +UnitAI.prototype.CheckRange = function(data, iid, type) +{ + if (data["target"]) + { + if (data["min"] || data["max"]) + { + if (!this.CheckTargetRangeExplicit(data.target, data.min || -1, data.max || -1)) + return false; + } + else + { + if (!iid) + { + if (!this.CheckTargetRangeExplicit(data.target, 0, 0)) + return false; + } + else + { + if (!this.CheckTargetRange(data.target, iid, type)) + return false; + } + } + } + else + { + if (data["min"] || data["max"]) + { + if (!this.CheckPointRangeExplicit(data.x, data.z, data.min || -1, data.max || -1)) + return false; + } + else + { + if (!this.CheckPointRangeExplicit(data.x, data.z, 0, 0)) + return false; + } + } + return true; +} + UnitAI.prototype.CheckPointRangeExplicit = function(x, z, min, max) { var cmpObstructionManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ObstructionManager); Index: source/simulation2/components/CCmpUnitMotion.cpp =================================================================== --- source/simulation2/components/CCmpUnitMotion.cpp +++ source/simulation2/components/CCmpUnitMotion.cpp @@ -548,8 +548,6 @@ void MoveFailed() { - StopMoving(); - CmpPtr cmpObstruction(GetEntityHandle()); if (cmpObstruction) cmpObstruction->SetMovingFlag(false); @@ -690,10 +688,8 @@ CmpPtr cmpPosition(GetEntityHandle()); if (!cmpPosition || !cmpPosition->IsInWorld()) { - if (m_PathState == PATHSTATE_WAITING_REQUESTING_LONG || m_PathState == PATHSTATE_WAITING_REQUESTING_SHORT) - MoveFailed(); - else if (m_PathState == PATHSTATE_FOLLOWING_REQUESTING_LONG || m_PathState == PATHSTATE_FOLLOWING_REQUESTING_SHORT) - StopMoving(); + // We will probably fail to move so inform components but keep on trying anyways. + MoveFailed(); return; } @@ -752,7 +748,6 @@ if (CloseEnoughFromDestinationToStop(pos)) { - StopMoving(); MoveSucceeded(); if (m_FacePointAfterMove) @@ -935,7 +930,6 @@ // check if we've arrived. if (CloseEnoughFromDestinationToStop(pos)) { - StopMoving(); MoveSucceeded(); if (m_FacePointAfterMove) @@ -1023,12 +1017,9 @@ else { // check if target was reached in case of a moving target - CmpPtr cmpUnitMotion(GetSimContext(), m_TargetEntity); - if (cmpUnitMotion && cmpUnitMotion->IsMoving() && - MoveToTargetRange(m_TargetEntity, m_TargetMinRange, m_TargetMaxRange)) + CmpPtr cmpObstructionManager(GetSystemEntity()); + if (cmpObstructionManager->IsInTargetRange(GetEntityId(), m_TargetEntity, m_TargetMinRange, m_TargetMaxRange, false)) { - // Not in formation, so just finish moving - StopMoving(); m_State = STATE_IDLE; MoveSucceeded();