📄 navigate.cpp
字号:
Vector max;
cell = GetNodesInCell( pos );
if ( !cell )
{
return NULL;
}
if ( ent && usebbox )
{
min = ent->mins;
max = ent->maxs;
}
else
{
min = Vector( -16, -16, 12 );
max = Vector( 16, 16, 40 );
}
n = cell->NumNodes();
if ( ai_debugpath->value )
{
gi.dprintf( "NearestNode: Checking %d nodes\n", n );
}
bestnode = NULL;
bestdist = 999999999; // greater than ( 8192 * sqr(2) ) ^ 2 -- maximum squared distance
for( i = 0; i < n; i++ )
{
node = ( PathNode * )cell->GetNode( i );
if ( !node )
{
continue;
}
delta = node->worldorigin - pos;
// get the distance squared (faster than getting real distance)
dist = delta * delta;
if ( ( dist < bestdist ) && node->CheckMove( ent, pos, min, max, false, false ) )
{
bestnode = node;
bestdist = dist;
// if we're close enough, early exit
if ( dist < 16 )
{
break;
}
}
}
return bestnode;
}
EXPORT_FROM_DLL void PathSearch::Teleport
(
Entity *teleportee,
Vector from,
Vector to
)
{
PathNode *node1;
PathNode *node2;
byte maxheight[ NUM_WIDTH_VALUES ];
int j;
if ( ai_createnodes->value )
{
node1 = new PathNode;
node1->Setup( from );
node2 = new PathNode;
node2->Setup( to );
// FIXME
// shouldn't hard-code width and height
for( j = 0; j < NUM_WIDTH_VALUES; j++ )
{
maxheight[ j ] = 72;
}
// connect with 0 cost
node1->ConnectTo( node2, maxheight, 0 );
}
}
EXPORT_FROM_DLL void PathSearch::ShowNodes
(
void
)
{
if ( ai_showroutes->value || ai_shownodenums->value )
{
DrawAllConnections();
}
}
EXPORT_FROM_DLL int PathSearch::NumNodes
(
void
)
{
return numNodes;
}
EXPORT_FROM_DLL void PathSearch::Archive
(
Archiver &arc
)
{
PathNode *node;
int i;
int num;
num = 0;
for( i = 0; i < MAX_PATHNODES; i++ )
{
node = AI_GetNode( i );
if ( node )
{
num++;
}
}
arc.WriteInteger( num );
for( i = 0; i < MAX_PATHNODES; i++ )
{
node = AI_GetNode( i );
if ( node )
{
arc.WriteObject( node );
}
}
if ( ai_debuginfo->value )
{
gi.dprintf( "Wrote %d path nodes\n", num );
}
}
EXPORT_FROM_DLL void PathSearch::ClearNodes
(
Event *ev
)
{
PathNode *node;
int i;
int num;
num = 0;
for( i = 0; i < MAX_PATHNODES; i++ )
{
node = AI_GetNode( i );
if ( node )
{
node->PostEvent( EV_Remove, 0 );
num++;
}
}
if ( ai_debuginfo->value )
{
gi.dprintf( "Deleted %d path nodes\n", num );
}
}
EXPORT_FROM_DLL void PathSearch::Unarchive
(
Archiver &arc
)
{
int num;
int i;
int x;
int y;
numNodes = 0;
NodeList = NULL;
loadingarchive = true;
// Get rid of the nodes that were spawned by the map
AI_ResetNodes();
// Init the grid
for( x = 0; x < PATHMAP_GRIDSIZE; x++ )
{
for( y = 0; y < PATHMAP_GRIDSIZE; y++ )
{
PathMap[ x ][ y ].Init();
}
}
num = arc.ReadInteger();
assert( num <= MAX_PATHNODES );
if ( num > MAX_PATHNODES )
{
arc.FileError( "Exceeded max path nodes" );
}
for( i = 0; i < num; i++ )
{
arc.ReadObject();
}
if ( ai_debuginfo->value )
{
gi.dprintf( "Path nodes loaded: %d\n", NumNodes() );
}
loadingarchive = false;
}
EXPORT_FROM_DLL void PathSearch::SetNodeFlagsEvent
(
Event *ev
)
{
const char * token;
int i, argnum, flags;
int mask;
int action;
int nodenum;
PathNode *node;
#define FLAG_IGNORE 0
#define FLAG_SET 1
#define FLAG_CLEAR 2
#define FLAG_ADD 3
nodenum = ev->GetInteger( 1 );
node = AI_GetNode( nodenum );
if ( !node )
{
ev->Error( "Node not found." );
return;
}
flags = 0;
argnum = 2;
for ( i = argnum; i <= ev->NumArgs() ; i++ )
{
token = ev->GetString( i );
action = 0;
switch( token[0] )
{
case '+':
action = FLAG_ADD;
token++;
break;
case '-':
action = FLAG_CLEAR;
token++;
break;
default:
action = FLAG_SET;
break;
}
if (!strcmpi( token, "flee"))
{
mask = AI_FLEE;
}
else if (!strcmpi (token, "duck"))
{
mask = AI_DUCK;
}
else if (!strcmpi (token, "cover"))
{
mask = AI_COVER;
}
else if (!strcmpi (token, "door"))
{
mask = AI_DOOR;
}
else if (!strcmpi (token, "jump"))
{
mask = AI_JUMP;
}
else if (!strcmpi (token, "ladder"))
{
mask = AI_LADDER;
}
else if (!strcmpi (token, "action"))
{
mask = AI_ACTION;
}
else
{
action = FLAG_IGNORE;
ev->Error( "Unknown token %s.", token );
}
switch (action)
{
case FLAG_SET:
node->nodeflags = 0;
case FLAG_ADD:
node->nodeflags |= mask;
break;
case FLAG_CLEAR:
node->nodeflags &= ~mask;
break;
case FLAG_IGNORE:
break;
}
}
}
EXPORT_FROM_DLL void PathSearch::CalcPathEvent
(
Event *ev
)
{
int nodenum;
PathNode *node;
PathNode *node2;
int j;
byte maxheight[ NUM_WIDTH_VALUES ];
nodenum = ev->GetInteger( 1 );
node = AI_GetNode( nodenum );
nodenum = ev->GetInteger( 2 );
node2 = AI_GetNode( nodenum );
if ( !node || !node2 )
{
ev->Error( "Node not found." );
return;
}
if ( ( node->numChildren < NUM_PATHSPERNODE ) && !node->ConnectedTo( node2 ) )
{
if ( node->ClearPathTo( node2, maxheight, false ) || node->LadderTo( node2, maxheight ) )
{
node->ConnectTo( node2, maxheight );
}
else if ( ( node->nodeflags & AI_JUMP ) && ( node->target == node2->targetname ) )
{
//FIXME
// don't hardcode size
for( j = 0; j < NUM_WIDTH_VALUES; j++ )
{
maxheight[ j ] = MAX_HEIGHT;
}
node->ConnectTo( node2, maxheight );
}
}
if ( ( node2->numChildren < NUM_PATHSPERNODE ) && !node2->ConnectedTo( node ) )
{
if ( node2->ClearPathTo( node, maxheight, false ) || node2->LadderTo( node, maxheight ) )
{
node2->ConnectTo( node, maxheight );
}
else if ( ( node2->nodeflags & AI_JUMP ) && ( node2->target == node->targetname ) )
{
//FIXME
// don't hardcode size
for( j = 0; j < NUM_WIDTH_VALUES; j++ )
{
maxheight[ j ] = MAX_HEIGHT;
}
node2->ConnectTo( node, maxheight );
}
}
}
EXPORT_FROM_DLL void PathSearch::DisconnectPathEvent
(
Event *ev
)
{
int nodenum;
PathNode *node;
PathNode *node2;
nodenum = ev->GetInteger( 1 );
node = AI_GetNode( nodenum );
nodenum = ev->GetInteger( 2 );
node2 = AI_GetNode( nodenum );
if ( !node || !node2 )
{
ev->Error( "Node not found." );
return;
}
if ( node->ConnectedTo( node2 ) )
{
node->Disconnect( node2 );
}
if ( node2->ConnectedTo( node ) )
{
node2->Disconnect( node );
}
}
EXPORT_FROM_DLL void PathSearch::RecalcPathsEvent
(
Event *ev
)
{
int nodenum;
PathNode *node;
nodenum = ev->GetInteger( 1 );
node = AI_GetNode( nodenum );
if ( node )
{
UpdateNode( node );
}
else
{
ev->Error( "Node not found." );
}
}
EXPORT_FROM_DLL void PathSearch::SaveNodes
(
Event *ev
)
{
Archiver arc;
str name;
if ( ev->NumArgs() != 1 )
{
gi.printf( "Usage: ai_save [filename]\n" );
return;
}
name = ev->GetString( 1 );
gi.printf( "Archiving\n" );
arc.Create( name );
arc.WriteInteger( PATHFILE_VERSION );
arc.WriteObject( this );
arc.Close();
gi.printf( "done.\n" );
}
EXPORT_FROM_DLL void PathSearch::LoadNodes
(
Event *ev
)
{
Archiver arc;
str name;
int version;
if ( ev->NumArgs() != 1 )
{
gi.printf( "Usage: ai_load [filename]\n" );
return;
}
gi.printf( "Loading nodes...\n" );
name = ev->GetString( 1 );
arc.Read( name );
version = arc.ReadInteger();
if ( version == PATHFILE_VERSION )
{
arc.ReadObject( this );
arc.Close();
gi.printf( "done.\n" );
}
else
{
arc.Close();
gi.printf( "Expecting version %d path file. Path file is version %d.", PATHFILE_VERSION, version );
// Only replace the file if this event was called from our init function (as opposed to the user
// calling us from the console) and the version number is older than our current version.
if ( ( ev->GetSource() == EV_FROM_CODE ) && ( version < PATHFILE_VERSION ) )
{
gi.printf( "Replacing file.\n\n" );
// At this point, the nodes are still scheduled to find their neighbors, because we posted this event
// before we the nodes were spawned. Post the event with 0 delay so that it gets processed after all
// the nodes find their neighbors.
PostEvent( EV_AI_SavePaths, 0 );
}
else
{
// otherwise, just let them know that the path file needs to be replaced.
gi.printf( "Type 'ai_savepaths' at the console to replace the current path file.\n" );
}
// Print out something fairly obvious
gi.dprintf( "***********************************\n"
"***********************************\n"
"\n"
"Creating paths...\n"
"\n"
"***********************************\n"
"***********************************\n" );
}
}
EXPORT_FROM_DLL void PathSearch::SavePaths
(
void
)
{
str filename;
Event *ev;
if ( loadingarchive )
{
// force it to zero since we probably had an error
gi.cvar_set( "ai_createnodes", "0" );
}
if ( !loadingarchive && ai_createnodes && ai_createnodes->value )
{
filename = gi.GameDir();
filename += "/maps/";
filename += level.mapname;
filename += ".pth";
gi.dprintf( "Saving path nodes to '%s'\n", filename.c_str() );
ev = new Event( EV_AI_SaveNodes );
ev->AddString( filename );
ProcessEvent( ev );
}
}
EXPORT_FROM_DLL void PathSearch::SavePathsEvent
(
Event *ev
)
{
str temp;
temp = ai_createnodes->string;
gi.cvar_set( "ai_createnodes", "1" );
SavePaths();
gi.cvar_set( "ai_createnodes", temp.c_str() );
}
EXPORT_FROM_DLL void PathSearch::Init
(
const char *mapname
)
{
int x;
int y;
str filename;
Event *ev;
ai_createnodes = gi.cvar ("ai_createnodes", "0", 0);
ai_showpath = gi.cvar ("ai_showpath", "0", 0);
ai_debugpath = gi.cvar ("ai_debugpath", "0", 0);
ai_debuginfo = gi.cvar ("ai_debuginfo", "0", 0);
ai_showroutes = gi.cvar ("ai_showroutes", "0", 0);
ai_shownodenums = gi.cvar ("ai_shownodenums", "0", 0);
ai_timepaths = gi.cvar ("ai_timepaths", "0", 0);
numNodes = 0;
NodeList = NULL;
loadingarchive = false;
// Get rid of the nodes that were spawned by the map
AI_ResetNodes();
// Init the grid
for( x = 0; x < PATHMAP_GRIDSIZE; x++ )
{
for( y = 0; y < PATHMAP_GRIDSIZE; y++ )
{
PathMap[ x ][ y ].Init();
}
}
if ( mapname )
{
filename = "maps/";
filename += mapname;
filename += ".pth";
if ( gi.LoadFile( filename.c_str(), NULL, 0 ) != -1 )
{
ev = new Event( EV_AI_LoadNodes );
ev->AddString( filename );
// This can't happen until the world is spawned
PostEvent( ev, 0 );
}
else
{
// Print out something fairly obvious
gi.dprintf( "***********************************\n"
"***********************************\n"
"\n"
"No paths found. Creating paths...\n"
"\n"
"***********************************\n"
"***********************************\n" );
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -