mirror of
https://github.com/tribes2/engine.git
synced 2026-01-20 03:34:48 +00:00
226 lines
6.1 KiB
C++
226 lines
6.1 KiB
C++
//-----------------------------------------------------------------------------
|
|
// V12 Engine
|
|
//
|
|
// Copyright (c) 2001 GarageGames.Com
|
|
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "ai/graph.h"
|
|
#include "ai/graphLOS.h"
|
|
|
|
U32 Loser::mCasts = 0; // Just for informal profiling, counting casts.
|
|
static const F32 scGraphStepCheck = 0.75;
|
|
|
|
Loser::Loser(U32 mask, bool checkFF)
|
|
: mCheckingFF(checkFF),
|
|
mMask(mask)
|
|
{
|
|
mHitForceField = false;
|
|
}
|
|
|
|
bool Loser::haveLOS(const Point3F& src, const Point3F& dst)
|
|
{
|
|
if (mCheckingFF)
|
|
{
|
|
// Keep track if a force field is encountered. Walkable connections allow,
|
|
// jettable connections can't go through them.
|
|
U32 maskWithFF = (mMask | ForceFieldObjectType);
|
|
mCasts++;
|
|
mColl.object = NULL; // (Not sure if system clears)
|
|
if (gServerContainer.castRay(src, dst, maskWithFF, &mColl))
|
|
{
|
|
if (mColl.object->getTypeMask() & ForceFieldObjectType)
|
|
mHitForceField = true; // and fall through do normal LOS.
|
|
else
|
|
return false;
|
|
}
|
|
}
|
|
mCasts++;
|
|
mColl.object = NULL;
|
|
return !gServerContainer.castRay(src, dst, mMask, &mColl);
|
|
}
|
|
|
|
// Drop the point down to collision (if any) below. If no collision, we don't
|
|
// change the drop point, and return false.
|
|
bool Loser::hitBelow(Point3F& drop, F32 down)
|
|
{
|
|
Point3F below(drop.x, drop.y, drop.z - down);
|
|
if (gServerContainer.castRay(drop, below, mMask, &mColl))
|
|
{
|
|
drop = mColl.point;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Height above, capped at UpHeightMax.
|
|
F32 Loser::heightUp(const Point3F& from, F32 maxUp)
|
|
{
|
|
Point3F up(from.x, from.y, from.z + maxUp);
|
|
if (haveLOS(from, up))
|
|
return maxUp;
|
|
else
|
|
return (mColl.point.z - from.z);
|
|
}
|
|
|
|
// Do LOS fanning one of the points. Just do a bunch of lines. We pre-increment
|
|
// and pre-decrement since the S-to-D los has already been checked.
|
|
bool Loser::fannedLOS1(const Point3F& S, const Point3F& D, const VectorF& inc, S32 N)
|
|
{
|
|
Point3F D0 = D, D1 = D;
|
|
while(N--)
|
|
if(!haveLOS(S, D0 += inc) || !haveLOS(S, D1 -= inc))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
// Do LOS fanning both points.
|
|
bool Loser::fannedLOS2(const Point3F& S, const Point3F& D, const VectorF& inc, S32 N)
|
|
{
|
|
Point3F D0 = D, D1 = D, S0 = S, S1 = S;
|
|
while(N--)
|
|
if(!haveLOS(S0 += inc, D0 += inc) || !haveLOS(S1 -= inc, D1 -= inc))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
// Do LOS checks downward to look for changes greater than step height.
|
|
bool Loser::walkOverBumps(Point3F S, Point3F D, F32 inc)
|
|
{
|
|
VectorF step = (D - S);
|
|
S32 N = getMax(S32(step.len() / inc), 2);
|
|
|
|
// Note D becomes our stepper directly under S.
|
|
F32 downDist = 140;
|
|
(D = S).z -= downDist;
|
|
|
|
if (haveLOS(S, D))
|
|
return false;
|
|
|
|
// Set up initial Z down.
|
|
F32 zdown = mColl.point.z;
|
|
step *= (1.0f / F32(N));
|
|
|
|
while (N--)
|
|
{
|
|
if (haveLOS(S += step, D += step))
|
|
return false;
|
|
|
|
if (mColl.object->getTypeMask() & WaterObjectType)
|
|
return false;
|
|
|
|
F32 getZ = mColl.point.z;
|
|
|
|
if (mFabs(getZ - zdown) > scGraphStepCheck)
|
|
return false;
|
|
|
|
// Save the Z, unless we're not walkable, in which case accumulate.
|
|
//==> Use proper slope values from PlayerData.
|
|
if (mColl.normal.z > gNavGlobs.mWalkableDot)
|
|
zdown = getZ;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Fan the walk - this is SLOW - we'll use less resolution. Note we could try:
|
|
// ==> Walk from outside in (on all fanned checks), which will probably
|
|
// ==> result in earlier exits whenever false is the result.
|
|
// ==> Note we have to profile this whole process.
|
|
bool Loser::fannedBumpWalk(const Point3F& S, const Point3F& D, VectorF inc, S32 N)
|
|
{
|
|
Point3F D0 = D, D1 = D, S0 = S, S1 = S;
|
|
inc *= 2.0f;
|
|
while ((N -= 2) >= 0)
|
|
if (!walkOverBumps(S0 += inc, D0 += inc) || !walkOverBumps(S1 -= inc, D1 -= inc))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
// (If both are outdoor, we'll look for a larger hop amount).
|
|
static const F32 sHopLookInc = 0.2;
|
|
|
|
// Move lines up, looking for a free line. We also want to assure that while there's
|
|
// not a free line, we at least have a minimum amount of breathing room - about 1.5m.
|
|
bool Loser::findHopLine(Point3F S, Point3F D, F32 maxUp, F32& freeHt)
|
|
{
|
|
F32 tThresh = (1.5f / (S - D).len());
|
|
|
|
F32 total = 0;
|
|
while (total < maxUp)
|
|
{
|
|
S.z += sHopLookInc;
|
|
D.z += sHopLookInc;
|
|
total += sHopLookInc;
|
|
if (haveLOS(S, D))
|
|
{
|
|
freeHt = total;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
if (mColl.t < tThresh) // look for minimum amount in
|
|
break;
|
|
haveLOS(D, S); // ... from both directions
|
|
if (mColl.t < tThresh)
|
|
break;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#define GapWalkInc 0.15f
|
|
|
|
bool Loser::walkOverGaps(Point3F S, Point3F D, F32 allowedGap)
|
|
{
|
|
VectorF incVec = (D - S);
|
|
VectorF step2D(incVec.x, incVec.y, 0);
|
|
F32 inc2D = step2D.len();
|
|
S32 N = getMax(S32(inc2D / GapWalkInc) + 1, 2);
|
|
F32 bandWid = gNavGlobs.mStepHeight;
|
|
F32 saveL = 0, total2D = 0;
|
|
bool wasBelow = false;
|
|
|
|
// D serves as our stepper to do LOS down to
|
|
(D = S).z -= 100;
|
|
incVec *= (1.0f / F32(N));
|
|
inc2D *= (1.0f / F32(N));
|
|
|
|
while (N--)
|
|
{
|
|
bool los = !gServerContainer.castRay(S, D, mMask, &mColl);
|
|
|
|
// Must handle water-
|
|
if (!los)
|
|
if (mColl.object->getTypeMask() & WaterObjectType)
|
|
return false;
|
|
|
|
// check if we're below the allowed band-
|
|
if (los || mColl.point.z < (S.z - bandWid))
|
|
{
|
|
if (wasBelow)
|
|
{
|
|
if (total2D - saveL > allowedGap)
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
wasBelow = true;
|
|
saveL = total2D;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
wasBelow = false;
|
|
saveL = total2D;
|
|
}
|
|
|
|
S += incVec;
|
|
D += incVec;
|
|
total2D += inc2D;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|