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 @@ -772,7 +772,7 @@ return; } - this.PushOrderFront("Attack", { "target": msg.data.target, "force": !!msg.data.force, "hunting": true, "allowCapture": false }); + this.PushOrderFront("Attack", { "target": msg.data.target, "force": !!msg.data.force, "hunting": true, "allowCapture": false, "min": 0, "max": 10 }); return; } @@ -1218,10 +1218,7 @@ } // No orders left, we're an individual now - if (this.IsAnimal()) - this.SetNextState("ANIMAL.IDLE"); - else - this.SetNextState("INDIVIDUAL.IDLE"); + this.SetNextState("INDIVIDUAL.IDLE"); }, // Override the LeaveFoundation order since we're not doing @@ -1251,6 +1248,15 @@ }, "enter": function() { + if (this.IsAnimal()) + { + // Animals can't go in formation. + warn("Entity " + this.entity + " was put in FORMATIONMEMBER state but is an animal"); + this.FinishOrder(); + this.SetNextState("ANIMAL.IDLE"); + return true; + } + let cmpFormation = Engine.QueryInterface(this.formationController, IID_Formation); if (cmpFormation) this.SetAnimationVariant(cmpFormation.GetFormationAnimation(this.entity)); @@ -1261,13 +1267,8 @@ }, "IDLE": { - "enter": function() { - if (this.IsAnimal()) - this.SetNextState("ANIMAL.IDLE"); - else - this.SetNextState("INDIVIDUAL.IDLE"); - return true; - }, + // Formation members do nothing while Idle, but we need the state + // so that they keep the formation variant. }, "WALKING": { @@ -1276,11 +1277,22 @@ cmpUnitMotion.MoveToFormationOffset(this.order.data.target, this.order.data.x, this.order.data.z); }, + "leave": function() { + this.StopMoving(); + }, + // Occurs when the unit has reached its destination and the controller // is done moving. The controller is notified. "MovementUpdate": function(msg) { // We can only finish this order if the move was really completed. - if (!this.CheckRange(this.order.data) || msg.error) + let cmpPosition = Engine.QueryInterface(this.formationController, IID_Position); + let atDestination = cmpPosition && cmpPosition.IsInWorld(); + if (!atDestination && cmpPosition) + { + let pos = cmpPosition.GetPosition2D(); + atDestination = this.CheckPointRangeExplicit(pos.X + this.order.data.x, pos.Y + this.order.data.z, 0, 0); + } + if (!atDestination && !msg.error) return; if (this.FinishOrder()) @@ -1386,15 +1398,15 @@ "IDLE": { "enter": function() { + if (this.formationController) + { + this.SetNextState("FORMATIONMEMBER.IDLE"); + return true; + } + // Switch back to idle animation to guarantee we won't // get stuck with an incorrect animation var animationName = "idle"; - if (this.IsFormationMember()) - { - var cmpFormation = Engine.QueryInterface(this.formationController, IID_Formation); - if (cmpFormation) - animationName = cmpFormation.GetFormationAnimation(this.entity, animationName); - } this.SelectAnimation(animationName); // If we have some orders, it is because we are in an intermediary state Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_formation.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_formation.xml +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_formation.xml @@ -36,6 +36,17 @@ false + + + true + false + false + false + false + false + false + false + 0 Index: ps/trunk/source/simulation2/components/CCmpUnitMotion.cpp =================================================================== --- ps/trunk/source/simulation2/components/CCmpUnitMotion.cpp +++ ps/trunk/source/simulation2/components/CCmpUnitMotion.cpp @@ -706,12 +706,6 @@ // This makes sure that units don't clump too much when they are not in a formation and tasked to move. if (m_LongPath.m_Waypoints.size() > 1) m_LongPath.m_Waypoints.pop_back(); - else if (IsFormationMember()) - { - CMessageMotionChanged msg(true); - GetSimContext().GetComponentManager().PostMessage(GetEntityId(), msg); - return; - } CMessageMotionChanged msg(false); GetSimContext().GetComponentManager().PostMessage(GetEntityId(), msg); @@ -818,15 +812,6 @@ if (m_MoveRequest.m_Type == MoveRequest::NONE) return false; - if (IsFormationMember()) - { - // We've reached our assigned position. If the controller - // is idle, send a notification in case it should disband, - // otherwise continue following the formation next turn. - CmpPtr cmpUnitMotion(GetSimContext(), m_MoveRequest.m_Entity); - return cmpUnitMotion && !cmpUnitMotion->IsMoving(); - } - CmpPtr cmpObstructionManager(GetSystemEntity()); ENSURE(cmpObstructionManager); @@ -836,9 +821,14 @@ return cmpObstructionManager->IsInTargetRange(GetEntityId(), m_MoveRequest.m_Entity, m_MoveRequest.m_MinRange, m_MoveRequest.m_MaxRange, false); if (m_MoveRequest.m_Type == MoveRequest::OFFSET) { + CmpPtr cmpControllerMotion(GetSimContext(), m_MoveRequest.m_Entity); + if (cmpControllerMotion && cmpControllerMotion->IsMoving()) + return false; + CFixedVector2D targetPos; ComputeTargetPosition(targetPos); - return cmpObstructionManager->IsInPointRange(GetEntityId(), m_MoveRequest.m_Position.X, m_MoveRequest.m_Position.Y, m_MoveRequest.m_MinRange, m_MoveRequest.m_MaxRange, false); + CmpPtr cmpPosition(GetEntityHandle()); + return cmpObstructionManager->IsInPointRange(GetEntityId(), targetPos.X, targetPos.Y, m_MoveRequest.m_MinRange, m_MoveRequest.m_MaxRange, false); } return false; }