mirror of
https://github.com/tribes2/engine.git
synced 2026-03-23 14:20:48 +00:00
t2 engine svn checkout
This commit is contained in:
commit
ff569bd2ae
988 changed files with 394180 additions and 0 deletions
388
ai/graphBase.cc
Normal file
388
ai/graphBase.cc
Normal file
|
|
@ -0,0 +1,388 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// V12 Engine
|
||||
//
|
||||
// Copyright (c) 2001 GarageGames.Com
|
||||
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "ai/graph.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
// GraphEdge
|
||||
|
||||
GraphEdge::GraphEdge()
|
||||
{
|
||||
mDest = -1; // Destination node index
|
||||
mBorder = -1; // Border segment on interiors
|
||||
mDist = 1.0; // 3D dist, though jet edges are adjusted a little so ratings work
|
||||
mJet = 0; // Jetting connection
|
||||
mHopOver = 0; // For jetting, amount to hop up and over
|
||||
|
||||
// Factors that influence edge scaling-
|
||||
//
|
||||
mTeam = 0; // An edge that only this team can pass - i.e. force fields.
|
||||
mJump = 0; // On jetting edges - is it flat enough to jump?
|
||||
mLateral = 0; // Crude lateral distance - needed to know if we can jet down.
|
||||
mDown = 0; // Down side of a jet?
|
||||
mSteep = 0; // Can't walk up
|
||||
setInverse(1.0);
|
||||
}
|
||||
|
||||
const char * GraphEdge::problems() const
|
||||
{
|
||||
if (mDist < 0)
|
||||
return "Dijkstra() forbids edge with negative distance";
|
||||
if (getInverse() < 0)
|
||||
return "Edge with negative scale";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Given the jet rating, can this connection be traversed? If down, must satisfy
|
||||
// lateral only. Used by searcher, and by partition builder. In the latter case it
|
||||
// must (on 1st of its 2 passes) treat down same as up to find stranded partitions.
|
||||
bool GraphEdge::canJet(const F32* ratings, bool both) const
|
||||
{
|
||||
if (!mDown || both)
|
||||
{
|
||||
F32 rating = ratings[mJump];
|
||||
return (rating >= mDist);
|
||||
}
|
||||
else
|
||||
{
|
||||
F32 rating = ratings[mJump];
|
||||
return rating > F32(mLateral);
|
||||
}
|
||||
}
|
||||
|
||||
// This table gives our inverse speed values, so we can keep it in a few bits. Should
|
||||
// still give the desired behavior, though a little coarsely at times. Perfect paths
|
||||
// in terms of travel times is not something that people seem to percieve that much...
|
||||
const F32 GraphEdge::csInvSpdTab[InvTabSz + 1] =
|
||||
{
|
||||
0.0, 0.1, 0.2, 0.3, 0.4,
|
||||
0.6, 0.8, 1.0, 1.2, 1.4,
|
||||
|
||||
1.7, 2.0, 2.6, 3.5, 4.6,
|
||||
5.7, 6.8, 8.0, 9.2, 10.4,
|
||||
|
||||
12.0, 14.1, 17.0, 20.0, 24.0,
|
||||
30.0, 37.0, 45.0, 54.0, 64.0,
|
||||
|
||||
80.0, 100.0, 125.0, 156.0, 200.0,
|
||||
400.0, 800.0, 1600.0, 3200.0, 6400.0,
|
||||
|
||||
2.56e4, 1.02e5, 4.1e5, 1.6e6, 6.5e6,
|
||||
2.6e7, 1.05e8, 4.19e8, 1.68e9, 9.9e10,
|
||||
|
||||
1e11, 1e12, 1e13, 1e14, 1e15,
|
||||
1e16, 1e17, 1e18, 1e19, 1e20,
|
||||
1e21, 1e22, 1e23, 1e24,
|
||||
|
||||
// Sentinal value -
|
||||
1e40,
|
||||
};
|
||||
|
||||
// Find the 6-bit number using a binary search...
|
||||
void GraphEdge::setInverse(F32 is)
|
||||
{
|
||||
S32 lo = 0;
|
||||
S32 hi = InvTabSz;
|
||||
|
||||
AssertFatal(is < csInvSpdTab[hi] && is > csInvSpdTab[lo], "Bad edge inverse speed");
|
||||
|
||||
// Find the largest value we're greater than in the table-
|
||||
for (S32 i = InvSpdBits; i > 0; i--)
|
||||
{
|
||||
S32 mid = (hi + lo >> 1);
|
||||
if (is >= csInvSpdTab[mid])
|
||||
lo = mid;
|
||||
else
|
||||
hi = mid;
|
||||
}
|
||||
|
||||
mInverse = lo;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
// GraphNode Methods, Default Virtuals
|
||||
|
||||
GraphNode::GraphNode()
|
||||
{
|
||||
mIsland = -1;
|
||||
mOnPath = 0;
|
||||
mAvoidUntil = 0;
|
||||
mLoc.set(-1,-1,-1);
|
||||
// mOwnedPtr = NULL;
|
||||
// mOwnedCnt = 0;
|
||||
mThreats = 0;
|
||||
}
|
||||
|
||||
// Derived classes must supply fetcher which takes a buffer to build in. This method
|
||||
// patches to that, and should serve fine if pointers to these aren't kept around.
|
||||
const Point3F& GraphNode::location() const
|
||||
{
|
||||
static Point3F buff[8];
|
||||
static U8 ind = 0;
|
||||
return fetchLoc(buff[ind++ & 7]);
|
||||
}
|
||||
|
||||
// Outdoor nodes use level (and they start at -1), others don't. Though this is also
|
||||
// used by path randomization to determine if it's reasonable to avoid the node.
|
||||
S32 GraphNode::getLevel() const
|
||||
{
|
||||
return -2;
|
||||
}
|
||||
|
||||
Point3F GraphNode::getRenderPos() const
|
||||
{
|
||||
Point3F buff, adjusted;
|
||||
adjusted = fetchLoc(buff);
|
||||
adjusted.z += 0.3;
|
||||
return adjusted;
|
||||
}
|
||||
|
||||
// Set if not already set (shouldn't be set already, caller asserts)
|
||||
void GraphNode::setIsland(S32 num)
|
||||
{
|
||||
AssertFatal(!mFlags.test(GotIsland), "GraphNode::setIsland()");
|
||||
mFlags.set(GotIsland);
|
||||
mIsland = num;
|
||||
}
|
||||
|
||||
// Ideally this ratio would accurately reflect travel speeds along the slopes.
|
||||
// Here we use
|
||||
// xy / total If we're going down. Hence XY is net path distance.
|
||||
// total / xy If we're going up.
|
||||
static F32 getBaseEdgeScale(const Point3F& src, Point3F dst)
|
||||
{
|
||||
F32 totalDist = (dst -= src).len();
|
||||
bool goingUp = (dst.z > 0);
|
||||
dst.z = 0;
|
||||
F32 xyDist = dst.len();
|
||||
F32 ratio = 1.0;
|
||||
|
||||
if (xyDist > 0.01 && totalDist > 0.01)
|
||||
{
|
||||
if (goingUp)
|
||||
{
|
||||
ratio = (totalDist / xyDist);
|
||||
if (ratio < 1.2)
|
||||
ratio = 1.0;
|
||||
}
|
||||
else
|
||||
ratio = (xyDist / totalDist);
|
||||
|
||||
// Want to do something between square root and straight value, so we'll
|
||||
// raise it to the 3/4 power here, which will bring it to 1 from either side.
|
||||
ratio = mSqrt(mSqrt(ratio) * ratio);
|
||||
}
|
||||
|
||||
return ratio;
|
||||
}
|
||||
|
||||
F32 GraphNode::edgeScale(const GraphNode * dest) const
|
||||
{
|
||||
// F32 scale = 1.0;
|
||||
F32 scale = getBaseEdgeScale(location(), dest->location());
|
||||
|
||||
if (submerged())
|
||||
scale *= gNavGraph->submergedScale();
|
||||
else if (shoreline())
|
||||
scale *= gNavGraph->shoreLineScale();
|
||||
|
||||
if (dest->submerged())
|
||||
scale *= gNavGraph->submergedScale();
|
||||
else if (dest->shoreline())
|
||||
scale *= gNavGraph->shoreLineScale();
|
||||
|
||||
// Searcher reserves REALLY large numbers (10^20th about) to signal search
|
||||
// failure, so make sure we don't inadvertently interfere with any of that.
|
||||
scale = getMin(scale, 1000000000.0f);
|
||||
|
||||
return scale;
|
||||
}
|
||||
|
||||
static GraphEdge sEdgeBuff[MaxOnDemandEdges];
|
||||
|
||||
bool GraphNode::neighbors(const GraphNode* other) const
|
||||
{
|
||||
GraphEdgeArray edgeList = other->getEdges(sEdgeBuff);
|
||||
S32 indexOfThis = getIndex();
|
||||
|
||||
while (GraphEdge * edge = edgeList++)
|
||||
if (edge->mDest == indexOfThis)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
GraphEdge * GraphNode::getEdgeTo(S32 to) const
|
||||
{
|
||||
GraphEdgeArray edgeList = getEdges(sEdgeBuff);
|
||||
|
||||
while (GraphEdge * edge = edgeList++)
|
||||
if (edge->mDest == to)
|
||||
return edge;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Virtual that is only used on non-grid nodes
|
||||
const GraphEdge * GraphNode::getEdgePtr() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// This is only called on nodes known to be terrain.
|
||||
F32 GraphNode::terrHeight() const
|
||||
{
|
||||
return 1e17;
|
||||
}
|
||||
|
||||
// Initial use of this is for weighted random selection of nodes for spawn points.
|
||||
F32 GraphNode::radius() const
|
||||
{
|
||||
return 2.0;
|
||||
}
|
||||
|
||||
// Not in yet, but we'll use this instead of radius for weighted random spawn points
|
||||
F32 GraphNode::area() const
|
||||
{
|
||||
return 0.01;
|
||||
}
|
||||
|
||||
// Used to know thinness of node. Spawn system avoids, as do smoothing edges.
|
||||
F32 GraphNode::minDim() const
|
||||
{
|
||||
return 0.01;
|
||||
}
|
||||
|
||||
const Point3F& GraphNode::getNormal() const
|
||||
{
|
||||
static const Point3F sStraightUp(0,0,1);
|
||||
return sStraightUp;
|
||||
}
|
||||
|
||||
// Used for finding how much a node contains a point. More negative => point is
|
||||
// more inside.
|
||||
NodeProximity GraphNode::containment(const Point3F& point) const
|
||||
{
|
||||
NodeProximity nodeProx;
|
||||
nodeProx.mLateral = (point - location()).len() - radius();
|
||||
return nodeProx;
|
||||
}
|
||||
|
||||
Point3F GraphNode::randomLoc() const
|
||||
{
|
||||
return location();
|
||||
}
|
||||
|
||||
// Nodes which have volumes associated. Added to allow the transient nodes to
|
||||
// respond with the proper volume that they may be associated with.
|
||||
S32 GraphNode::volumeIndex() const
|
||||
{
|
||||
return getIndex();
|
||||
}
|
||||
|
||||
// Set the avoidance if it isn't already. The inmportant flag will override though.
|
||||
void GraphNode::setAvoid(U32 duration, bool isImportant)
|
||||
{
|
||||
U32 curTime = Sim::getCurrentTime();
|
||||
|
||||
if (curTime > mAvoidUntil || isImportant)
|
||||
{
|
||||
mAvoidUntil = curTime + duration;
|
||||
mFlags.set(StuckAvoid, isImportant);
|
||||
|
||||
// On avoidance, also mark neighbors-
|
||||
if (isImportant)
|
||||
{
|
||||
GraphEdgeArray edgeList = getEdges(sEdgeBuff);
|
||||
while (GraphEdge * edge = edgeList++)
|
||||
{
|
||||
GraphNode * node = gNavGraph->lookupNode(edge->mDest);
|
||||
node->mAvoidUntil = mAvoidUntil;
|
||||
node->mFlags.clear(StuckAvoid); // slight avoid here.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
// Default RegularNode Virtuals
|
||||
|
||||
RegularNode::RegularNode()
|
||||
{
|
||||
mIndex = -1;
|
||||
mNormal.set(0,0,1);
|
||||
}
|
||||
|
||||
GraphEdgeArray RegularNode::getEdges(GraphEdge*) const
|
||||
{
|
||||
return GraphEdgeArray(mEdges.size(), mEdges.address());
|
||||
}
|
||||
|
||||
GraphEdge & RegularNode::pushEdge(GraphNode * node)
|
||||
{
|
||||
GraphEdge edge;
|
||||
edge.mDest = node->getIndex();
|
||||
mEdges.push_back(edge);
|
||||
return mEdges.last();
|
||||
}
|
||||
|
||||
const Point3F& RegularNode::getNormal() const
|
||||
{
|
||||
return mNormal;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
void GraphNodeList::setFlags(U32 bits)
|
||||
{
|
||||
for(const_iterator it = begin(); it != end(); it++)
|
||||
if(*it)
|
||||
(*it)->mFlags.set(bits);
|
||||
}
|
||||
|
||||
void GraphNodeList::clearFlags(U32 bits)
|
||||
{
|
||||
for(const_iterator it = begin(); it != end(); it++)
|
||||
if(*it)
|
||||
(*it)->mFlags.clear(bits);
|
||||
}
|
||||
|
||||
S32 GraphNodeList::searchSphere(const SphereF& sphere, const GraphNodeList& listIn)
|
||||
{
|
||||
clear();
|
||||
F32 radiusSquared = (sphere.radius * sphere.radius);
|
||||
for(const_iterator it = listIn.begin(); it != listIn.end(); it++)
|
||||
if(*it)
|
||||
if((sphere.center - (*it)->location()).lenSquared() < radiusSquared)
|
||||
push_back(*it);
|
||||
return size();
|
||||
}
|
||||
|
||||
GraphNode * GraphNodeList::closest(const Point3F& loc, bool)
|
||||
{
|
||||
GraphNode * bestNode = NULL;
|
||||
F32 bestRadSquared = 1e12, dSqrd;
|
||||
for (const_iterator it = begin(); it != end(); it++)
|
||||
if (*it)
|
||||
if ((dSqrd = (loc - (*it)->location()).lenSquared()) < bestRadSquared)
|
||||
{
|
||||
bestRadSquared = dSqrd;
|
||||
bestNode = *it;
|
||||
}
|
||||
return bestNode;
|
||||
}
|
||||
|
||||
// Add the node pointer if it's not already in the list.
|
||||
bool GraphNodeList::addUnique(GraphNode * node)
|
||||
{
|
||||
for (iterator n = begin(); n != end(); n++)
|
||||
if (* n == node)
|
||||
return false;
|
||||
push_back(node);
|
||||
return true;
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue