Fix Formation walking / other orders with the new UnitMotion

This fixes a number of issues with formations:
- Gives the controller an obstruction. This fixes the bug where units in
formations can't gather from a tree.
- Stop special-casing formation members in PathResult. This fixes
formation members being stuck in-place when they run in an obstruction.
- Makes sure units stay in Formation.IDle when they are idle so that
D1337/D1901 can work correctly in the IDLE state.
- Warn if animals enter this state.
- Make sure that formation members that end up in INDIVIDUAL.IDLE go
back to FORMATION.IDLE for sanity and for better housekeeping (refs
#3144 - fixed completely upstream).

Differential Revision: https://code.wildfiregames.com/D2048
This was SVN commit r22447.
This commit is contained in:
wraitii 2019-07-09 19:58:44 +00:00
parent a1dc9cadd8
commit 4ca448a686
3 changed files with 48 additions and 35 deletions

View File

@ -772,7 +772,7 @@ UnitAI.prototype.UnitFsmSpec = {
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 @@ UnitAI.prototype.UnitFsmSpec = {
}
// 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 @@ UnitAI.prototype.UnitFsmSpec = {
},
"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 @@ UnitAI.prototype.UnitFsmSpec = {
},
"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 @@ UnitAI.prototype.UnitFsmSpec = {
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 @@ UnitAI.prototype.UnitFsmSpec = {
"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

View File

@ -36,6 +36,17 @@
<FormationAttack>
<CanAttackAsFormation>false</CanAttackAsFormation>
</FormationAttack>
<Obstruction>
<Unit/>
<Active>true</Active>
<BlockMovement>false</BlockMovement>
<BlockPathfinding>false</BlockPathfinding>
<BlockFoundation>false</BlockFoundation>
<BlockConstruction>false</BlockConstruction>
<DisableBlockMovement>false</DisableBlockMovement>
<DisableBlockPathfinding>false</DisableBlockPathfinding>
<DeleteUponConstruction>false</DeleteUponConstruction>
</Obstruction>
<Ownership/>
<Position>
<Altitude>0</Altitude>

View File

@ -706,12 +706,6 @@ void CCmpUnitMotion::PathResult(u32 ticket, const WaypointPath& path)
// 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 @@ bool CCmpUnitMotion::PossiblyAtDestination() const
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<ICmpUnitMotion> cmpUnitMotion(GetSimContext(), m_MoveRequest.m_Entity);
return cmpUnitMotion && !cmpUnitMotion->IsMoving();
}
CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSystemEntity());
ENSURE(cmpObstructionManager);
@ -836,9 +821,14 @@ bool CCmpUnitMotion::PossiblyAtDestination() const
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<ICmpUnitMotion> 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<ICmpPosition> cmpPosition(GetEntityHandle());
return cmpObstructionManager->IsInPointRange(GetEntityId(), targetPos.X, targetPos.Y, m_MoveRequest.m_MinRange, m_MoveRequest.m_MaxRange, false);
}
return false;
}