openmohaa/code/game/b_nav.cpp

633 lines
14 KiB
C++
Raw Normal View History

2016-03-27 11:49:47 +02:00
//
// b_nav.c
//
//FIXME make botInfo, etc visible here too and get rid of all the mutliple dereferences like bot->bot->
#include "b_local.h"
#define NAVF_EDGEZONE 0x00000001
#define INFINITE 1000000
#define BOTAI_PUSHED (1<<0)
cvar_t *nav_showsectors;
char *navFileData;
int surfaceCount;
nsurface_t *surface;
int neighborCount;
nneighbor_t *neighbor;
byte *route;
//#if 0
static int spawnpadModelIndex;
int Nav_SurfaceUnderPlayer( gentity_t *player ) {
vec3_t start;
vec3_t end;
vec3_t p;
trace_t tr;
float bestDist;
int bestSurf;
vec3_t v;
int n;
float dist;
VectorCopy( player->s.origin, start );
VectorCopy( player->s.origin, end );
end[2] -= 4096;
gi.trace ( &tr, start, player->mins, player->maxs, end, player->s.number, MASK_DEADSOLID, true );
// p[0] = ((int)tr.endpos[0] + 8) & (~16);
// p[1] = ((int)tr.endpos[1] + 8) & (~16);
p[0] = tr.endpos[0];
p[1] = tr.endpos[1];
p[2] = floor(tr.endpos[2]+player->mins[2]);
bestDist = INFINITE;
bestSurf = -1;
for ( n = 0; n < surfaceCount; n++ ) {
if ( Q_fabs( surface[n].origin[2] - p[2] ) > 24 ) {
continue;
}
VectorSubtract( p, surface[n].origin, v );
dist = VectorLength( v );
if ( dist < bestDist ) {
bestDist = dist;
bestSurf = n;
}
if ( surface[n].origin[2] != p[2] ) {
continue;
}
if ( surface[n].absmin[0] > p[0] ) {
continue;
}
if ( surface[n].absmax[0] < p[0] ) {
continue;
}
if ( surface[n].absmin[1] > p[1] ) {
continue;
}
if ( surface[n].absmax[1] < p[1] ) {
continue;
}
return n;
}
// gi.Printf( "guess for %s at %s\n", ent->classname, vtos( p ) );
return bestSurf;
}
/*
=============
Nav_GroundSurfaceNumber
Returns the surface number for where an entity is currently located.
If the entity is not on the ground, it returns -1.
FIXME we can make this more efficient
right now surfaces are in Z sorted order
we could make a Z index and binary search it to get to right z group fast
=============
*/
int Nav_GroundSurfaceNumber( gentity_t *ent ) {
vec3_t p;
vec3_t v;
int n;
float dist;
float bestDist;
int bestSurf;
gentity_t *groundEntity;
// if ent is not on the ground it is not on a surface
if ( ent->s.groundEntityNum == -1 ) {
return -1;
}
// p[0] = ((int)ent->s.origin[0] + 8) & (~16);
// p[1] = ((int)ent->s.origin[1] + 8) & (~16);
p[0] = ent->s.origin[0];
p[1] = ent->s.origin[1];
p[2] = floor(ent->s.origin[2]+ent->mins[2]);
// if ground is not the world we need to handle it differently.
if ( ent->s.groundEntityNum != ENTITYNUM_WORLD ) {
groundEntity = &g_entities[ent->s.groundEntityNum];
// check for sitting on a spawn pad
if ( !groundEntity->bmodel && groundEntity->s.modelindex == spawnpadModelIndex ) {
p[2] -= 8.0;
}
// check for plats
/* else if ( groundEntity->bmodel && Q_stricmp( groundEntity->classname, "func_plat" ) == 0 ) {
// if at the top the return PLATHIGH surface number, otherwise return PLATLOW surface number
if ( VectorCompare( groundEntity->currentOrigin, groundEntity->pos2 ) ) {
return surface[groundEntity->navSurfaceNum].parm;
}
return groundEntity->navSurfaceNum;
} */
}
bestDist = INFINITE;
bestSurf = -1;
for ( n = 0; n < surfaceCount; n++ ) {
if ( Q_fabs( surface[n].origin[2] - p[2] ) > 24 ) {
continue;
}
VectorSubtract( p, surface[n].origin, v );
dist = VectorLength( v );
if ( dist < bestDist ) {
bestDist = dist;
bestSurf = n;
}
if ( surface[n].origin[2] != p[2] ) {
continue;
}
if ( surface[n].absmin[0] > p[0] ) {
continue;
}
if ( surface[n].absmax[0] < p[0] ) {
continue;
}
if ( surface[n].absmin[1] > p[1] ) {
continue;
}
if ( surface[n].absmax[1] < p[1] ) {
continue;
}
return n;
}
// gi.Printf( "guess for %s at %s\n", ent->classname, vtos( p ) );
return bestSurf;
}
/*
=============
Nav_ItemSurfaceNumber
Returns the surface number for where an item entity is currently located.
If the entity is not on the ground, it returns -1. This is a specialized
copy of Nav_GroundSurfaceNumber for items that caches the result.
=============
*/
int Nav_ItemSurfaceNumber( gentity_t *ent ) {
if ( !VectorCompare( ent->s.origin, ent->navOrigin ) && level.time > ent->navTime ) {
VectorCopy( ent->s.origin, ent->navOrigin );
ent->navTime = level.time;
ent->navSurfaceNum = Nav_GroundSurfaceNumber( ent );
}
return ent->navSurfaceNum;
}
/*
=============
Nav_EntitySurfaceNumber
=============
*/
int Nav_EntitySurfaceNumber( gentity_t *ent ) {
if ( ent->s.eType == ET_ITEM ) {
return Nav_ItemSurfaceNumber( ent );
}
/*if ( ent->classname&& strcmp( ent->classname, "bot_goal" ) == 0 ) {
return Nav_SurfaceUnderPlayer( ent );
}*/
return Nav_GroundSurfaceNumber( ent );
}
/*
=============
PathEdge
=============
*/
static nneighbor_t *PathEdge( int sourceSurfNum, int destSurfNum ) {
int base;
int n;
base = surface[sourceSurfNum].neighborIndex;
for ( n = 0; n < surface[sourceSurfNum].neighborCount; n++ ) {
if ( neighbor[base+n].surfaceNum == destSurfNum ) {
return &neighbor[base+n];
}
}
return NULL;
}
/*
=============
PointIsInEdgeRegion
=============
*/
static qboolean PointIsInEdgeRegion( vec3_t p, nneighbor_t *n ) {
if ( p[0] < n->absmin[0] ) {
return qfalse;
}
if ( p[0] > n->absmax[0] ) {
return qfalse;
}
if ( p[1] < n->absmin[1] ) {
return qfalse;
}
if ( p[1] > n->absmax[1] ) {
return qfalse;
}
return qtrue;
}
/*
=============
Nav_MoveToGoal
=============
*/
int Nav_MoveToGoal( gentity_t *bot, vec3_t dir, int *flags ) {
int currentSurf;
int nextSurf;
int thirdSurf;
int goalSurf;
int routeIndex;
nneighbor_t *edge;
nneighbor_t *nextEdge;
//gentity_t *ent;
//float dist;
*flags = 0;
VectorCopy( bot->navDir, dir );
currentSurf = bot->currentSurface;
// if bot is airborne, just keep heading the same
if ( currentSurf == -1 ) {
if ( bot->aiFlags & BOTAI_PUSHED ) {
//gi.Printf( "bot was bounced\n" );
*flags |= NAVF_HOLD;
}
//gi.Printf( "not on ground\n" );
return 0;
}
if ( bot->pushedTime < level.time ) {
bot->aiFlags &= ~BOTAI_PUSHED;
}
if ( !bot->goalEntity ) {
// gi.Printf( ERROR "Nav_MoveToGoal called with no goalEntity set\n" );
return -1;
}
goalSurf = Nav_EntitySurfaceNumber( bot->goalEntity );
if ( goalSurf == -1 ) {
return -1;
}
// if we've changed surfaces, the surface to surface navigation flags and timer need to be cleared
if ( currentSurf != bot->lastSurface ) {
bot->navFlags = 0;
bot->navTime = 0;
//gi.Printf( "surface changed from %i to %i\n", bot->bot->lastSurface, bot->bot->currentSurface );
}
if ( currentSurf == goalSurf ) {
//gi.Printf( "On target surface\n" );
VectorSubtract( bot->goalEntity->s.origin, bot->s.origin, dir );
//VectorCopy( dir, bot->bot->navDir );
VectorCopy( dir, bot->navDir );
return 0;
}
routeIndex = route[currentSurf * surfaceCount + goalSurf];
if ( routeIndex == 255 ) {
//gi.Printf( "Nav_MoveToGoal - no known route from %i to %i\n", currentSurf, goalSurf );
return -1;
}
if ( routeIndex >= surface[currentSurf].neighborCount ) {
gi.Printf( ERROR "Nav_MoveToGoal - bad routeIndex\n" );
return -1;
}
nextSurf = neighbor[surface[currentSurf].neighborIndex + routeIndex].surfaceNum;
edge = PathEdge( currentSurf, nextSurf );
if ( !edge ) {
gi.Printf( ERROR "Nav_MoveToGoal - %i does not have %i as a neighbor\n", currentSurf, nextSurf );
VectorClear( dir );
return -1;
}
if ( ! ( bot->navFlags & NAVF_EDGEZONE ) ) {
if ( PointIsInEdgeRegion( bot->s.origin, edge ) ) {
//gi.Printf( "hit edge\n" );
bot->navFlags |= NAVF_EDGEZONE;
bot->navTime = level.time;
}
}
// if we are in the edge zone
if ( bot->navFlags & NAVF_EDGEZONE ) {
// if we're trying to get onto a plat, we must make sure it's there
/*if ( surface[nextSurf].flags & SF_PLATLOW ) {
ent = &g_entities[surface[surface[nextSurf].parm].parm]; //FIXME this works for now, but I don't like it
if ( VectorCompare( ent->currentOrigin, ent->pos1 ) == 0 ) {
*flags |= NAVF_HOLD;
//gi.Printf(" wait for plat2\n" );
}
}*/
// if we're riding up on a plat, we don't need to move
if ( surface[nextSurf].flags & SF_PLATHIGH ) {
*flags |= NAVF_HOLD;
//gi.Printf(" hold on plat\n" );
}
// if the next surface contains the goalEntity, head towards it
if ( nextSurf == goalSurf ) {
//gi.Printf( "next surf has goal - targeting directly\n" );
VectorSubtract( bot->goalEntity->s.origin, bot->s.origin, dir );
//VectorCopy( dir, bot->bot->navDir );
VectorCopy( dir, bot->navDir );
}
// start heading towards the next edge
else {
routeIndex = route[nextSurf * surfaceCount + goalSurf];
if ( routeIndex == 255 ) {
gi.Printf( ERROR "Nav_MoveToGoal - no known route from %i to %i\n", nextSurf, goalSurf );
return -1;
}
if ( routeIndex >= surface[nextSurf].neighborCount ) {
gi.Printf( ERROR "Nav_MoveToGoal - bad routeIndex\n" );
return -1;
}
thirdSurf = neighbor[surface[nextSurf].neighborIndex + routeIndex].surfaceNum;
nextEdge = PathEdge( nextSurf, thirdSurf );
if ( !nextEdge ) {
gi.Printf( ERROR "Nav_MoveToGoal - %i does not have %i as a neighbor\n", nextSurf, thirdSurf );
VectorClear( dir );
return -1;
}
//gi.Printf( "targeting next edge\n" );
if ( surface[nextSurf].flags & SF_PLATHIGH ) {
VectorSubtract( nextEdge->origin, surface[nextSurf].origin, dir );
}
else {
VectorSubtract( nextEdge->origin, bot->s.origin, dir );
}
//VectorCopy( dir, bot->bot->navDir );
VectorCopy( dir, bot->navDir );
}
if ( edge->flags & NF_DUCK ) {
*flags |= NAVF_DUCK;
}
if ( edge->flags & NF_JUMP ) {
*flags |= NAVF_JUMP;
}
}
else {
VectorSubtract( edge->origin, bot->s.origin, dir );
//VectorCopy( dir, bot->bot->navDir );
VectorCopy( dir, bot->navDir );
/*if ( surface[nextSurf].flags & SF_PLATLOW ) {
ent = &g_entities[surface[surface[nextSurf].parm].parm]; //FIXME this works for now, but I don't like it
if ( VectorCompare( ent->currentOrigin, ent->pos1 ) == 0 ) {
dist = VectorLength( dir );
if ( dist > 64 ) {
*flags |= NAVF_SLOW;
//gi.Printf(" slow for plat\n" );
}
else {
*flags |= NAVF_HOLD;
//gi.Printf(" wait for plat\n" );
}
}
}*/
}
return 0;
}
void Nav_ShowPath( gentity_t *bot ) {
#if 0
gentity_t *tent;
int m, n;
if ( !bot->bot->goalEntity ) {
return;
}
tent = G_TempEntity( bot->s.origin, EV_DEBUG_LINE );
VectorCopy( bot->bot->currentWaypoint->s.origin, tent->s.origin2 );
m = bot->bot->currentWaypoint->count;
for (;;) {
if ( m == bot->bot->finalWaypoint->count ) {
break;
}
n = route[m*maxwaypoints+bot->bot->finalWaypoint->count];
if ( n == -1 ) {
break;
}
tent = G_TempEntity( rents[m]->s.origin, EV_DEBUG_LINE );
VectorCopy( rents[n]->s.origin, tent->s.origin2 );
m = n;
}
if ( bot->bot->finalWaypoint != bot->bot->goalEntity ) {
tent = G_TempEntity( bot->bot->finalWaypoint->s.origin, EV_DEBUG_LINE );
VectorCopy( bot->bot->goalEntity->s.origin, tent->s.origin2 );
}
#endif
/* gentity_t *tent;
int m, n;
gentity_t *player;
int pSurf;
player = &g_entities[1];
pSurf = Nav_GroundSurfaceNumber( player );
tent = G_TempEntity( player->s.origin, EV_DEBUG_LINE );
VectorCopy( surface[pSurf].origin, tent->s.origin2 ); */
}
//#endif
/*
// Init and Shutdown
//
//
*/
static void Nav_Cleanup( void ) {
if ( navFileData ) {
gi.FS_FreeFile ( navFileData );
navFileData = NULL;
}
surfaceCount = 0;
neighborCount = 0;
}
void Nav_InitPreSpawn( void ) {
nav_showsectors = gi.cvar( "nav_showsectors", "0", 0 );
Nav_Cleanup();
Nav_LoadRoutes();
}
void Nav_InitPostSpawn( void ) {
#if 0
int n;
nsurface_t *s;
gentity_t *ent;
// FIXME resolve targetnames here (like button needed to open a door)
// get the modelindex for the spawnpad model so we can use it for surface determination
spawnpadModelIndex = G_ModelIndex( "models/objects/dmspot.md3" );
// set the navSurface for plats
for ( n = 0; n < surfaceCount; n++ ) {
s = &surface[n];
if ( s->flags & SF_PLATLOW ) {
ent = &g_entities[surface[s->parm].parm]; //FIXME this works for now, but I don't like it
ent->navSurfaceNum = n;
}
}
#endif
}
void Nav_Shutdown ( void ) {
Nav_Cleanup();
}
void Nav_Test_f( void ) {
#if 0
gentity_t *player;
gentity_t *goal;
char *goalname;
int pSurf;
int gSurf;
int cSurf;
int n;
gentity_t *tent;
player = &g_entities[0];
pSurf = Nav_GroundSurfaceNumber( player );
goalname = gi.argv(2);
if ( !goalname[0] ) {
gi.Printf( "Player1 is at (%f, %f, %f) on surface %i\n", player->s.origin[0], player->s.origin[1], player->s.origin[2] + player->mins[2], pSurf );
return;
}
goal = NULL;
goal = G_Find( goal, FOFS( classname ), goalname );
if ( !goal ) {
gi.Printf( "no %s on level\n", goalname );
return;
}
gSurf = Nav_EntitySurfaceNumber( goal );
cSurf = pSurf;
while ( cSurf != gSurf ) {
n = route[cSurf * surfaceCount + gSurf];
if ( n == 255 ) {
//gi.Printf( "no known route from %i to %i\n", cSurf, gSurf );
return;
}
if ( n >= surface[cSurf].neighborCount ) {
gi.Printf( ERROR "bad routeIndex\n" );
return;
}
n = neighbor[surface[cSurf].neighborIndex + n].surfaceNum;
if ( cSurf == pSurf ) {
tent = G_TempEntity( player->s.origin, EV_DEBUG_LINE );
}
else {
tent = G_TempEntity( surface[cSurf].origin, EV_DEBUG_LINE );
}
if ( n == gSurf ) {
VectorCopy( goal->s.origin, tent->s.origin2 );
}
else {
VectorCopy( surface[n].origin, tent->s.origin2 );
}
cSurf = n;
}
#endif
}
void Nav_Gen_f( void );
void Cmd_Nav_f( void )
{
char *cmd;
cmd = gi.argv(1);
if ( Q_stricmp ( cmd, "gen" ) == 0 ) {
Nav_Gen_f();
Nav_InitPreSpawn();
}
else if ( Q_stricmp ( cmd, "test" ) == 0 ) {
Nav_Test_f();
}
else {
gi.Printf("Unknown nav command '%s'\n", cmd);
}
}
void Nav_ShowStuff
(
void
)
{
int i;
nsurface_t *surf;
if ( !nav_showsectors->integer )
{
return;
}
G_Color4f( 1, 1, 0, 1 );
for( i = 0; i < surfaceCount; i++ )
{
surf = &surface[ i ];
G_BeginLine();
G_Vertex( Vector( surf->absmin[ 0 ], surf->absmin[ 1 ], surf->origin[ 2 ] ) );
G_Vertex( Vector( surf->absmin[ 0 ], surf->absmax[ 1 ], surf->origin[ 2 ] ) );
G_Vertex( Vector( surf->absmax[ 0 ], surf->absmax[ 1 ], surf->origin[ 2 ] ) );
G_Vertex( Vector( surf->absmax[ 0 ], surf->absmin[ 1 ], surf->origin[ 2 ] ) );
G_Vertex( Vector( surf->absmin[ 0 ], surf->absmin[ 1 ], surf->origin[ 2 ] ) );
G_EndLine();
}
}