Update UnitMotion logic, to partially simplify it.
Do all pathfinding asynchronously. Recalculate paths when the target has moved. Fix vertex pathfinder start point heuristic, to avoid finding zero-length paths. When units fail to reach a resource to gather, look for another resource of the same type. This was SVN commit r8751.
This commit is contained in:
parent
3401672887
commit
ab186c2b16
@ -258,6 +258,9 @@ var UnitFsmSpec = {
|
||||
"FORMATIONMEMBER": {
|
||||
|
||||
"FormationLeave": function(msg) {
|
||||
// Stop moving as soon as the formation disbands
|
||||
this.StopMoving();
|
||||
|
||||
// We're leaving the formation, so stop our FormationWalk order
|
||||
if (this.FinishOrder())
|
||||
return;
|
||||
@ -290,7 +293,8 @@ var UnitFsmSpec = {
|
||||
this.PushOrderFront("Attack", { "target": msg.data.attacker });
|
||||
}
|
||||
else
|
||||
{ // TODO: If unit can't attack, run away
|
||||
{
|
||||
// TODO: If unit can't attack, run away
|
||||
}
|
||||
},
|
||||
|
||||
@ -420,7 +424,34 @@ var UnitFsmSpec = {
|
||||
this.SelectAnimation("move");
|
||||
},
|
||||
|
||||
"MoveCompleted": function() {
|
||||
"MoveCompleted": function(msg) {
|
||||
if (msg.data.error)
|
||||
{
|
||||
// We failed to reach the target
|
||||
|
||||
// Save the current order's data in case we need it later
|
||||
var oldType = this.order.data.type;
|
||||
var oldTarget = this.order.data.target;
|
||||
|
||||
// Try the next queued order if there is any
|
||||
if (this.FinishOrder())
|
||||
return;
|
||||
|
||||
// Try to find another nearby target of the same type
|
||||
var nearby = this.FindNearbyResource(oldType, oldTarget);
|
||||
if (nearby)
|
||||
{
|
||||
this.Gather(nearby, true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Couldn't find anything else. Just try this one again,
|
||||
// maybe we'll succeed next time
|
||||
this.Gather(oldTarget, true);
|
||||
return;
|
||||
}
|
||||
|
||||
// We reached the target - start gathering from it now
|
||||
this.SetNextState("GATHERING");
|
||||
},
|
||||
},
|
||||
@ -509,26 +540,11 @@ var UnitFsmSpec = {
|
||||
|
||||
// Try to find a nearby target of the same type
|
||||
|
||||
var range = 64; // TODO: what's a sensible number?
|
||||
|
||||
// Accept any resources owned by Gaia
|
||||
var players = [0];
|
||||
// Also accept resources owned by this unit's player:
|
||||
var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
|
||||
if (cmpOwnership)
|
||||
players.push(cmpOwnership.GetOwner());
|
||||
|
||||
var rangeMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
|
||||
var nearby = rangeMan.ExecuteQuery(this.entity, 0, range, players, IID_ResourceSupply);
|
||||
for each (var ent in nearby)
|
||||
var nearby = this.FindNearbyResource(oldType);
|
||||
if (nearby)
|
||||
{
|
||||
var cmpResourceSupply = Engine.QueryInterface(ent, IID_ResourceSupply);
|
||||
var type = cmpResourceSupply.GetType();
|
||||
if (type.specific == oldType.specific)
|
||||
{
|
||||
this.Gather(ent, true);
|
||||
return;
|
||||
}
|
||||
this.Gather(nearby, true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Nothing else to gather - just give up
|
||||
@ -894,14 +910,14 @@ UnitAI.prototype.OnRangeUpdate = function(msg)
|
||||
|
||||
UnitAI.prototype.GetWalkSpeed = function()
|
||||
{
|
||||
var cmpMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
|
||||
return cmpMotion.GetWalkSpeed();
|
||||
var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
|
||||
return cmpUnitMotion.GetWalkSpeed();
|
||||
};
|
||||
|
||||
UnitAI.prototype.GetRunSpeed = function()
|
||||
{
|
||||
var cmpMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
|
||||
return cmpMotion.GetRunSpeed();
|
||||
var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
|
||||
return cmpUnitMotion.GetRunSpeed();
|
||||
};
|
||||
|
||||
/**
|
||||
@ -932,6 +948,40 @@ UnitAI.prototype.MustKillGatherTarget = function(ent)
|
||||
return this.TargetIsAlive(ent);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the entity ID of the nearest resource supply of the given
|
||||
* type, excluding the one with ID 'exclude',
|
||||
* or undefined if none can be found.
|
||||
* TODO: extend this to exclude resources that already have lots of
|
||||
* gatherers.
|
||||
*/
|
||||
UnitAI.prototype.FindNearbyResource = function(requestedType, exclude)
|
||||
{
|
||||
var range = 64; // TODO: what's a sensible number?
|
||||
|
||||
// Accept any resources owned by Gaia
|
||||
var players = [0];
|
||||
// Also accept resources owned by this unit's player:
|
||||
var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
|
||||
if (cmpOwnership)
|
||||
players.push(cmpOwnership.GetOwner());
|
||||
|
||||
var rangeMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
|
||||
var nearby = rangeMan.ExecuteQuery(this.entity, 0, range, players, IID_ResourceSupply);
|
||||
for each (var ent in nearby)
|
||||
{
|
||||
if (ent == exclude)
|
||||
continue;
|
||||
|
||||
var cmpResourceSupply = Engine.QueryInterface(ent, IID_ResourceSupply);
|
||||
var type = cmpResourceSupply.GetType();
|
||||
if (type.specific == requestedType.specific)
|
||||
return ent;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* Play a sound appropriate to the current entity.
|
||||
*/
|
||||
@ -998,23 +1048,22 @@ UnitAI.prototype.SetAnimationSync = function(actiontime, repeattime)
|
||||
cmpVisual.SetAnimationSyncOffset(actiontime);
|
||||
};
|
||||
|
||||
UnitAI.prototype.StopMoving = function()
|
||||
{
|
||||
var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
|
||||
cmpUnitMotion.StopMoving();
|
||||
};
|
||||
|
||||
UnitAI.prototype.MoveToPoint = function(x, z)
|
||||
{
|
||||
var cmpMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
|
||||
return cmpMotion.MoveToPoint(x, z);
|
||||
var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
|
||||
return cmpUnitMotion.MoveToPointRange(x, z, 0, 0);
|
||||
};
|
||||
|
||||
UnitAI.prototype.MoveToTarget = function(target)
|
||||
{
|
||||
var cmpPosition = Engine.QueryInterface(target, IID_Position);
|
||||
if (!cmpPosition)
|
||||
return false;
|
||||
|
||||
if (!cmpPosition.IsInWorld())
|
||||
return false;
|
||||
|
||||
var pos = cmpPosition.GetPosition();
|
||||
return this.MoveToPoint(pos.x, pos.z);
|
||||
var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
|
||||
return cmpUnitMotion.MoveToTargetRange(target, 0, 0);
|
||||
};
|
||||
|
||||
UnitAI.prototype.MoveToTargetRange = function(target, iid, type)
|
||||
@ -1022,8 +1071,8 @@ UnitAI.prototype.MoveToTargetRange = function(target, iid, type)
|
||||
var cmpRanged = Engine.QueryInterface(this.entity, iid);
|
||||
var range = cmpRanged.GetRange(type);
|
||||
|
||||
var cmpMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
|
||||
return cmpMotion.MoveToAttackRange(target, range.min, range.max);
|
||||
var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
|
||||
return cmpUnitMotion.MoveToTargetRange(target, range.min, range.max);
|
||||
};
|
||||
|
||||
UnitAI.prototype.CheckTargetRange = function(target, iid, type)
|
||||
@ -1031,8 +1080,8 @@ UnitAI.prototype.CheckTargetRange = function(target, iid, type)
|
||||
var cmpRanged = Engine.QueryInterface(this.entity, iid);
|
||||
var range = cmpRanged.GetRange(type);
|
||||
|
||||
var cmpMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
|
||||
return cmpMotion.IsInAttackRange(target, range.min, range.max);
|
||||
var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
|
||||
return cmpUnitMotion.IsInTargetRange(target, range.min, range.max);
|
||||
};
|
||||
|
||||
UnitAI.prototype.GetBestAttack = function()
|
||||
|
@ -58,7 +58,8 @@ function TestFormationExiting(mode)
|
||||
AddMock(unit, IID_UnitMotion, {
|
||||
GetWalkSpeed: function() { return 1; },
|
||||
MoveToFormationOffset: function(target, x, z) { },
|
||||
MoveToAttackRange: function(target, min, max) { },
|
||||
MoveToTargetRange: function(target, min, max) { },
|
||||
StopMoving: function() { },
|
||||
});
|
||||
|
||||
AddMock(unit, IID_Vision, {
|
||||
@ -97,7 +98,7 @@ function TestFormationExiting(mode)
|
||||
AddMock(controller, IID_UnitMotion, {
|
||||
SetUnitRadius: function(r) { },
|
||||
SetSpeed: function(speed) { },
|
||||
MoveToPoint: function(x, z) { },
|
||||
MoveToPointRange: function(x, z, minRange, maxRange) { },
|
||||
});
|
||||
|
||||
controllerAI.OnCreate();
|
||||
|
@ -226,6 +226,8 @@ public:
|
||||
|
||||
virtual fixed GetMovementSpeed(entity_pos_t x0, entity_pos_t z0, u8 costClass);
|
||||
|
||||
virtual CFixedVector2D GetNearestPointOnGoal(CFixedVector2D pos, const Goal& goal);
|
||||
|
||||
virtual bool CheckMovement(const IObstructionTestFilter& filter, entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, entity_pos_t r, u8 passClass);
|
||||
|
||||
virtual void FinishAsyncRequests();
|
||||
|
@ -317,6 +317,14 @@ static CFixedVector2D NearestPointOnGoal(CFixedVector2D pos, const CCmpPathfinde
|
||||
}
|
||||
}
|
||||
|
||||
CFixedVector2D CCmpPathfinder::GetNearestPointOnGoal(CFixedVector2D pos, const CCmpPathfinder::Goal& goal)
|
||||
{
|
||||
return NearestPointOnGoal(pos, goal);
|
||||
// (It's intentional that we don't put the implementation inside this
|
||||
// function, to avoid the (admittedly unmeasured and probably trivial)
|
||||
// cost of a virtual call inside ComputeShortPath)
|
||||
}
|
||||
|
||||
typedef PriorityQueueList<u16, fixed> PriorityQueue;
|
||||
|
||||
struct TileEdge
|
||||
@ -541,14 +549,14 @@ void CCmpPathfinder::ComputeShortPath(const IObstructionTestFilter& filter, enti
|
||||
edges.push_back(e3);
|
||||
}
|
||||
|
||||
CFixedVector2D goalVec(goal.x, goal.z);
|
||||
|
||||
// List of obstruction vertexes (plus start/end points); we'll try to find paths through
|
||||
// the graph defined by these vertexes
|
||||
std::vector<Vertex> vertexes;
|
||||
|
||||
// Add the start point to the graph
|
||||
Vertex start = { CFixedVector2D(x0, z0), fixed::Zero(), (CFixedVector2D(x0, z0) - goalVec).Length(), 0, Vertex::OPEN, QUADRANT_NONE, QUADRANT_ALL };
|
||||
CFixedVector2D posStart(x0, z0);
|
||||
fixed hStart = (posStart - NearestPointOnGoal(posStart, goal)).Length();
|
||||
Vertex start = { posStart, fixed::Zero(), hStart, 0, Vertex::OPEN, QUADRANT_NONE, QUADRANT_ALL };
|
||||
vertexes.push_back(start);
|
||||
const size_t START_VERTEX_ID = 0;
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -129,6 +129,11 @@ public:
|
||||
*/
|
||||
virtual fixed GetMovementSpeed(entity_pos_t x0, entity_pos_t z0, u8 costClass) = 0;
|
||||
|
||||
/**
|
||||
* Returns the coordinates of the point on the goal that is closest to pos in a straight line.
|
||||
*/
|
||||
virtual CFixedVector2D GetNearestPointOnGoal(CFixedVector2D pos, const Goal& goal) = 0;
|
||||
|
||||
/**
|
||||
* Check whether the given movement line is valid and doesn't hit any obstructions
|
||||
* or impassable terrain.
|
||||
|
@ -22,10 +22,9 @@
|
||||
#include "simulation2/system/InterfaceScripted.h"
|
||||
|
||||
BEGIN_INTERFACE_WRAPPER(UnitMotion)
|
||||
DEFINE_INTERFACE_METHOD_2("MoveToPoint", bool, ICmpUnitMotion, MoveToPoint, entity_pos_t, entity_pos_t)
|
||||
DEFINE_INTERFACE_METHOD_3("IsInAttackRange", bool, ICmpUnitMotion, IsInAttackRange, entity_id_t, entity_pos_t, entity_pos_t)
|
||||
DEFINE_INTERFACE_METHOD_3("MoveToAttackRange", bool, ICmpUnitMotion, MoveToAttackRange, entity_id_t, entity_pos_t, entity_pos_t)
|
||||
DEFINE_INTERFACE_METHOD_4("MoveToPointRange", bool, ICmpUnitMotion, MoveToPointRange, entity_pos_t, entity_pos_t, entity_pos_t, entity_pos_t)
|
||||
DEFINE_INTERFACE_METHOD_3("IsInTargetRange", bool, ICmpUnitMotion, IsInTargetRange, entity_id_t, entity_pos_t, entity_pos_t)
|
||||
DEFINE_INTERFACE_METHOD_3("MoveToTargetRange", bool, ICmpUnitMotion, MoveToTargetRange, entity_id_t, entity_pos_t, entity_pos_t)
|
||||
DEFINE_INTERFACE_METHOD_3("MoveToFormationOffset", void, ICmpUnitMotion, MoveToFormationOffset, entity_id_t, entity_pos_t, entity_pos_t)
|
||||
DEFINE_INTERFACE_METHOD_0("StopMoving", void, ICmpUnitMotion, StopMoving)
|
||||
DEFINE_INTERFACE_METHOD_1("SetSpeed", void, ICmpUnitMotion, SetSpeed, fixed)
|
||||
|
@ -34,36 +34,30 @@
|
||||
class ICmpUnitMotion : public IComponent
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Attempt to walk to a given point, or as close as possible.
|
||||
* Attempt to walk into range of a to a given point, or as close as possible.
|
||||
* If the unit cannot move anywhere at all, or if there is some other error, then
|
||||
* returns false.
|
||||
* Otherwise, sends a MotionChanged message and returns true; it will send another
|
||||
* MotionChanged message (with speed 0) once it has reached the target or otherwise
|
||||
* given up trying to reach it.
|
||||
* Otherwise, returns true and sends a MotionChanged message after starting to move,
|
||||
* and sends another MotionChanged after finishing moving.
|
||||
*/
|
||||
virtual bool MoveToPoint(entity_pos_t x, entity_pos_t z) = 0;
|
||||
virtual bool MoveToPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t minRange, entity_pos_t maxRange) = 0;
|
||||
|
||||
/**
|
||||
* Determine whether the target is within the given range, using the same measurement
|
||||
* as MoveToAttackRange.
|
||||
* as MoveToTargetRange.
|
||||
*/
|
||||
virtual bool IsInAttackRange(entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange) = 0;
|
||||
virtual bool IsInTargetRange(entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange) = 0;
|
||||
|
||||
/**
|
||||
* Attempt to walk into range of a given target entity, or as close as possible.
|
||||
* If the unit is already in range, or cannot move anywhere at all, or if there is
|
||||
* some other error, then returns false.
|
||||
* Otherwise, sends a MotionChanged message and returns true; it will send another
|
||||
* MotionChanged message (with speed 0) once it has reached the target range (such that
|
||||
* IsInAttackRange should return true) or otherwise given up trying to reach it.
|
||||
* Otherwise, returns true and sends a MotionChanged message after starting to move,
|
||||
* and sends another MotionChanged after finishing moving.
|
||||
*/
|
||||
virtual bool MoveToAttackRange(entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange) = 0;
|
||||
|
||||
/**
|
||||
* See MoveToAttackRange, but the target is the given point.
|
||||
*/
|
||||
virtual bool MoveToPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t minRange, entity_pos_t maxRange) = 0;
|
||||
virtual bool MoveToTargetRange(entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange) = 0;
|
||||
|
||||
/**
|
||||
* Join a formation, and move towards a given offset relative to the formation controller entity.
|
||||
|
Loading…
Reference in New Issue
Block a user