Index: ps/trunk/binaries/data/mods/public/simulation/components/UnitAI.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/components/UnitAI.js +++ ps/trunk/binaries/data/mods/public/simulation/components/UnitAI.js @@ -207,9 +207,6 @@ return; } - var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); - cmpUnitMotion.MoveToFormationOffset(msg.data.target, msg.data.x, msg.data.z); - this.SetNextState("FORMATIONMEMBER.WALKING"); }, @@ -225,17 +222,15 @@ this.FinishOrder(); return; } - // Move a tile outside the building + + // Move a tile outside the building if necessary. let range = 4; - if (this.MoveToTargetRangeExplicit(msg.data.target, range, range)) - { - // We've started walking to the given point - this.SetNextState("INDIVIDUAL.WALKING"); - } + if (this.CheckTargetRangeExplicit(msg.data.target, range, -1)) + this.FinishOrder(); else { - // We are already at the target, or can't move at all - this.FinishOrder(); + this.order.data.min = range; + this.SetNextState("INDIVIDUAL.WALKING"); } }, @@ -280,10 +275,6 @@ } this.SetHeldPosition(this.order.data.x, this.order.data.z); - if (!this.order.data.max) - this.MoveToPoint(this.order.data.x, this.order.data.z); - else - this.MoveToPointRange(this.order.data.x, this.order.data.z, this.order.data.min, this.order.data.max); if (this.IsAnimal()) this.SetNextState("ANIMAL.WALKING"); else @@ -308,7 +299,6 @@ } this.SetHeldPosition(this.order.data.x, this.order.data.z); - this.MoveToPoint(this.order.data.x, this.order.data.z); if (this.IsAnimal()) this.SetNextState("ANIMAL.WALKING"); // WalkAndFight not applicable for animals else @@ -333,21 +323,18 @@ return; } - var ok = this.MoveToTarget(this.order.data.target); - if (ok) - { - // We've started walking to the given point - if (this.IsAnimal()) - this.SetNextState("ANIMAL.WALKING"); - else - this.SetNextState("INDIVIDUAL.WALKING"); - } - else + + if (this.CheckTargetRangeExplicit(this.order.data.target, 0, 0)) { // We are already at the target, or can't move at all - this.StopMoving(); this.FinishOrder(); + return true; } + + if (this.IsAnimal()) + this.SetNextState("ANIMAL.WALKING"); + else + this.SetNextState("INDIVIDUAL.WALKING"); }, "Order.PickupUnit": function(msg) { @@ -358,6 +345,12 @@ return; } + if (this.CheckTargetRangeExplicit(this.order.data.target, 0, 0)) + { + this.FinishOrder(); + return; + } + // Check if we need to move TODO implement a better way to know if we are on the shoreline var needToMove = true; var cmpPosition = Engine.QueryInterface(this.entity, IID_Position); @@ -369,18 +362,10 @@ needToMove = false; } - // TODO: what if the units are on a cliff ? the ship will go below the cliff - // and the units won't be able to garrison. Should go to the nearest (accessible) shore - if (needToMove && this.MoveToTarget(this.order.data.target)) - { + if (needToMove) this.SetNextState("INDIVIDUAL.PICKUP.APPROACHING"); - } else - { - // We are already at the target, or can't move at all - this.StopMoving(); this.SetNextState("INDIVIDUAL.PICKUP.LOADING"); - } }, "Order.Guard": function(msg) { @@ -390,30 +375,17 @@ return; } - if (this.MoveToTargetRangeExplicit(this.isGuardOf, 0, this.guardRange)) + if (!this.CheckTargetRangeExplicit(this.isGuardOf, 0, this.guardRange)) this.SetNextState("INDIVIDUAL.GUARD.ESCORTING"); else this.SetNextState("INDIVIDUAL.GUARD.GUARDING"); }, "Order.Flee": function(msg) { - // We use the distance between the entities to account for ranged attacks - var distance = DistanceBetweenEntities(this.entity, this.order.data.target) + (+this.template.FleeDistance); - var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); - if (cmpUnitMotion.MoveToTargetRange(this.order.data.target, distance, -1)) - { - // We've started fleeing from the given target - if (this.IsAnimal()) - this.SetNextState("ANIMAL.FLEEING"); - else - this.SetNextState("INDIVIDUAL.FLEEING"); - } + if (this.IsAnimal()) + this.SetNextState("ANIMAL.FLEEING"); else - { - // We are already at the target, or can't move at all - this.StopMoving(); - this.FinishOrder(); - } + this.SetNextState("INDIVIDUAL.FLEEING"); }, "Order.Attack": function(msg) { @@ -437,7 +409,6 @@ // If we are already at the target, try attacking it from here if (this.CheckTargetAttackRange(this.order.data.target, this.order.data.attackType)) { - this.StopMoving(); // For packable units within attack range: // 1. If unpacked, we can attack the target. // 2. If packed, we first need to unpack, then follow case 1. @@ -447,20 +418,10 @@ return; } - if (this.order.data.attackType == this.oldAttackType) - { - if (this.IsAnimal()) - this.SetNextState("ANIMAL.COMBAT.ATTACKING"); - else - this.SetNextState("INDIVIDUAL.COMBAT.ATTACKING"); - } + if (this.IsAnimal()) + this.SetNextState("ANIMAL.COMBAT.ATTACKING"); else - { - if (this.IsAnimal()) - this.SetNextState("ANIMAL.COMBAT.ATTACKING"); - else - this.SetNextState("INDIVIDUAL.COMBAT.ATTACKING"); - } + this.SetNextState("INDIVIDUAL.COMBAT.ATTACKING"); return; } @@ -481,20 +442,10 @@ return; } - // Try to move within attack range - if (this.MoveToTargetAttackRange(this.order.data.target, this.order.data.attackType)) - { - // We've started walking to the given point - if (this.IsAnimal()) - this.SetNextState("ANIMAL.COMBAT.APPROACHING"); - else - this.SetNextState("INDIVIDUAL.COMBAT.APPROACHING"); - return; - } - - // We can't reach the target, and can't move towards it, - // so abandon this attack order - this.FinishOrder(); + if (this.IsAnimal()) + this.SetNextState("ANIMAL.COMBAT.APPROACHING"); + else + this.SetNextState("INDIVIDUAL.COMBAT.APPROACHING"); }, "Order.Patrol": function(msg) { @@ -510,7 +461,6 @@ return; } - this.MoveToPoint(this.order.data.x, this.order.data.z); this.SetNextState("INDIVIDUAL.PATROL"); }, @@ -532,7 +482,6 @@ // Check if the target is in range if (this.CheckTargetRange(this.order.data.target, IID_Heal)) { - this.StopMoving(); this.SetNextState("INDIVIDUAL.HEAL.HEALING"); return; } @@ -545,17 +494,7 @@ return; } - // Try to move within heal range - if (this.MoveToTargetRange(this.order.data.target, IID_Heal)) - { - // We've started walking to the given point - this.SetNextState("INDIVIDUAL.HEAL.APPROACHING"); - return; - } - - // We can't reach the target, and can't move towards it, - // so abandon this heal order - this.FinishOrder(); + this.SetNextState("INDIVIDUAL.HEAL.APPROACHING"); }, "Order.Gather": function(msg) { @@ -591,25 +530,13 @@ return; } - // Try to move within range - if (this.MoveToTargetRange(this.order.data.target, IID_ResourceGatherer)) - { - // We've started walking to the given point - this.SetNextState("INDIVIDUAL.GATHER.APPROACHING"); - } - else - { - // We are already at the target, or can't move at all, - // so try gathering it from here. - // TODO: need better handling of the can't-reach-target case - this.StopMoving(); + if (this.CheckTargetRange(this.order.data.target, IID_ResourceGatherer)) this.SetNextState("INDIVIDUAL.GATHER.GATHERING"); - } + else + this.SetNextState("INDIVIDUAL.GATHER.APPROACHING"); }, "Order.GatherNearPosition": function(msg) { - // Move the unit to the position to gather from. - this.MoveToPoint(this.order.data.x, this.order.data.z); this.SetNextState("INDIVIDUAL.GATHER.WALKING"); }, @@ -633,20 +560,7 @@ return; } } - // Try to move to the dropsite - if (this.MoveToTargetRange(this.order.data.target, IID_ResourceGatherer)) - { - // We've started walking to the target - this.SetNextState("INDIVIDUAL.RETURNRESOURCE.APPROACHING"); - return; - } - // Oops, we can't reach the dropsite. - // Maybe we should try to pick another dropsite, to find an - // accessible one? - // For now, just give up. - this.StopMoving(); - this.FinishOrder(); - return; + this.SetNextState("INDIVIDUAL.RETURNRESOURCE.APPROACHING"); }, "Order.Trade": function(msg) { @@ -660,28 +574,15 @@ // TODO find the nearest way-point from our position, and start with it this.waypoints = undefined; - if (this.MoveToMarket(this.order.data.target)) - // We've started walking to the next market - this.SetNextState("TRADE.APPROACHINGMARKET"); - else - this.FinishOrder(); + this.SetNextState("TRADE.APPROACHINGMARKET"); }, "Order.Repair": function(msg) { // Try to move within range - if (this.MoveToTargetRange(this.order.data.target, IID_Builder)) - { - // We've started walking to the given point - this.SetNextState("INDIVIDUAL.REPAIR.APPROACHING"); - } - else - { - // We are already at the target, or can't move at all, - // so try repairing it from here. - // TODO: need better handling of the can't-reach-target case - this.StopMoving(); + if (this.CheckTargetRange(this.order.data.target, IID_Builder)) this.SetNextState("INDIVIDUAL.REPAIR.REPAIRING"); - } + else + this.SetNextState("INDIVIDUAL.REPAIR.APPROACHING"); }, "Order.Garrison": function(msg) { @@ -705,16 +606,7 @@ return; } - if (this.MoveToGarrisonRange(this.order.data.target)) - { - this.SetNextState("INDIVIDUAL.GARRISON.APPROACHING"); - } - else - { - // We do a range check before actually garrisoning - this.StopMoving(); - this.SetNextState("INDIVIDUAL.GARRISON.GARRISONED"); - } + this.SetNextState("INDIVIDUAL.GARRISON.APPROACHING"); }, "Order.Ungarrison": function() { @@ -728,18 +620,12 @@ "Order.Pack": function(msg) { if (this.CanPack()) - { - this.StopMoving(); this.SetNextState("INDIVIDUAL.PACKING"); - } }, "Order.Unpack": function(msg) { if (this.CanUnpack()) - { - this.StopMoving(); this.SetNextState("INDIVIDUAL.UNPACKING"); - } }, "Order.CancelPack": function(msg) { @@ -761,42 +647,36 @@ "Order.Walk": function(msg) { this.CallMemberFunction("SetHeldPosition", [msg.data.x, msg.data.z]); - - this.MoveToPoint(this.order.data.x, this.order.data.z); this.SetNextState("WALKING"); }, "Order.WalkAndFight": function(msg) { this.CallMemberFunction("SetHeldPosition", [msg.data.x, msg.data.z]); - - this.MoveToPoint(this.order.data.x, this.order.data.z); this.SetNextState("WALKINGANDFIGHTING"); }, "Order.MoveIntoFormation": function(msg) { this.CallMemberFunction("SetHeldPosition", [msg.data.x, msg.data.z]); - - this.MoveToPoint(this.order.data.x, this.order.data.z); this.SetNextState("FORMING"); }, // Only used by other orders to walk there in formation "Order.WalkToTargetRange": function(msg) { - if (this.MoveToTargetRangeExplicit(this.order.data.target, this.order.data.min, this.order.data.max)) + if (!this.CheckTargetRangeExplicit(this.order.data.target, this.order.data.min, this.order.data.max)) this.SetNextState("WALKING"); else this.FinishOrder(); }, "Order.WalkToTarget": function(msg) { - if (this.MoveToTarget(this.order.data.target)) + if (!this.CheckTargetRangeExplicit(this.order.data.target, 0, 0)) this.SetNextState("WALKING"); else this.FinishOrder(); }, "Order.WalkToPointRange": function(msg) { - if (this.MoveToPointRange(this.order.data.x, this.order.data.z, this.order.data.min, this.order.data.max)) + if (!this.CheckPointRangeExplicit(this.order.data.x, this.order.data.z, this.order.data.min, this.order.data.max)) this.SetNextState("WALKING"); else this.FinishOrder(); @@ -804,8 +684,6 @@ "Order.Patrol": function(msg) { this.CallMemberFunction("SetHeldPosition", [msg.data.x, msg.data.z]); - - this.MoveToPoint(this.order.data.x, this.order.data.z); this.SetNextState("PATROL"); }, @@ -835,11 +713,8 @@ { if (this.TargetIsAlive(target) && this.CheckTargetVisible(target)) { - if (this.MoveToTargetAttackRange(target, target)) - { - this.SetNextState("COMBAT.APPROACHING"); - return; - } + this.SetNextState("COMBAT.APPROACHING"); + return; } this.FinishOrder(); return; @@ -867,12 +742,8 @@ } else { - // Out of range; move there in formation - if (this.MoveToGarrisonRange(msg.data.target)) - { - this.SetNextState("GARRISON.APPROACHING"); - return; - } + this.SetNextState("GARRISON.APPROACHING"); + return; } } @@ -1021,6 +892,18 @@ }, "WALKING": { + "enter": function() { + if (!this.MoveTo(this.order.data)) + { + this.FinishOrder(); + return true; + } + }, + + "leave": function() { + this.StopMoving(); + }, + "MoveStarted": function(msg) { var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation); cmpFormation.SetRearrange(true); @@ -1035,9 +918,18 @@ "WALKINGANDFIGHTING": { "enter": function(msg) { + if (!this.MoveTo(this.order.data)) + { + this.FinishOrder(); + return true; + } this.StartTimer(0, 1000); }, + "leave": function() { + this.StopMoving(); + }, + "Timer": function(msg) { // check if there are no enemies to attack this.FindWalkAndFightTargets(); @@ -1075,6 +967,11 @@ this.patrolStartPosOrder.allowCapture = this.order.data.allowCapture; } + if (!this.MoveTo(this.order.data)) + { + this.FinishOrder(); + return true; + } this.StartTimer(0, 1000); }, @@ -1085,6 +982,7 @@ "leave": function(msg) { this.StopTimer(); + this.StopMoving(); delete this.patrolStartPosOrder; }, @@ -1135,6 +1033,18 @@ "APPROACHING": { + "enter": function() { + if (!this.MoveToGarrisonRange(this.order.data.target)) + { + this.FinishOrder(); + return true; + } + }, + + "leave": function() { + this.StopMoving(); + }, + "MoveStarted": function(msg) { var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation); cmpFormation.SetRearrange(true); @@ -1161,6 +1071,18 @@ }, "FORMING": { + "enter": function() { + if (!this.MoveTo(this.order.data)) + { + this.FinishOrder(); + return true; + } + }, + + "leave": function() { + this.StopMoving(); + }, + "MoveStarted": function(msg) { var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation); cmpFormation.SetRearrange(true); @@ -1182,6 +1104,18 @@ "COMBAT": { "APPROACHING": { + "enter": function() { + if (!this.MoveTo(this.order.data)) + { + this.FinishOrder(); + return true; + } + }, + + "leave": function() { + this.StopMoving(); + }, + "MoveStarted": function(msg) { var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation); cmpFormation.SetRearrange(true); @@ -1324,15 +1258,15 @@ } // Move a tile outside the building let range = 4; - if (this.MoveToTargetRangeExplicit(msg.data.target, range, range)) + if (this.CheckTargetRangeExplicit(msg.data.target, range, range)) { - // We've started walking to the given point - this.SetNextState("WALKINGTOPOINT"); + // We are already at the target, or can't move at all + this.FinishOrder(); } else { - // We are already at the target, or can't move at all - this.FinishOrder(); + this.order.data.min = range; + this.SetNextState("WALKINGTOPOINT"); } }, @@ -1349,6 +1283,9 @@ "WALKING": { "enter": function() { + var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); + cmpUnitMotion.MoveToFormationOffset(this.order.data.target, this.order.data.x, this.order.data.z); + var cmpFormation = Engine.QueryInterface(this.formationController, IID_Formation); var cmpVisual = Engine.QueryInterface(this.entity, IID_Visual); if (cmpFormation && cmpVisual) @@ -1384,10 +1321,16 @@ var cmpFormation = Engine.QueryInterface(this.formationController, IID_Formation); if (cmpFormation) cmpFormation.UnsetInPosition(this.entity); + if (!this.MoveTo(this.order.data)) + { + this.FinishOrder(); + return true; + } this.SelectAnimation("move"); }, "MoveCompleted": function() { + this.StopMoving(); this.FinishOrder(); }, }, @@ -1560,9 +1503,19 @@ "WALKING": { "enter": function() { + if (!this.MoveTo(this.order.data)) + { + this.FinishOrder(); + return; + } this.SelectAnimation("move"); }, + "leave": function () { + this.SelectAnimation("idle"); + this.StopMoving(); + }, + "MoveCompleted": function() { this.FinishOrder(); }, @@ -1570,6 +1523,11 @@ "WALKINGANDFIGHTING": { "enter": function() { + if (!this.MoveTo(this.order.data)) + { + this.FinishOrder(); + return; + } // Show weapons rather than carried resources. this.SetAnimationVariant("combat"); @@ -1582,6 +1540,7 @@ }, "leave": function(msg) { + this.StopMoving(); this.StopTimer(); this.SetDefaultAnimationVariant(); }, @@ -1595,11 +1554,13 @@ "enter": function() { // Memorize the origin position in case that we want to go back let cmpPosition = Engine.QueryInterface(this.entity, IID_Position); - if (!cmpPosition || !cmpPosition.IsInWorld()) + if (!cmpPosition || !cmpPosition.IsInWorld() || + !this.MoveTo(this.order.data)) { this.FinishOrder(); return; } + if (!this.patrolStartPosOrder) { this.patrolStartPosOrder = cmpPosition.GetPosition(); @@ -1613,6 +1574,7 @@ }, "leave": function() { + this.StopMoving(); this.StopTimer(); delete this.patrolStartPosOrder; this.SetDefaultAnimationVariant(); @@ -1639,6 +1601,12 @@ "ESCORTING": { "enter": function() { + if (!this.MoveToTargetRangeExplicit(this.isGuardOf, 0, this.guardRange)) + { + this.FinishOrder(); + return; + } + // Show weapons rather than carried resources. this.SetAnimationVariant("combat"); @@ -1652,7 +1620,6 @@ // Check the target is alive if (!this.TargetIsAlive(this.isGuardOf)) { - this.StopMoving(); this.FinishOrder(); return; } @@ -1660,6 +1627,7 @@ }, "leave": function(msg) { + this.StopMoving(); this.ResetSpeedMultiplier(); this.StopTimer(); this.SetDefaultAnimationVariant(); @@ -1682,13 +1650,14 @@ "MoveCompleted": function() { this.ResetSpeedMultiplier(); - if (!this.MoveToTargetRangeExplicit(this.isGuardOf, 0, this.guardRange)) + if (this.CheckTargetRangeExplicit(this.isGuardOf, 0, this.guardRange)) this.SetNextState("GUARDING"); }, }, "GUARDING": { "enter": function() { + this.StopMoving(); this.StartTimer(1000, 1000); this.SetHeldPositionOnEntity(this.entity); this.SetAnimationVariant("combat"); @@ -1709,8 +1678,10 @@ this.FinishOrder(); return; } - // then check is the target has moved - if (this.MoveToTargetRangeExplicit(this.isGuardOf, 0, this.guardRange)) + // Then check is the target has moved and try following it. + // TODO: find out what to do if we cannot move. + if (!this.CheckTargetRangeExplicit(this.isGuardOf, 0, this.guardRange) && + this.MoveToTargetRangeExplicit(this.isGuardOf, 0, this.guardRange)) this.SetNextState("ESCORTING"); else { @@ -1735,6 +1706,17 @@ "FLEEING": { "enter": function() { + // We use the distance between the entities to account for ranged attacks + let distance = DistanceBetweenEntities(this.entity, this.order.data.target) + (+this.template.FleeDistance); + let cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); + // Use unit motion directly to ignore the visibility check. TODO: change this if we add LOS to fauna. + if (this.CheckTargetRangeExplicit(this.order.data.target, distance, distance) || + !cmpUnitMotion || !cmpUnitMotion.MoveToTargetRange(this.order.data.target, distance, -1)) + { + this.FinishOrder(); + return; + } + this.PlaySound("panic"); // Run quickly @@ -1748,6 +1730,7 @@ "leave": function() { this.ResetSpeedMultiplier(); + this.StopMoving(); }, "MoveCompleted": function() { @@ -1773,6 +1756,12 @@ "APPROACHING": { "enter": function() { + if (!this.MoveToTargetAttackRange(this.order.data.target, this.order.data.attackType)) + { + this.FinishOrder(); + return; + } + // Show weapons rather than carried resources. this.SetAnimationVariant("combat"); @@ -1783,7 +1772,7 @@ "leave": function() { // Show carried resources when walking. this.SetDefaultAnimationVariant(); - + this.StopMoving(); this.StopTimer(); }, @@ -1857,6 +1846,8 @@ } } + this.StopMoving(); + var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack); this.attackTimers = cmpAttack.GetTimers(this.order.data.attackType); @@ -2023,6 +2014,12 @@ "CHASING": { "enter": function() { + if (!this.MoveTo(this.order.data)) + { + this.FinishOrder(); + return; + } + // Show weapons rather than carried resources. this.SetAnimationVariant("combat"); @@ -2041,7 +2038,7 @@ // Show carried resources when walking. this.SetDefaultAnimationVariant(); - + this.StopMoving(); this.StopTimer(); }, @@ -2066,8 +2063,6 @@ "GATHER": { "APPROACHING": { "enter": function() { - this.SelectAnimation("move"); - this.gatheringTarget = this.order.data.target; // temporary, deleted in "leave". // check that we can gather from the resource we're supposed to gather from. @@ -2126,6 +2121,16 @@ } return true; } + + // We can gather from out target, so try to move there. + if (!this.MoveTo(this.order.data, IID_ResourceGatherer)) + { + this.FinishOrder(); + return true; + } + + this.SelectAnimation("move"); + return false; }, @@ -2178,22 +2183,35 @@ }, "leave": function() { + this.StopMoving(); + + this.SetDefaultAnimationVariant(); + if (!this.gatheringTarget) + return; // don't use ownership because this is called after a conversion/resignation // and the ownership would be invalid then. var cmpSupply = Engine.QueryInterface(this.gatheringTarget, IID_ResourceSupply); if (cmpSupply) cmpSupply.RemoveGatherer(this.entity); delete this.gatheringTarget; - this.SetDefaultAnimationVariant(); }, }, // Walking to a good place to gather resources near, used by GatherNearPosition "WALKING": { "enter": function() { + if (!this.MoveTo(this.order.data)) + { + this.FinishOrder(); + return; + } this.SelectAnimation("move"); }, + "leave": function() { + this.StopMoving(); + }, + "MoveCompleted": function(msg) { var resourceType = this.order.data.type; var resourceTemplate = this.order.data.template; @@ -2288,6 +2306,7 @@ // off to a different target.) if (this.CheckTargetRange(this.gatheringTarget, IID_ResourceGatherer)) { + this.StopMoving(); this.SetDefaultAnimationVariant(); var typename = "gather_" + this.order.data.type.specific; this.SelectAnimation(typename); @@ -2378,8 +2397,7 @@ var maxRange = 8; // get close but not too close if (this.order.data.lastPos && - this.MoveToPointRange(this.order.data.lastPos.x, this.order.data.lastPos.z, - 0, maxRange)) + this.MoveToPointRange(this.order.data.lastPos.x, this.order.data.lastPos.z, 0, maxRange)) { this.SetNextState("APPROACHING"); return; @@ -2454,22 +2472,32 @@ "APPROACHING": { "enter": function() { + if (this.CheckTargetRange(this.order.data.target, IID_Heal)) + { + this.SetNextState("HEALING"); + return true; + } + + if (!this.MoveTo(this.order.data, IID_Heal)) + { + this.FinishOrder(); + return true; + } + this.SelectAnimation("move"); this.StartTimer(1000, 1000); }, "leave": function() { + this.StopMoving(); this.StopTimer(); }, "Timer": function(msg) { if (this.ShouldAbandonChase(this.order.data.target, this.order.data.force, IID_Heal, null)) { - this.StopMoving(); - this.FinishOrder(); - - // Return to our original position - if (this.GetStance().respondHoldGround) + // Return to our original position unless we have a better order. + if (!this.FinishOrder() && this.GetStance().respondHoldGround) this.WalkToHeldPosition(); } }, @@ -2494,6 +2522,8 @@ prepare = Math.max(prepare, repeatLeft); } + this.StopMoving(); + this.SelectAnimation("heal"); this.SetAnimationSync(prepare, this.healTimers.repeat); this.StartTimer(prepare, this.healTimers.repeat); @@ -2538,11 +2568,7 @@ this.PushOrderFront("Pack", { "force": true }); return; } - if (this.MoveToTargetRange(target, IID_Heal)) - { - this.SetNextState("HEAL.CHASING"); - return; - } + this.SetNextState("HEAL.APPROACHING"); } } // Can't reach it, healed to max hp or doesn't exist any more - give up @@ -2558,44 +2584,27 @@ this.WalkToHeldPosition(); }, }, - "CHASING": { - "enter": function() { - this.SelectAnimation("move"); - this.StartTimer(1000, 1000); - }, - - "leave": function() { - this.StopTimer(); - }, - "Timer": function(msg) { - if (this.ShouldAbandonChase(this.order.data.target, this.order.data.force, IID_Heal, null)) - { - this.StopMoving(); - this.FinishOrder(); - - // Return to our original position - if (this.GetStance().respondHoldGround) - this.WalkToHeldPosition(); - } - }, - "MoveCompleted": function() { - this.SetNextState("HEALING"); - }, - }, }, // Returning to dropsite "RETURNRESOURCE": { "APPROACHING": { "enter": function() { + if (!this.MoveTo(this.order.data)) + { + this.FinishOrder(); + return true; + } this.SelectAnimation("move"); }, - "MoveCompleted": function() { + "leave": function() { // Switch back to idle animation to guarantee we won't // get stuck with the carry animation after stopping moving this.SelectAnimation("idle"); + }, + "MoveCompleted": function() { // Check the dropsite is in range and we can return our resource there // (we didn't get stopped before reaching it) if (this.CheckTargetRange(this.order.data.target, IID_ResourceGatherer) && this.CanReturnResource(this.order.data.target, true)) @@ -2646,9 +2655,18 @@ "APPROACHINGMARKET": { "enter": function() { + if (!this.MoveToMarket(this.order.data.target)) + { + this.FinishOrder(); + return; + } this.SelectAnimation("move"); }, + "leave": function() { + this.StopMoving(); + }, + "MoveCompleted": function() { if (this.waypoints && this.waypoints.length) { @@ -2674,9 +2692,18 @@ "REPAIR": { "APPROACHING": { "enter": function() { + if (!this.MoveTo(this.order.data)) + { + this.FinishOrder(); + return true; + } this.SelectAnimation("move"); }, + "leave": function() { + this.StopMoving(); + }, + "MoveCompleted": function() { this.SetNextState("REPAIRING"); }, @@ -2718,6 +2745,8 @@ return true; } + this.StopMoving(); + let cmpBuilderList = QueryBuilderListInterface(this.repairTarget); if (cmpBuilderList) cmpBuilderList.AddBuilder(this.entity); @@ -2751,9 +2780,10 @@ // in that case, the repairTarget is deleted, and we can just return if (!this.repairTarget) return; - if (this.MoveToTargetRange(this.repairTarget, IID_Builder)) + let inRange = this.CheckTargetRange(this.repairTarget, IID_Builder); + if (!inRange && this.MoveToTargetRange(this.repairTarget, IID_Builder)) this.SetNextState("APPROACHING"); - else if (!this.CheckTargetRange(this.repairTarget, IID_Builder)) + else if (!inRange) this.FinishOrder(); //can't approach and isn't in reach }, }, @@ -2871,9 +2901,18 @@ "APPROACHING": { "enter": function() { + if (!this.MoveTo(this.order.data)) + { + this.FinishOrder(); + return true; + } this.SelectAnimation("move"); }, + "leave": function() { + this.StopMoving(); + }, + "MoveCompleted": function() { this.SetNextState("GARRISONED"); }, @@ -2964,7 +3003,7 @@ } } - if (this.MoveToTarget(target)) + if (!this.CheckTargetRangeExplicit(target, 0, 0) && this.MoveToTarget(target)) { this.SetNextState("APPROACHING"); return false; @@ -3004,6 +3043,8 @@ "PACKING": { "enter": function() { + this.StopMoving(); + var cmpPack = Engine.QueryInterface(this.entity, IID_Pack); cmpPack.Pack(); }, @@ -3022,6 +3063,8 @@ "UNPACKING": { "enter": function() { + this.StopMoving(); + var cmpPack = Engine.QueryInterface(this.entity, IID_Pack); cmpPack.Unpack(); }, @@ -3041,9 +3084,18 @@ "PICKUP": { "APPROACHING": { "enter": function() { + if (!this.MoveTo(this.order.data)) + { + this.FinishOrder(); + return true; + } this.SelectAnimation("move"); }, + "leave": function() { + this.StopMoving(); + }, + "MoveCompleted": function() { this.SetNextState("LOADING"); }, @@ -3056,6 +3108,7 @@ "LOADING": { "enter": function() { + this.StopMoving(); this.SelectAnimation("idle"); var cmpGarrisonHolder = Engine.QueryInterface(this.entity, IID_GarrisonHolder); if (!cmpGarrisonHolder || cmpGarrisonHolder.IsFull()) @@ -3095,15 +3148,15 @@ "Order.LeaveFoundation": function(msg) { // Move a tile outside the building var range = 4; - if (this.MoveToTargetRangeExplicit(msg.data.target, range, range)) + if (this.CheckTargetRangeExplicit(msg.data.target, range, range)) { - // We've started walking to the given point - this.SetNextState("WALKING"); + this.FinishOrder(); + return; } else { - // We are already at the target, or can't move at all - this.FinishOrder(); + this.order.data.min = range; + this.SetNextState("WALKING"); } }, @@ -3128,6 +3181,7 @@ }, "leave": function() { + this.StopMoving(); this.StopTimer(); this.SetFacePointAfterMove(true); }, @@ -4234,6 +4288,35 @@ cmpUnitMotion.StopMoving(); }; +/** + * Generic dispatcher for other MoveTo functions. + * @param iid - Interface ID (optional) implementing GetRange + * @param type - Range type for the interface call + * @returns whether the move succeeded or failed. + */ +UnitAI.prototype.MoveTo = function(data, iid, type) +{ + if (data["target"]) + { + if (data["min"] || data["max"]) + return this.MoveToTargetRangeExplicit(data.target, data.min || -1, data.max || -1); + else + { + if (!iid) + return this.MoveToTarget(data.target); + else + return this.MoveToTargetRange(data.target, iid, type); + } + } + else + { + if (data["min"] || data["max"]) + return this.MoveToPointRange(data.x, data.z, data.min || -1, data.max || -1); + else + return this.MoveToPoint(data.x, data.z); + } +} + UnitAI.prototype.MoveToPoint = function(x, z) { var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); Index: ps/trunk/binaries/data/mods/public/simulation/components/tests/test_UnitAI.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/components/tests/test_UnitAI.js +++ ps/trunk/binaries/data/mods/public/simulation/components/tests/test_UnitAI.js @@ -11,6 +11,7 @@ Engine.LoadComponentScript("interfaces/Health.js"); Engine.LoadComponentScript("interfaces/Pack.js"); Engine.LoadComponentScript("interfaces/ResourceSupply.js"); +Engine.LoadComponentScript("interfaces/ResourceGatherer.js"); Engine.LoadComponentScript("interfaces/Timer.js"); Engine.LoadComponentScript("interfaces/UnitAI.js"); Engine.LoadComponentScript("Formation.js"); @@ -86,7 +87,7 @@ GetWalkSpeed: function() { return 1; }, MoveToFormationOffset: function(target, x, z) { }, IsInTargetRange: function(target, min, max) { return true; }, - MoveToTargetRange: function(target, min, max) { }, + MoveToTargetRange: function(target, min, max) { return true; }, StopMoving: function() { }, GetPassabilityClassName: function() { return "default"; }, }); @@ -137,10 +138,11 @@ }); AddMock(controller, IID_UnitMotion, { - GetWalkSpeed: function() { return 1; }, - SetSpeedMultiplier: function(speed) { }, - MoveToPointRange: function(x, z, minRange, maxRange) { }, - GetPassabilityClassName: function() { return "default"; }, + "GetWalkSpeed": () => 1, + "StopMoving": () => {}, + "SetSpeedMultiplier": () => {}, + "MoveToPointRange": () => true, + "GetPassabilityClassName": () => "default" }); controllerAI.OnCreate(); @@ -238,7 +240,7 @@ GetWalkSpeed: function() { return 1; }, MoveToFormationOffset: function(target, x, z) { }, IsInTargetRange: function(target, min, max) { return true; }, - MoveToTargetRange: function(target, min, max) { }, + MoveToTargetRange: function(target, min, max) { return true; }, StopMoving: function() { }, GetPassabilityClassName: function() { return "default"; }, }); Index: ps/trunk/source/simulation2/components/CCmpUnitMotion.cpp =================================================================== --- ps/trunk/source/simulation2/components/CCmpUnitMotion.cpp +++ ps/trunk/source/simulation2/components/CCmpUnitMotion.cpp @@ -1051,25 +1051,25 @@ m_Moving = false; CMessageMotionChanged msg(false, false); GetSimContext().GetComponentManager().PostMessage(GetEntityId(), msg); + return; } } 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)) - return; - - // Not in formation, so just finish moving - StopMoving(); - m_State = STATE_IDLE; - MoveSucceeded(); - - if (m_FacePointAfterMove) - FaceTowardsPointFromPos(pos, m_FinalGoal.x, m_FinalGoal.z); - // TODO: if the goal was a square building, we ought to point towards the - // nearest point on the square, not towards its center + if (!cmpUnitMotion || cmpUnitMotion->IsInTargetRange(GetEntityId(), m_TargetMinRange, m_TargetMaxRange)) + { + // Not in formation, so just finish moving + StopMoving(); + m_State = STATE_IDLE; + MoveSucceeded(); + + if (m_FacePointAfterMove) + FaceTowardsPointFromPos(pos, m_FinalGoal.x, m_FinalGoal.z); + // TODO: if the goal was a square building, we ought to point towards the + // nearest point on the square, not towards its center + } } } @@ -1454,7 +1454,7 @@ // We're already in range - no need to move anywhere if (m_FacePointAfterMove) FaceTowardsPointFromPos(pos, x, z); - return false; + return true; } } @@ -1623,7 +1623,7 @@ { // We're already in range - no need to move anywhere FaceTowardsPointFromPos(pos, goal.x, goal.z); - return false; + return true; } else { Index: ps/trunk/source/simulation2/components/ICmpUnitMotion.h =================================================================== --- ps/trunk/source/simulation2/components/ICmpUnitMotion.h +++ ps/trunk/source/simulation2/components/ICmpUnitMotion.h @@ -38,10 +38,8 @@ /** * Attempt to walk into range of a to a given point, or as close as possible. * The range is measured from the center of the unit. - * If the unit is already in range, or cannot move anywhere at all, or if there is - * some other error, then returns false. - * Otherwise, returns true and sends a MotionChanged message after starting to move, - * and sends another MotionChanged after finishing moving. + * If cannot move anywhere at all, or if there is some other error, then returns false. + * Otherwise, returns true. * If maxRange is negative, then the maximum range is treated as infinity. */ virtual bool MoveToPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t minRange, entity_pos_t maxRange) = 0; @@ -62,10 +60,8 @@ * Attempt to walk into range of a given target entity, or as close as possible. * The range is measured between approximately the edges of the unit and the target, so that * maxRange=0 is not unreachably close to the target. - * If the unit is already in range, or cannot move anywhere at all, or if there is - * some other error, then returns false. - * Otherwise, returns true and sends a MotionChanged message after starting to move, - * and sends another MotionChanged after finishing moving. + * If the unit cannot move anywhere at all, or if there is some other error, then returns false. + * Otherwise, returns true. * If maxRange is negative, then the maximum range is treated as infinity. */ virtual bool MoveToTargetRange(entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange) = 0;