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 @@ -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"); }, @@ -228,15 +225,12 @@ // Move a tile outside the building if necessary. let range = 4; - if (!this.CheckTargetRangeExplicit(msg.data.target, range, range) && - this.MoveToTargetRangeExplicit(msg.data.target, range, range)) - { - this.SetNextState("INDIVIDUAL.WALKING"); - } - else - { - // We are already at the target, or can't move at all + if (this.CheckTargetRangeExplicit(msg.data.target, range, -1)) this.FinishOrder(); + else + { + this.order.data.min = range; + this.SetNextState("INDIVIDUAL.WALKING"); } }, @@ -281,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 @@ -309,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 @@ -334,21 +323,18 @@ return; } - let ok = !this.CheckTargetRangeExplicit(this.order.data.target, 0, 0) && 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) { @@ -359,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); @@ -370,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.CheckTargetRangeExplicit(this.order.data.target, 0, 0) && 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) { @@ -391,8 +375,7 @@ return; } - if (!this.CheckTargetRangeExplicit(this.isGuardOf, 0, this.guardRange) && - 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"); @@ -401,21 +384,16 @@ "Order.Flee": function(msg) { // We use the distance between the entities to account for ranged attacks let distance = DistanceBetweenEntities(this.entity, this.order.data.target) + (+this.template.FleeDistance); - if (!this.CheckTargetRangeExplicit(this.order.data.target, distance, distance) && - this.MoveToTargetRangeExplicit(this.order.data.target, distance, distance)) + if (this.CheckTargetRangeExplicit(this.order.data.target, distance, distance)) { - // We've started fleeing from the given target - if (this.IsAnimal()) - this.SetNextState("ANIMAL.FLEEING"); - else - this.SetNextState("INDIVIDUAL.FLEEING"); - } - else - { - // We are already at the target, or can't move at all - this.StopMoving(); this.FinishOrder(); + return; } + + if (this.IsAnimal()) + this.SetNextState("ANIMAL.FLEEING"); + else + this.SetNextState("INDIVIDUAL.FLEEING"); }, "Order.Attack": function(msg) { @@ -439,7 +417,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. @@ -449,20 +426,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; } @@ -483,21 +450,10 @@ return; } - // Try to move within attack range - if (!this.CheckTargetAttackRange(this.order.data.target, this.order.data.attackType) && - 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) { @@ -513,7 +469,6 @@ return; } - this.MoveToPoint(this.order.data.x, this.order.data.z); this.SetNextState("INDIVIDUAL.PATROL"); }, @@ -535,7 +490,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; } @@ -548,18 +502,7 @@ return; } - // Try to move within heal range - if (!this.CheckTargetRange(this.order.data.target, IID_Heal) && - 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) { @@ -595,64 +538,18 @@ return; } - // Try to move within range - if (this.CheckTargetRange(this.order.data.target, IID_ResourceGatherer) && - !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"); }, "Order.ReturnResource": function(msg) { - // Check if the dropsite is already in range - if (this.CheckTargetRange(this.order.data.target, IID_ResourceGatherer) && this.CanReturnResource(this.order.data.target, true)) - { - var cmpResourceDropsite = Engine.QueryInterface(this.order.data.target, IID_ResourceDropsite); - if (cmpResourceDropsite) - { - // Dump any resources we can - var dropsiteTypes = cmpResourceDropsite.GetTypes(); - - Engine.QueryInterface(this.entity, IID_ResourceGatherer).CommitResources(dropsiteTypes); - // Stop showing the carried resource animation. - this.SetDefaultAnimationVariant(); - - // Our next order should always be a Gather, - // so just switch back to that order - this.FinishOrder(); - return; - } - } - // Try to move to the dropsite - if (!this.CheckTargetRange(this.order.data.target, IID_ResourceGatherer) && - 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) { @@ -666,29 +563,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.CheckTargetRange(this.order.data.target, IID_Builder) && - 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) { @@ -712,21 +595,11 @@ 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() { this.FinishOrder(); - this.isGarrisoned = false; }, "Order.Cheering": function(msg) { @@ -735,31 +608,19 @@ "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) { - var cmpPack = Engine.QueryInterface(this.entity, IID_Pack); - if (cmpPack && cmpPack.IsPacking() && !cmpPack.IsPacked()) - cmpPack.CancelPack(); this.FinishOrder(); }, "Order.CancelUnpack": function(msg) { - var cmpPack = Engine.QueryInterface(this.entity, IID_Pack); - if (cmpPack && cmpPack.IsPacking() && cmpPack.IsPacked()) - cmpPack.CancelPack(); this.FinishOrder(); }, @@ -768,45 +629,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.CheckTargetRangeExplicit(this.order.data.target, this.order.data.min, this.order.data.max) && - 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.CheckTargetRange(this.order.data.target) && - 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.CheckPointRangeExplicit(this.order.data.x, this.order.data.z, this.order.data.min, this.order.data.max) && - 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(); @@ -814,8 +666,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"); }, @@ -845,11 +695,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; @@ -877,12 +724,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; } } @@ -1031,6 +874,14 @@ }, "WALKING": { + "enter": function() { + this.MoveTo(this.order.data); + }, + + "leave": function() { + this.StopMoving(); + }, + "MoveStarted": function(msg) { var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation); cmpFormation.SetRearrange(true); @@ -1045,9 +896,14 @@ "WALKINGANDFIGHTING": { "enter": function(msg) { + this.MoveTo(this.order.data); this.StartTimer(0, 1000); }, + "leave": function() { + this.StopMoving(); + }, + "Timer": function(msg) { // check if there are no enemies to attack this.FindWalkAndFightTargets(); @@ -1085,6 +941,7 @@ this.patrolStartPosOrder.allowCapture = this.order.data.allowCapture; } + this.MoveTo(this.order.data); this.StartTimer(0, 1000); }, @@ -1095,6 +952,7 @@ "leave": function(msg) { this.StopTimer(); + this.StopMoving(); delete this.patrolStartPosOrder; }, @@ -1145,6 +1003,14 @@ "APPROACHING": { + "enter": function() { + this.MoveToGarrisonRange(this.order.data.target); + }, + + "leave": function() { + this.StopMoving(); + }, + "MoveStarted": function(msg) { var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation); cmpFormation.SetRearrange(true); @@ -1171,6 +1037,14 @@ }, "FORMING": { + "enter": function() { + this.MoveTo(this.order.data); + }, + + "leave": function() { + this.StopMoving(); + }, + "MoveStarted": function(msg) { var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation); cmpFormation.SetRearrange(true); @@ -1192,6 +1066,14 @@ "COMBAT": { "APPROACHING": { + "enter": function() { + this.MoveTo(this.order.data); + }, + + "leave": function() { + this.StopMoving(); + }, + "MoveStarted": function(msg) { var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation); cmpFormation.SetRearrange(true); @@ -1334,17 +1216,16 @@ } // Move a tile outside the building let range = 4; - if (!this.CheckTargetRangeExplicit(msg.data.target, range, range) && - this.MoveToTargetRangeExplicit(msg.data.target, range, range)) + if (this.CheckTargetRangeExplicit(msg.data.target, range, range)) { - // We've started walking to the given point + // We are already at the target, or can't move at all + this.FinishOrder(); + } + else + { + this.order.data.min = range; this.SetNextState("WALKINGTOPOINT"); } - else - { - // We are already at the target, or can't move at all - this.FinishOrder(); - } }, @@ -1360,6 +1241,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) @@ -1395,10 +1279,12 @@ var cmpFormation = Engine.QueryInterface(this.formationController, IID_Formation); if (cmpFormation) cmpFormation.UnsetInPosition(this.entity); + this.MoveTo(this.order.data); this.SelectAnimation("move"); }, "MoveCompleted": function() { + this.StopMoving(); this.FinishOrder(); }, }, @@ -1563,6 +1449,9 @@ "Timer": function(msg) { if (!this.isIdle) { + // Sanity stop + this.StopMoving(); + this.isIdle = true; Engine.PostMessage(this.entity, MT_UnitIdleChanged, { "idle": this.isIdle }); } @@ -1571,9 +1460,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(); }, @@ -1581,6 +1480,11 @@ "WALKINGANDFIGHTING": { "enter": function() { + if (!this.MoveTo(this.order.data)) + { + this.FinishOrder(); + return; + } // Show weapons rather than carried resources. this.SetAnimationVariant("combat"); @@ -1593,6 +1497,7 @@ }, "leave": function(msg) { + this.StopMoving(); this.StopTimer(); this.SetDefaultAnimationVariant(); }, @@ -1606,11 +1511,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(); @@ -1624,6 +1531,7 @@ }, "leave": function() { + this.StopMoving(); this.StopTimer(); delete this.patrolStartPosOrder; this.SetDefaultAnimationVariant(); @@ -1650,6 +1558,12 @@ "ESCORTING": { "enter": function() { + if (!this.MoveTo(this.order.data)) + { + this.FinishOrder(); + return; + } + // Show weapons rather than carried resources. this.SetAnimationVariant("combat"); @@ -1663,7 +1577,6 @@ // Check the target is alive if (!this.TargetIsAlive(this.isGuardOf)) { - this.StopMoving(); this.FinishOrder(); return; } @@ -1671,6 +1584,7 @@ }, "leave": function(msg) { + this.StopMoving(); this.ResetMoveSpeed(); this.StopTimer(); this.SetDefaultAnimationVariant(); @@ -1700,6 +1614,7 @@ "GUARDING": { "enter": function() { + this.StopMoving(); this.StartTimer(1000, 1000); this.SetHeldPositionOnEntity(this.entity); this.SetAnimationVariant("combat"); @@ -1747,6 +1662,12 @@ "FLEEING": { "enter": function() { + if (!this.MoveTo(this.order.data)) + { + this.FinishOrder(); + return; + } + this.PlaySound("panic"); // Run quickly @@ -1760,6 +1681,7 @@ "leave": function() { this.ResetMoveSpeed(); + this.StopMoving(); }, "MoveCompleted": function() { @@ -1785,6 +1707,12 @@ "APPROACHING": { "enter": function() { + if (!this.MoveTo(this.order.data)) + { + this.FinishOrder(); + return; + } + // Show weapons rather than carried resources. this.SetAnimationVariant("combat"); @@ -1795,7 +1723,7 @@ "leave": function() { // Show carried resources when walking. this.SetDefaultAnimationVariant(); - + this.StopMoving(); this.StopTimer(); }, @@ -1869,6 +1797,8 @@ } } + this.StopMoving(); + var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack); this.attackTimers = cmpAttack.GetTimers(this.order.data.attackType); @@ -2035,6 +1965,12 @@ "CHASING": { "enter": function() { + if (!this.MoveTo(this.order.data)) + { + this.FinishOrder(); + return; + } + // Show weapons rather than carried resources. this.SetAnimationVariant("combat"); @@ -2051,7 +1987,7 @@ "leave": function() { // Show carried resources when walking. this.SetDefaultAnimationVariant(); - + this.StopMoving(); this.StopTimer(); }, @@ -2076,8 +2012,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. @@ -2136,6 +2070,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; }, @@ -2188,22 +2132,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; @@ -2298,6 +2255,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); @@ -2464,22 +2422,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(); } }, @@ -2504,6 +2472,8 @@ prepare = Math.max(prepare, repeatLeft); } + this.StopMoving(); + this.SelectAnimation("heal"); this.SetAnimationSync(prepare, this.healTimers.repeat); this.StartTimer(prepare, this.healTimers.repeat); @@ -2548,11 +2518,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 @@ -2567,79 +2533,80 @@ if (this.GetStance().respondHoldGround) 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.CheckTargetRange(this.order.data.target, IID_ResourceGatherer)) + { + this.SetNextState("DROPPING"); + return true; + } + + if (!this.MoveTo(this.order.data, IID_ResourceGatherer)) + { + this.SetNextState("LOOKINGFORDROPSITE"); + return; + } + this.SelectAnimation("move"); }, - "MoveCompleted": function() { - // Switch back to idle animation to guarantee we won't - // get stuck with the carry animation after stopping moving + "leave": function() { + this.StopMoving(); this.SelectAnimation("idle"); + }, - // 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)) + "MoveCompleted": function() { + this.SetNextState("DROPPING"); + }, + }, + + "DROPPING": { + "enter": function() { + if (!this.CheckTargetRange(this.order.data.target, IID_ResourceGatherer)) + { + this.SetNextState("APPROACHING"); + return true; + } + + let cmpResourceDropsite = Engine.QueryInterface(this.order.data.target, IID_ResourceDropsite); + + if (! cmpResourceDropsite || !this.CanReturnResource(this.order.data.target, true)) { - var cmpResourceDropsite = Engine.QueryInterface(this.order.data.target, IID_ResourceDropsite); - if (cmpResourceDropsite) - { - // Dump any resources we can - var dropsiteTypes = cmpResourceDropsite.GetTypes(); - - var cmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer); - cmpResourceGatherer.CommitResources(dropsiteTypes); - - // Stop showing the carried resource animation. - this.SetDefaultAnimationVariant(); - - // Our next order should always be a Gather, - // so just switch back to that order - this.FinishOrder(); - return; - } + this.SetNextState("LOOKINGFORDROPSITE"); + return true; } - // The dropsite was destroyed, or we couldn't reach it, or ownership changed - // Look for a new one. + // Dump any resources we can + let dropsiteTypes = cmpResourceDropsite.GetTypes(); + + let cmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer); + cmpResourceGatherer.CommitResources(dropsiteTypes); + + // Stop showing the carried resource animation. + this.SetDefaultAnimationVariant(); + + // Our next order should always be a Gather, + // so just switch back to that order + this.FinishOrder(); + return true; + }, + }, - var cmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer); - var genericType = cmpResourceGatherer.GetMainCarryingType(); - var nearby = this.FindNearestDropsite(genericType); + "LOOKINGFORDROPSITE": { + "enter": function() { + let cmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer); + let genericType = cmpResourceGatherer.GetMainCarryingType(); + let nearby = this.FindNearestDropsite(genericType); if (nearby) { this.FinishOrder(); this.PushOrderFront("ReturnResource", { "target": nearby, "force": false }); - return; + return true; } // Oh no, couldn't find any drop sites. Give up on returning. @@ -2656,9 +2623,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) { @@ -2684,9 +2660,14 @@ "REPAIR": { "APPROACHING": { "enter": function() { + this.MoveTo(this.order.data); this.SelectAnimation("move"); }, + "leave": function() { + this.StopMoving(); + }, + "MoveCompleted": function() { this.SetNextState("REPAIRING"); }, @@ -2728,6 +2709,8 @@ return true; } + this.StopMoving(); + let cmpBuilderList = QueryBuilderListInterface(this.repairTarget); if (cmpBuilderList) cmpBuilderList.AddBuilder(this.entity); @@ -2882,9 +2865,14 @@ "APPROACHING": { "enter": function() { + this.MoveTo(this.order.data); this.SelectAnimation("move"); }, + "leave": function() { + this.StopMoving(); + }, + "MoveCompleted": function() { this.SetNextState("GARRISONED"); }, @@ -2988,6 +2976,7 @@ }, "leave": function() { + this.isGarrisoned = false; } }, }, @@ -3015,6 +3004,8 @@ "PACKING": { "enter": function() { + this.StopMoving(); + var cmpPack = Engine.QueryInterface(this.entity, IID_Pack); cmpPack.Pack(); }, @@ -3024,6 +3015,9 @@ }, "leave": function() { + let cmpPack = Engine.QueryInterface(this.entity, IID_Pack); + if (cmpPack && cmpPack.IsPacking() && !cmpPack.IsPacked()) + cmpPack.CancelPack(); }, "Attacked": function(msg) { @@ -3033,6 +3027,8 @@ "UNPACKING": { "enter": function() { + this.StopMoving(); + var cmpPack = Engine.QueryInterface(this.entity, IID_Pack); cmpPack.Unpack(); }, @@ -3042,6 +3038,9 @@ }, "leave": function() { + let cmpPack = Engine.QueryInterface(this.entity, IID_Pack); + if (cmpPack && cmpPack.IsPacking() && !cmpPack.IsPacked()) + cmpPack.CancelPack(); }, "Attacked": function(msg) { @@ -3052,9 +3051,14 @@ "PICKUP": { "APPROACHING": { "enter": function() { + this.MoveTo(this.order.data); this.SelectAnimation("move"); }, + "leave": function() { + this.StopMoving(); + }, + "MoveCompleted": function() { this.SetNextState("LOADING"); }, @@ -3067,6 +3071,7 @@ "LOADING": { "enter": function() { + this.StopMoving(); this.SelectAnimation("idle"); var cmpGarrisonHolder = Engine.QueryInterface(this.entity, IID_GarrisonHolder); if (!cmpGarrisonHolder || cmpGarrisonHolder.IsFull()) @@ -3106,16 +3111,15 @@ "Order.LeaveFoundation": function(msg) { // Move a tile outside the building var range = 4; - if (!this.CheckTargetRangeExplicit(msg.data.target, range, range) && - 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"); } }, @@ -3140,6 +3144,7 @@ }, "leave": function() { + this.StopMoving(); this.StopTimer(); this.SetFacePointAfterMove(true); }, @@ -4246,6 +4251,50 @@ cmpUnitMotion.StopMoving(); }; +/** + * Generic dispatcher for other MoveTo functions. + * @param iid - Interface ID (optional) implementing GetRange + * @param type - Range type for the interface call + */ +UnitAI.prototype.MoveTo = function(data, iid, type) +{ + if (data["target"]) + { + if (data["min"] || data["max"]) + { + if (!this.MoveToTargetRangeExplicit(data.target, data.min || -1, data.max || -1)) + return false; + } + else + { + if (!iid) + { + if (!this.MoveToTarget(data.target)) + return false; + } + else + { + if (!this.MoveToTargetRange(data.target, iid, type)) + return false; + } + } + } + else + { + if (data["min"] || data["max"]) + { + if (!this.MoveToPointRange(data.x, data.z, data.min || -1, data.max || -1)) + return false; + } + else + { + if (!this.MoveToPoint(data.x, data.z)) + return false; + } + } + return true; +} + UnitAI.prototype.MoveToPoint = function(x, z) { var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);