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 @@ -146,6 +146,92 @@ // When leaving a foundation, we want to be clear of it by this distance. var g_LeaveFoundationRange = 4; +var GetPatrolState = () => +{ + return { + "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()) + { + this.FinishOrder(); + return true; + } + + if (!this.patrolStartPosOrder) + { + this.patrolStartPosOrder = cmpPosition.GetPosition(); + this.patrolStartPosOrder.targetClasses = this.order.data.targetClasses; + this.patrolStartPosOrder.allowCapture = this.order.data.allowCapture; + } + + this.SetAnimationVariant("combat"); + }, + + "leave": function() { + delete this.patrolStartPosOrder; + this.SetDefaultAnimationVariant(); + }, + + "PATROLLING": { + "enter": function() { + let cmpPosition = Engine.QueryInterface(this.entity, IID_Position); + if (!cmpPosition || !cmpPosition.IsInWorld() || + !this.MoveTo(this.order.data)) + { + this.FinishOrder(); + return true; + } + this.StartTimer(0, 1000); + return false; + }, + + "leave": function() { + this.StopMoving(); + this.StopTimer(); + }, + + "Timer": function(msg) { + this.FindWalkAndFightTargets(); + }, + + "MovementUpdate": function(msg) { + if (!msg.likelyFailure && !msg.likelySuccess && !this.RelaxedMaxRangeCheck(this.order.data, this.DefaultRelaxedMaxRange)) + return; + + if (this.orderQueue.length == 1) + this.PushOrder("Patrol", this.patrolStartPosOrder); + + this.PushOrder(this.order.type, this.order.data); + this.SetNextState("CHECKINGWAYPOINT"); + }, + }, + + "CHECKINGWAYPOINT": { + "enter": function() { + this.StartTimer(0, 1000); + this.stopSurveying = 0; + return false; + }, + + "leave": function() { + this.StopTimer(); + delete this.stopSurveying; + }, + + "Timer": function(msg) { + if (this.stopSurveying > 2) + { + this.FinishOrder(); + return; + } + this.FindWalkAndFightTargets(); + this.stopSurveying++; + } + } + }; +}; + // See ../helpers/FSM.js for some documentation of this FSM specification syntax UnitAI.prototype.UnitFsmSpec = { @@ -496,7 +582,7 @@ // It's not too bad if we don't arrive at exactly the right position. this.order.data.relaxed = true; - this.SetNextState("INDIVIDUAL.PATROL"); + this.SetNextState("INDIVIDUAL.PATROL.PATROLLING"); }, "Order.Heal": function(msg) { @@ -737,7 +823,7 @@ "Order.Patrol": function(msg) { this.CallMemberFunction("SetHeldPosition", [msg.data.x, msg.data.z]); - this.SetNextState("PATROL"); + this.SetNextState("PATROL.PATROLLING"); }, "Order.Guard": function(msg) { @@ -1001,66 +1087,20 @@ }, }, - "PATROL": { - "enter": function(msg) { - // Memorize the origin position in case that we want to go back - let cmpPosition = Engine.QueryInterface(this.entity, IID_Position); - if (!cmpPosition || !cmpPosition.IsInWorld()) - { - this.FinishOrder(); + "PATROL": (() => { + let patrolState = GetPatrolState(); + let og = patrolState.PATROLLING.enter; + patrolState.PATROLLING.enter = function() { + if (og.apply(this)) return true; - } - if (!this.patrolStartPosOrder) - { - this.patrolStartPosOrder = cmpPosition.GetPosition(); - this.patrolStartPosOrder.targetClasses = this.order.data.targetClasses; - this.patrolStartPosOrder.allowCapture = this.order.data.allowCapture; - } - - if (!this.MoveTo(this.order.data)) - { - this.FinishOrder(); - return true; - } - this.StartTimer(0, 1000); let cmpFormation = Engine.QueryInterface(this.entity, IID_Formation); cmpFormation.SetRearrange(true); cmpFormation.MoveMembersIntoFormation(true, true, "combat"); return false; - }, - - "Timer": function(msg) { - // Check if there are no enemies to attack - this.FindWalkAndFightTargets(); - }, - - "leave": function(msg) { - this.StopTimer(); - this.StopMoving(); - delete this.patrolStartPosOrder; - }, - - "MovementUpdate": function(msg) { - if (!msg.likelyFailure && !this.CheckRange(this.order.data)) - return; - /** - * A-B-A-B-..: - * if the user only commands one patrol order, the patrol will be between - * the last position and the defined waypoint - * A-B-C-..-A-B-..: - * otherwise, the patrol is only between the given patrol commands and the - * last position is not included (last position = the position where the unit - * is located at the time of the first patrol order) - */ - - if (this.orderQueue.length == 1) - this.PushOrder("Patrol", this.patrolStartPosOrder); - - this.PushOrder(this.order.type, this.order.data); - this.FinishOrder(); - }, - }, + }; + return patrolState; + })(), "GARRISON":{ "APPROACHING": { @@ -1640,51 +1680,7 @@ }, }, - "PATROL": { - "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() || - !this.MoveTo(this.order.data)) - { - this.FinishOrder(); - return true; - } - - if (!this.patrolStartPosOrder) - { - this.patrolStartPosOrder = cmpPosition.GetPosition(); - this.patrolStartPosOrder.targetClasses = this.order.data.targetClasses; - this.patrolStartPosOrder.allowCapture = this.order.data.allowCapture; - } - - this.StartTimer(0, 1000); - this.SetAnimationVariant("combat"); - return false; - }, - - "leave": function() { - this.StopMoving(); - this.StopTimer(); - delete this.patrolStartPosOrder; - this.SetDefaultAnimationVariant(); - }, - - "Timer": function(msg) { - this.FindWalkAndFightTargets(); - }, - - "MovementUpdate": function(msg) { - if (!msg.likelyFailure && !msg.likelySuccess && !this.RelaxedMaxRangeCheck(this.order.data, this.DefaultRelaxedMaxRange)) - return; - - if (this.orderQueue.length == 1) - this.PushOrder("Patrol", this.patrolStartPosOrder); - - this.PushOrder(this.order.type, this.order.data); - this.FinishOrder(); - }, - }, + "PATROL": GetPatrolState(), "GUARD": { "RemoveGuard": function() {