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:
parent
a1dc9cadd8
commit
4ca448a686
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user