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;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1218,9 +1218,6 @@ UnitAI.prototype.UnitFsmSpec = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// No orders left, we're an individual now
|
// No orders left, we're an individual now
|
||||||
if (this.IsAnimal())
|
|
||||||
this.SetNextState("ANIMAL.IDLE");
|
|
||||||
else
|
|
||||||
this.SetNextState("INDIVIDUAL.IDLE");
|
this.SetNextState("INDIVIDUAL.IDLE");
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -1251,6 +1248,15 @@ UnitAI.prototype.UnitFsmSpec = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
"enter": function() {
|
"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);
|
let cmpFormation = Engine.QueryInterface(this.formationController, IID_Formation);
|
||||||
if (cmpFormation)
|
if (cmpFormation)
|
||||||
this.SetAnimationVariant(cmpFormation.GetFormationAnimation(this.entity));
|
this.SetAnimationVariant(cmpFormation.GetFormationAnimation(this.entity));
|
||||||
@ -1261,13 +1267,8 @@ UnitAI.prototype.UnitFsmSpec = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
"IDLE": {
|
"IDLE": {
|
||||||
"enter": function() {
|
// Formation members do nothing while Idle, but we need the state
|
||||||
if (this.IsAnimal())
|
// so that they keep the formation variant.
|
||||||
this.SetNextState("ANIMAL.IDLE");
|
|
||||||
else
|
|
||||||
this.SetNextState("INDIVIDUAL.IDLE");
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
"WALKING": {
|
"WALKING": {
|
||||||
@ -1276,11 +1277,22 @@ UnitAI.prototype.UnitFsmSpec = {
|
|||||||
cmpUnitMotion.MoveToFormationOffset(this.order.data.target, this.order.data.x, this.order.data.z);
|
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
|
// Occurs when the unit has reached its destination and the controller
|
||||||
// is done moving. The controller is notified.
|
// is done moving. The controller is notified.
|
||||||
"MovementUpdate": function(msg) {
|
"MovementUpdate": function(msg) {
|
||||||
// We can only finish this order if the move was really completed.
|
// 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;
|
return;
|
||||||
|
|
||||||
if (this.FinishOrder())
|
if (this.FinishOrder())
|
||||||
@ -1386,15 +1398,15 @@ UnitAI.prototype.UnitFsmSpec = {
|
|||||||
|
|
||||||
"IDLE": {
|
"IDLE": {
|
||||||
"enter": function() {
|
"enter": function() {
|
||||||
|
if (this.formationController)
|
||||||
|
{
|
||||||
|
this.SetNextState("FORMATIONMEMBER.IDLE");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Switch back to idle animation to guarantee we won't
|
// Switch back to idle animation to guarantee we won't
|
||||||
// get stuck with an incorrect animation
|
// get stuck with an incorrect animation
|
||||||
var animationName = "idle";
|
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);
|
this.SelectAnimation(animationName);
|
||||||
|
|
||||||
// If we have some orders, it is because we are in an intermediary state
|
// If we have some orders, it is because we are in an intermediary state
|
||||||
|
@ -36,6 +36,17 @@
|
|||||||
<FormationAttack>
|
<FormationAttack>
|
||||||
<CanAttackAsFormation>false</CanAttackAsFormation>
|
<CanAttackAsFormation>false</CanAttackAsFormation>
|
||||||
</FormationAttack>
|
</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/>
|
<Ownership/>
|
||||||
<Position>
|
<Position>
|
||||||
<Altitude>0</Altitude>
|
<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.
|
// 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)
|
if (m_LongPath.m_Waypoints.size() > 1)
|
||||||
m_LongPath.m_Waypoints.pop_back();
|
m_LongPath.m_Waypoints.pop_back();
|
||||||
else if (IsFormationMember())
|
|
||||||
{
|
|
||||||
CMessageMotionChanged msg(true);
|
|
||||||
GetSimContext().GetComponentManager().PostMessage(GetEntityId(), msg);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
CMessageMotionChanged msg(false);
|
CMessageMotionChanged msg(false);
|
||||||
GetSimContext().GetComponentManager().PostMessage(GetEntityId(), msg);
|
GetSimContext().GetComponentManager().PostMessage(GetEntityId(), msg);
|
||||||
@ -818,15 +812,6 @@ bool CCmpUnitMotion::PossiblyAtDestination() const
|
|||||||
if (m_MoveRequest.m_Type == MoveRequest::NONE)
|
if (m_MoveRequest.m_Type == MoveRequest::NONE)
|
||||||
return false;
|
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());
|
CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSystemEntity());
|
||||||
ENSURE(cmpObstructionManager);
|
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);
|
return cmpObstructionManager->IsInTargetRange(GetEntityId(), m_MoveRequest.m_Entity, m_MoveRequest.m_MinRange, m_MoveRequest.m_MaxRange, false);
|
||||||
if (m_MoveRequest.m_Type == MoveRequest::OFFSET)
|
if (m_MoveRequest.m_Type == MoveRequest::OFFSET)
|
||||||
{
|
{
|
||||||
|
CmpPtr<ICmpUnitMotion> cmpControllerMotion(GetSimContext(), m_MoveRequest.m_Entity);
|
||||||
|
if (cmpControllerMotion && cmpControllerMotion->IsMoving())
|
||||||
|
return false;
|
||||||
|
|
||||||
CFixedVector2D targetPos;
|
CFixedVector2D targetPos;
|
||||||
ComputeTargetPosition(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;
|
return false;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user