mirror of
https://github.com/ENSL/NS.git
synced 2024-11-24 05:21:16 +00:00
73a03548a7
git-svn-id: https://unknownworlds.svn.cloudforge.com/ns1@369 67975925-1194-0748-b3d5-c16f83f1a3a1
1938 lines
52 KiB
C++
1938 lines
52 KiB
C++
// HPB bot - botman's High Ping Bastard bot
|
|
//
|
|
// (http://planethalflife.com/botman/)
|
|
//
|
|
// waypoint.cpp
|
|
//
|
|
|
|
#ifndef __linux__
|
|
#include <io.h>
|
|
#endif
|
|
#include <fcntl.h>
|
|
#ifndef __linux__
|
|
#include <sys\stat.h>
|
|
#else
|
|
#include <sys/stat.h>
|
|
#endif
|
|
|
|
#include "dlls/extdll.h"
|
|
#include "dlls/enginecallback.h"
|
|
#include "dlls/util.h"
|
|
#include "dlls/cbase.h"
|
|
|
|
#include "bot.h"
|
|
#include "waypoint.h"
|
|
//#include "mod/NetworkMeter.h"
|
|
|
|
extern int mod_id;
|
|
extern int m_spriteTexture;
|
|
|
|
// waypoints with information bits (flags)
|
|
WAYPOINT waypoints[MAX_WAYPOINTS];
|
|
|
|
// number of waypoints currently in use
|
|
int num_waypoints;
|
|
|
|
// declare the array of head pointers to the path structures...
|
|
PATH *paths[MAX_WAYPOINTS];
|
|
|
|
// time that this waypoint was displayed (while editing)
|
|
float wp_display_time[MAX_WAYPOINTS];
|
|
|
|
bool g_waypoint_paths = FALSE; // have any paths been allocated?
|
|
bool g_waypoint_on = FALSE;
|
|
bool g_auto_waypoint = FALSE;
|
|
bool g_path_waypoint = FALSE;
|
|
Vector last_waypoint;
|
|
float f_path_time = 0.0;
|
|
|
|
unsigned int route_num_waypoints;
|
|
unsigned short *shortest_path[4] = {NULL, NULL, NULL, NULL};
|
|
unsigned short *from_to[4] = {NULL, NULL, NULL, NULL};
|
|
|
|
static FILE *fp;
|
|
|
|
|
|
void WaypointDebug(void)
|
|
{
|
|
int y = 1, x = 1;
|
|
|
|
fp=fopen("bot.txt","a");
|
|
fprintf(fp,"WaypointDebug: LINKED LIST ERROR!!!\n");
|
|
fclose(fp);
|
|
|
|
x = x - 1; // x is zero
|
|
y = y / x; // cause an divide by zero exception
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
// free the linked list of waypoint path nodes...
|
|
void WaypointFree(void)
|
|
{
|
|
for (int i=0; i < MAX_WAYPOINTS; i++)
|
|
{
|
|
int count = 0;
|
|
|
|
if (paths[i])
|
|
{
|
|
PATH *p = paths[i];
|
|
PATH *p_next;
|
|
|
|
while (p) // free the linked list
|
|
{
|
|
p_next = p->next; // save the link to next
|
|
free(p);
|
|
p = p_next;
|
|
|
|
#ifdef _DEBUG
|
|
count++;
|
|
if (count > 1000) WaypointDebug();
|
|
#endif
|
|
}
|
|
|
|
paths[i] = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// initialize the waypoint structures...
|
|
void WaypointInit(void)
|
|
{
|
|
int i;
|
|
|
|
// have any waypoint path nodes been allocated yet?
|
|
if (g_waypoint_paths)
|
|
WaypointFree(); // must free previously allocated path memory
|
|
|
|
for (i=0; i < 4; i++)
|
|
{
|
|
if (shortest_path[i] != NULL)
|
|
free(shortest_path[i]);
|
|
|
|
if (from_to[i] != NULL)
|
|
free(from_to[i]);
|
|
}
|
|
|
|
for (i=0; i < MAX_WAYPOINTS; i++)
|
|
{
|
|
waypoints[i].flags = 0;
|
|
waypoints[i].origin = Vector(0,0,0);
|
|
|
|
wp_display_time[i] = 0.0;
|
|
|
|
paths[i] = NULL; // no paths allocated yet
|
|
}
|
|
|
|
f_path_time = 0.0; // reset waypoint path display time
|
|
|
|
num_waypoints = 0;
|
|
|
|
last_waypoint = Vector(0,0,0);
|
|
|
|
for (i=0; i < 4; i++)
|
|
{
|
|
shortest_path[i] = NULL;
|
|
from_to[i] = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
// add a path from one waypoint (add_index) to another (path_index)...
|
|
void WaypointAddPath(short int add_index, short int path_index)
|
|
{
|
|
PATH *p, *prev;
|
|
int i;
|
|
int count = 0;
|
|
|
|
p = paths[add_index];
|
|
prev = NULL;
|
|
|
|
// find an empty slot for new path_index...
|
|
while (p != NULL)
|
|
{
|
|
i = 0;
|
|
|
|
while (i < MAX_PATH_INDEX)
|
|
{
|
|
if (p->index[i] == -1)
|
|
{
|
|
p->index[i] = path_index;
|
|
|
|
return;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
prev = p; // save the previous node in linked list
|
|
p = p->next; // go to next node in linked list
|
|
|
|
#ifdef _DEBUG
|
|
count++;
|
|
if (count > 100) WaypointDebug();
|
|
#endif
|
|
}
|
|
|
|
p = (PATH *)malloc(sizeof(PATH));
|
|
|
|
if (p == NULL)
|
|
{
|
|
ALERT(at_error, "HPB_bot - Error allocating memory for path!");
|
|
}
|
|
|
|
p->index[0] = path_index;
|
|
p->index[1] = -1;
|
|
p->index[2] = -1;
|
|
p->index[3] = -1;
|
|
p->next = NULL;
|
|
|
|
if (prev != NULL)
|
|
prev->next = p; // link new node into existing list
|
|
|
|
if (paths[add_index] == NULL)
|
|
paths[add_index] = p; // save head point if necessary
|
|
}
|
|
|
|
|
|
// delete all paths to this waypoint index...
|
|
void WaypointDeletePath(short int del_index)
|
|
{
|
|
PATH *p;
|
|
int index, i;
|
|
|
|
// search all paths for this index...
|
|
for (index=0; index < num_waypoints; index++)
|
|
{
|
|
p = paths[index];
|
|
|
|
int count = 0;
|
|
|
|
// search linked list for del_index...
|
|
while (p != NULL)
|
|
{
|
|
i = 0;
|
|
|
|
while (i < MAX_PATH_INDEX)
|
|
{
|
|
if (p->index[i] == del_index)
|
|
{
|
|
p->index[i] = -1; // unassign this path
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
p = p->next; // go to next node in linked list
|
|
|
|
#ifdef _DEBUG
|
|
count++;
|
|
if (count > 100) WaypointDebug();
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// delete a path from a waypoint (path_index) to another waypoint
|
|
// (del_index)...
|
|
void WaypointDeletePath(short int path_index, short int del_index)
|
|
{
|
|
PATH *p;
|
|
int i;
|
|
int count = 0;
|
|
|
|
p = paths[path_index];
|
|
|
|
// search linked list for del_index...
|
|
while (p != NULL)
|
|
{
|
|
i = 0;
|
|
|
|
while (i < MAX_PATH_INDEX)
|
|
{
|
|
if (p->index[i] == del_index)
|
|
{
|
|
p->index[i] = -1; // unassign this path
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
p = p->next; // go to next node in linked list
|
|
|
|
#ifdef _DEBUG
|
|
count++;
|
|
if (count > 100) WaypointDebug();
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
// find a path from the current waypoint. (pPath MUST be NULL on the
|
|
// initial call. subsequent calls will return other paths if they exist.)
|
|
int WaypointFindPath(PATH **pPath, int *path_index, int waypoint_index, int team)
|
|
{
|
|
int index;
|
|
int count = 0;
|
|
|
|
if (*pPath == NULL)
|
|
{
|
|
*pPath = paths[waypoint_index];
|
|
*path_index = 0;
|
|
}
|
|
|
|
if (*path_index == MAX_PATH_INDEX)
|
|
{
|
|
*path_index = 0;
|
|
|
|
*pPath = (*pPath)->next; // go to next node in linked list
|
|
}
|
|
|
|
while (*pPath != NULL)
|
|
{
|
|
while (*path_index < MAX_PATH_INDEX)
|
|
{
|
|
if ((*pPath)->index[*path_index] != -1) // found a path?
|
|
{
|
|
// save the return value
|
|
index = (*pPath)->index[*path_index];
|
|
|
|
// skip this path if next waypoint is team specific and NOT this team
|
|
if ((team != -1) && (waypoints[index].flags & W_FL_TEAM_SPECIFIC) &&
|
|
((waypoints[index].flags & W_FL_TEAM) != team))
|
|
{
|
|
(*path_index)++;
|
|
continue;
|
|
}
|
|
|
|
// set up stuff for subsequent calls...
|
|
(*path_index)++;
|
|
|
|
return index;
|
|
}
|
|
|
|
(*path_index)++;
|
|
}
|
|
|
|
*path_index = 0;
|
|
|
|
*pPath = (*pPath)->next; // go to next node in linked list
|
|
|
|
#ifdef _DEBUG
|
|
count++;
|
|
if (count > 100) WaypointDebug();
|
|
#endif
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
// find the nearest waypoint to the player and return the index
|
|
// (-1 if not found)...
|
|
int WaypointFindNearest(edict_t *pEntity, float range, int team)
|
|
{
|
|
int i, min_index;
|
|
float distance;
|
|
float min_distance;
|
|
TraceResult tr;
|
|
|
|
if (num_waypoints < 1)
|
|
return -1;
|
|
|
|
// find the nearest waypoint...
|
|
|
|
min_index = -1;
|
|
min_distance = 9999.0;
|
|
|
|
for (i=0; i < num_waypoints; i++)
|
|
{
|
|
if (waypoints[i].flags & W_FL_DELETED)
|
|
continue; // skip any deleted waypoints
|
|
|
|
if (waypoints[i].flags & W_FL_AIMING)
|
|
continue; // skip any aiming waypoints
|
|
|
|
// skip this waypoint if it's team specific and teams don't match...
|
|
if ((team != -1) && (waypoints[i].flags & W_FL_TEAM_SPECIFIC) &&
|
|
((waypoints[i].flags & W_FL_TEAM) != team))
|
|
continue;
|
|
|
|
distance = (waypoints[i].origin - pEntity->v.origin).Length();
|
|
|
|
if ((distance < min_distance) && (distance < range))
|
|
{
|
|
// if waypoint is visible from current position (even behind head)...
|
|
UTIL_TraceLine( pEntity->v.origin + pEntity->v.view_ofs, waypoints[i].origin,
|
|
ignore_monsters, pEntity->v.pContainingEntity, &tr );
|
|
|
|
if (tr.flFraction >= 1.0)
|
|
{
|
|
min_index = i;
|
|
min_distance = distance;
|
|
}
|
|
}
|
|
}
|
|
|
|
return min_index;
|
|
}
|
|
|
|
|
|
// find the nearest waypoint to the source postition and return the index
|
|
// of that waypoint...
|
|
int WaypointFindNearest(Vector v_src, edict_t *pEntity, float range, int team)
|
|
{
|
|
int index, min_index;
|
|
float distance;
|
|
float min_distance;
|
|
TraceResult tr;
|
|
|
|
if (num_waypoints < 1)
|
|
return -1;
|
|
|
|
// find the nearest waypoint...
|
|
|
|
min_index = -1;
|
|
min_distance = 9999.0;
|
|
|
|
for (index=0; index < num_waypoints; index++)
|
|
{
|
|
if (waypoints[index].flags & W_FL_DELETED)
|
|
continue; // skip any deleted waypoints
|
|
|
|
if (waypoints[index].flags & W_FL_AIMING)
|
|
continue; // skip any aiming waypoints
|
|
|
|
// skip this waypoint if it's team specific and teams don't match...
|
|
if ((team != -1) && (waypoints[index].flags & W_FL_TEAM_SPECIFIC) &&
|
|
((waypoints[index].flags & W_FL_TEAM) != team))
|
|
continue;
|
|
|
|
distance = (waypoints[index].origin - v_src).Length();
|
|
|
|
if ((distance < min_distance) && (distance < range))
|
|
{
|
|
// if waypoint is visible from source position...
|
|
UTIL_TraceLine( v_src, waypoints[index].origin, ignore_monsters,
|
|
pEntity->v.pContainingEntity, &tr );
|
|
|
|
if (tr.flFraction >= 1.0)
|
|
{
|
|
min_index = index;
|
|
min_distance = distance;
|
|
}
|
|
}
|
|
}
|
|
|
|
return min_index;
|
|
}
|
|
|
|
|
|
// find the goal nearest to the player matching the "flags" bits and return
|
|
// the index of that waypoint...
|
|
int WaypointFindNearestGoal(edict_t *pEntity, int src, int team, int flags)
|
|
{
|
|
int index, min_index;
|
|
int distance, min_distance;
|
|
|
|
if (num_waypoints < 1)
|
|
return -1;
|
|
|
|
// find the nearest waypoint with the matching flags...
|
|
|
|
min_index = -1;
|
|
min_distance = 99999;
|
|
|
|
for (index=0; index < num_waypoints; index++)
|
|
{
|
|
if (index == src)
|
|
continue; // skip the source waypoint
|
|
|
|
if (waypoints[index].flags & W_FL_DELETED)
|
|
continue; // skip any deleted waypoints
|
|
|
|
if (waypoints[index].flags & W_FL_AIMING)
|
|
continue; // skip any aiming waypoints
|
|
|
|
// skip this waypoint if it's team specific and teams don't match...
|
|
if ((team != -1) && (waypoints[index].flags & W_FL_TEAM_SPECIFIC) &&
|
|
((waypoints[index].flags & W_FL_TEAM) != team))
|
|
continue;
|
|
|
|
if ((waypoints[index].flags & flags) != flags)
|
|
continue; // skip this waypoint if the flags don't match
|
|
|
|
distance = WaypointDistanceFromTo(src, index, team);
|
|
|
|
if (distance < min_distance)
|
|
{
|
|
min_index = index;
|
|
min_distance = distance;
|
|
}
|
|
}
|
|
|
|
return min_index;
|
|
}
|
|
|
|
|
|
// find the goal nearest to the source position (v_src) matching the "flags"
|
|
// bits and return the index of that waypoint...
|
|
int WaypointFindNearestGoal(Vector v_src, edict_t *pEntity, float range, int team, int flags)
|
|
{
|
|
int index, min_index;
|
|
int distance, min_distance;
|
|
|
|
if (num_waypoints < 1)
|
|
return -1;
|
|
|
|
// find the nearest waypoint with the matching flags...
|
|
|
|
min_index = -1;
|
|
min_distance = 99999;
|
|
|
|
for (index=0; index < num_waypoints; index++)
|
|
{
|
|
if (waypoints[index].flags & W_FL_DELETED)
|
|
continue; // skip any deleted waypoints
|
|
|
|
if (waypoints[index].flags & W_FL_AIMING)
|
|
continue; // skip any aiming waypoints
|
|
|
|
// skip this waypoint if it's team specific and teams don't match...
|
|
if ((team != -1) && (waypoints[index].flags & W_FL_TEAM_SPECIFIC) &&
|
|
((waypoints[index].flags & W_FL_TEAM) != team))
|
|
continue;
|
|
|
|
if ((waypoints[index].flags & flags) != flags)
|
|
continue; // skip this waypoint if the flags don't match
|
|
|
|
distance = (waypoints[index].origin - v_src).Length();
|
|
|
|
if ((distance < range) && (distance < min_distance))
|
|
{
|
|
min_index = index;
|
|
min_distance = distance;
|
|
}
|
|
}
|
|
|
|
return min_index;
|
|
}
|
|
|
|
|
|
// find a random goal matching the "flags" bits and return the index of
|
|
// that waypoint...
|
|
int WaypointFindRandomGoal(edict_t *pEntity, int team, int flags)
|
|
{
|
|
int index;
|
|
int indexes[50];
|
|
int count = 0;
|
|
|
|
if (num_waypoints < 1)
|
|
return -1;
|
|
|
|
// find all the waypoints with the matching flags...
|
|
|
|
for (index=0; index < num_waypoints; index++)
|
|
{
|
|
if (waypoints[index].flags & W_FL_DELETED)
|
|
continue; // skip any deleted waypoints
|
|
|
|
if (waypoints[index].flags & W_FL_AIMING)
|
|
continue; // skip any aiming waypoints
|
|
|
|
// skip this waypoint if it's team specific and teams don't match...
|
|
if ((team != -1) && (waypoints[index].flags & W_FL_TEAM_SPECIFIC) &&
|
|
((waypoints[index].flags & W_FL_TEAM) != team))
|
|
continue;
|
|
|
|
if ((waypoints[index].flags & flags) != flags)
|
|
continue; // skip this waypoint if the flags don't match
|
|
|
|
if (count < 50)
|
|
{
|
|
indexes[count] = index;
|
|
|
|
count++;
|
|
}
|
|
}
|
|
|
|
if (count == 0) // no matching waypoints found
|
|
return -1;
|
|
|
|
index = RANDOM_LONG(1, count) - 1;
|
|
|
|
return indexes[index];
|
|
}
|
|
|
|
|
|
// find a random goal within a range of a position (v_src) matching the
|
|
// "flags" bits and return the index of that waypoint...
|
|
int WaypointFindRandomGoal(Vector v_src, edict_t *pEntity, float range, int team, int flags)
|
|
{
|
|
int index;
|
|
int indexes[50];
|
|
int count = 0;
|
|
float distance;
|
|
|
|
if (num_waypoints < 1)
|
|
return -1;
|
|
|
|
// find all the waypoints with the matching flags...
|
|
|
|
for (index=0; index < num_waypoints; index++)
|
|
{
|
|
if (waypoints[index].flags & W_FL_DELETED)
|
|
continue; // skip any deleted waypoints
|
|
|
|
if (waypoints[index].flags & W_FL_AIMING)
|
|
continue; // skip any aiming waypoints
|
|
|
|
// skip this waypoint if it's team specific and teams don't match...
|
|
if ((team != -1) && (waypoints[index].flags & W_FL_TEAM_SPECIFIC) &&
|
|
((waypoints[index].flags & W_FL_TEAM) != team))
|
|
continue;
|
|
|
|
if ((waypoints[index].flags & flags) != flags)
|
|
continue; // skip this waypoint if the flags don't match
|
|
|
|
distance = (waypoints[index].origin - v_src).Length();
|
|
|
|
if ((distance < range) && (count < 50))
|
|
{
|
|
indexes[count] = index;
|
|
|
|
count++;
|
|
}
|
|
}
|
|
|
|
if (count == 0) // no matching waypoints found
|
|
return -1;
|
|
|
|
index = RANDOM_LONG(1, count) - 1;
|
|
|
|
return indexes[index];
|
|
}
|
|
|
|
|
|
// find the nearest "special" aiming waypoint (for sniper aiming)...
|
|
int WaypointFindNearestAiming(Vector v_origin)
|
|
{
|
|
int index;
|
|
int min_index = -1;
|
|
int min_distance = 9999.0;
|
|
float distance;
|
|
|
|
if (num_waypoints < 1)
|
|
return -1;
|
|
|
|
// search for nearby aiming waypoint...
|
|
for (index=0; index < num_waypoints; index++)
|
|
{
|
|
if (waypoints[index].flags & W_FL_DELETED)
|
|
continue; // skip any deleted waypoints
|
|
|
|
if ((waypoints[index].flags & W_FL_AIMING) == 0)
|
|
continue; // skip any NON aiming waypoints
|
|
|
|
distance = (v_origin - waypoints[index].origin).Length();
|
|
|
|
if ((distance < min_distance) && (distance < 40))
|
|
{
|
|
min_index = index;
|
|
min_distance = distance;
|
|
}
|
|
}
|
|
|
|
return min_index;
|
|
}
|
|
|
|
|
|
void WaypointDrawBeam(edict_t *pEntity, Vector start, Vector end, int width,
|
|
int noise, int red, int green, int blue, int brightness, int speed)
|
|
{
|
|
// MESSAGE_BEGIN(MSG_ONE, SVC_TEMPENTITY, NULL, pEntity);
|
|
// WRITE_BYTE( TE_BEAMPOINTS);
|
|
// WRITE_COORD(start.x);
|
|
// WRITE_COORD(start.y);
|
|
// WRITE_COORD(start.z);
|
|
// WRITE_COORD(end.x);
|
|
// WRITE_COORD(end.y);
|
|
// WRITE_COORD(end.z);
|
|
// WRITE_SHORT( m_spriteTexture );
|
|
// WRITE_BYTE( 1 ); // framestart
|
|
// WRITE_BYTE( 10 ); // framerate
|
|
// WRITE_BYTE( 10 ); // life in 0.1's
|
|
// WRITE_BYTE( width ); // width
|
|
// WRITE_BYTE( noise ); // noise
|
|
//
|
|
// WRITE_BYTE( red ); // r, g, b
|
|
// WRITE_BYTE( green ); // r, g, b
|
|
// WRITE_BYTE( blue ); // r, g, b
|
|
//
|
|
// WRITE_BYTE( brightness ); // brightness
|
|
// WRITE_BYTE( speed ); // speed
|
|
// MESSAGE_END();
|
|
}
|
|
|
|
|
|
void WaypointAdd(edict_t *pEntity)
|
|
{
|
|
int index;
|
|
edict_t *pent = NULL;
|
|
float radius = 40;
|
|
char item_name[64];
|
|
|
|
if (num_waypoints >= MAX_WAYPOINTS)
|
|
return;
|
|
|
|
index = 0;
|
|
|
|
// find the next available slot for the new waypoint...
|
|
while (index < num_waypoints)
|
|
{
|
|
if (waypoints[index].flags & W_FL_DELETED)
|
|
break;
|
|
|
|
index++;
|
|
}
|
|
|
|
waypoints[index].flags = 0;
|
|
|
|
// store the origin (location) of this waypoint (use entity origin)
|
|
waypoints[index].origin = pEntity->v.origin;
|
|
|
|
// store the last used waypoint for the auto waypoint code...
|
|
last_waypoint = pEntity->v.origin;
|
|
|
|
// set the time that this waypoint was originally displayed...
|
|
wp_display_time[index] = gpGlobals->time;
|
|
|
|
|
|
Vector start, end;
|
|
|
|
start = pEntity->v.origin - Vector(0, 0, 34);
|
|
end = start + Vector(0, 0, 68);
|
|
|
|
if ((pEntity->v.flags & FL_DUCKING) == FL_DUCKING)
|
|
{
|
|
waypoints[index].flags |= W_FL_CROUCH; // crouching waypoint
|
|
|
|
start = pEntity->v.origin - Vector(0, 0, 17);
|
|
end = start + Vector(0, 0, 34);
|
|
}
|
|
|
|
if (pEntity->v.movetype == MOVETYPE_FLY)
|
|
waypoints[index].flags |= W_FL_LADDER; // waypoint on a ladder
|
|
|
|
|
|
//********************************************************
|
|
// look for lift, ammo, flag, health, armor, etc.
|
|
//********************************************************
|
|
|
|
while ((pent = UTIL_FindEntityInSphere( pent, pEntity->v.origin, radius )) != NULL)
|
|
{
|
|
strcpy(item_name, STRING(pent->v.classname));
|
|
|
|
if (strcmp("item_healthkit", item_name) == 0)
|
|
{
|
|
ClientPrint(pEntity, HUD_PRINTCONSOLE, "found a healthkit!\n");
|
|
waypoints[index].flags = W_FL_HEALTH;
|
|
}
|
|
|
|
if (strncmp("item_armor", item_name, 10) == 0)
|
|
{
|
|
ClientPrint(pEntity, HUD_PRINTCONSOLE, "found some armor!\n");
|
|
waypoints[index].flags = W_FL_ARMOR;
|
|
}
|
|
|
|
// *************
|
|
// LOOK FOR AMMO
|
|
// *************
|
|
|
|
}
|
|
|
|
// draw a blue waypoint
|
|
WaypointDrawBeam(pEntity, start, end, 30, 0, 0, 0, 255, 250, 5);
|
|
|
|
EMIT_SOUND_DYN2(pEntity, CHAN_WEAPON, "weapons/xbow_hit1.wav", 1.0,
|
|
ATTN_NORM, 0, 100);
|
|
|
|
// increment total number of waypoints if adding at end of array...
|
|
if (index == num_waypoints)
|
|
num_waypoints++;
|
|
|
|
// calculate all the paths to this new waypoint
|
|
for (int i=0; i < num_waypoints; i++)
|
|
{
|
|
if (i == index)
|
|
continue; // skip the waypoint that was just added
|
|
|
|
if (waypoints[i].flags & W_FL_AIMING)
|
|
continue; // skip any aiming waypoints
|
|
|
|
// check if the waypoint is reachable from the new one (one-way)
|
|
if ( WaypointReachable(pEntity->v.origin, waypoints[i].origin, pEntity) )
|
|
{
|
|
WaypointAddPath(index, i);
|
|
}
|
|
|
|
// check if the new one is reachable from the waypoint (other way)
|
|
if ( WaypointReachable(waypoints[i].origin, pEntity->v.origin, pEntity) )
|
|
{
|
|
WaypointAddPath(i, index);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void WaypointAddAiming(edict_t *pEntity)
|
|
{
|
|
int index;
|
|
edict_t *pent = NULL;
|
|
|
|
if (num_waypoints >= MAX_WAYPOINTS)
|
|
return;
|
|
|
|
index = 0;
|
|
|
|
// find the next available slot for the new waypoint...
|
|
while (index < num_waypoints)
|
|
{
|
|
if (waypoints[index].flags & W_FL_DELETED)
|
|
break;
|
|
|
|
index++;
|
|
}
|
|
|
|
waypoints[index].flags = W_FL_AIMING; // aiming waypoint
|
|
|
|
Vector v_angle = pEntity->v.v_angle;
|
|
|
|
v_angle.x = 0; // reset pitch to horizontal
|
|
v_angle.z = 0; // reset roll to level
|
|
|
|
UTIL_MakeVectors(v_angle);
|
|
|
|
// store the origin (location) of this waypoint (use entity origin)
|
|
waypoints[index].origin = pEntity->v.origin + gpGlobals->v_forward * 25;
|
|
|
|
// set the time that this waypoint was originally displayed...
|
|
wp_display_time[index] = gpGlobals->time;
|
|
|
|
|
|
Vector start, end;
|
|
|
|
start = pEntity->v.origin - Vector(0, 0, 10);
|
|
end = start + Vector(0, 0, 14);
|
|
|
|
// draw a blue waypoint
|
|
WaypointDrawBeam(pEntity, start, end, 30, 0, 0, 0, 255, 250, 5);
|
|
|
|
EMIT_SOUND_DYN2(pEntity, CHAN_WEAPON, "weapons/xbow_hit1.wav", 1.0,
|
|
ATTN_NORM, 0, 100);
|
|
|
|
// increment total number of waypoints if adding at end of array...
|
|
if (index == num_waypoints)
|
|
num_waypoints++;
|
|
}
|
|
|
|
|
|
void WaypointDelete(edict_t *pEntity)
|
|
{
|
|
int index;
|
|
int count = 0;
|
|
|
|
if (num_waypoints < 1)
|
|
return;
|
|
|
|
index = WaypointFindNearest(pEntity, 50.0, -1);
|
|
|
|
if (index == -1)
|
|
return;
|
|
|
|
if ((waypoints[index].flags & W_FL_SNIPER) ||
|
|
((mod_id == FRONTLINE_DLL) && (waypoints[index].flags & W_FL_FLF_DEFEND)))
|
|
{
|
|
int i;
|
|
int min_index = -1;
|
|
int min_distance = 9999.0;
|
|
float distance;
|
|
|
|
// search for nearby aiming waypoint and delete it also...
|
|
for (i=0; i < num_waypoints; i++)
|
|
{
|
|
if (waypoints[i].flags & W_FL_DELETED)
|
|
continue; // skip any deleted waypoints
|
|
|
|
if ((waypoints[i].flags & W_FL_AIMING) == 0)
|
|
continue; // skip any NON aiming waypoints
|
|
|
|
distance = (waypoints[i].origin - waypoints[index].origin).Length();
|
|
|
|
if ((distance < min_distance) && (distance < 40))
|
|
{
|
|
min_index = i;
|
|
min_distance = distance;
|
|
}
|
|
}
|
|
|
|
if (min_index != -1)
|
|
{
|
|
waypoints[min_index].flags = W_FL_DELETED; // not being used
|
|
waypoints[min_index].origin = Vector(0,0,0);
|
|
|
|
wp_display_time[min_index] = 0.0;
|
|
}
|
|
}
|
|
|
|
// delete any paths that lead to this index...
|
|
WaypointDeletePath(index);
|
|
|
|
// free the path for this index...
|
|
|
|
if (paths[index] != NULL)
|
|
{
|
|
PATH *p = paths[index];
|
|
PATH *p_next;
|
|
|
|
while (p) // free the linked list
|
|
{
|
|
p_next = p->next; // save the link to next
|
|
free(p);
|
|
p = p_next;
|
|
|
|
#ifdef _DEBUG
|
|
count++;
|
|
if (count > 100) WaypointDebug();
|
|
#endif
|
|
}
|
|
|
|
paths[index] = NULL;
|
|
}
|
|
|
|
waypoints[index].flags = W_FL_DELETED; // not being used
|
|
waypoints[index].origin = Vector(0,0,0);
|
|
|
|
wp_display_time[index] = 0.0;
|
|
|
|
EMIT_SOUND_DYN2(pEntity, CHAN_WEAPON, "weapons/mine_activate.wav", 1.0,
|
|
ATTN_NORM, 0, 100);
|
|
}
|
|
|
|
|
|
// allow player to manually create a path from one waypoint to another
|
|
void WaypointCreatePath(edict_t *pEntity, int cmd)
|
|
{
|
|
static int waypoint1 = -1; // initialized to unassigned
|
|
static int waypoint2 = -1; // initialized to unassigned
|
|
|
|
if (cmd == 1) // assign source of path
|
|
{
|
|
waypoint1 = WaypointFindNearest(pEntity, 50.0, -1);
|
|
|
|
if (waypoint1 == -1)
|
|
{
|
|
// play "cancelled" sound...
|
|
EMIT_SOUND_DYN2(pEntity, CHAN_WEAPON, "common/wpn_moveselect.wav", 1.0,
|
|
ATTN_NORM, 0, 100);
|
|
|
|
return;
|
|
}
|
|
|
|
// play "start" sound...
|
|
EMIT_SOUND_DYN2(pEntity, CHAN_WEAPON, "common/wpn_hudoff.wav", 1.0,
|
|
ATTN_NORM, 0, 100);
|
|
|
|
return;
|
|
}
|
|
|
|
if (cmd == 2) // assign dest of path and make path
|
|
{
|
|
waypoint2 = WaypointFindNearest(pEntity, 50.0, -1);
|
|
|
|
if ((waypoint1 == -1) || (waypoint2 == -1))
|
|
{
|
|
// play "error" sound...
|
|
EMIT_SOUND_DYN2(pEntity, CHAN_WEAPON, "common/wpn_denyselect.wav", 1.0,
|
|
ATTN_NORM, 0, 100);
|
|
|
|
return;
|
|
}
|
|
|
|
WaypointAddPath(waypoint1, waypoint2);
|
|
|
|
// play "done" sound...
|
|
EMIT_SOUND_DYN2(pEntity, CHAN_WEAPON, "common/wpn_hudon.wav", 1.0,
|
|
ATTN_NORM, 0, 100);
|
|
}
|
|
}
|
|
|
|
|
|
// allow player to manually remove a path from one waypoint to another
|
|
void WaypointRemovePath(edict_t *pEntity, int cmd)
|
|
{
|
|
static int waypoint1 = -1; // initialized to unassigned
|
|
static int waypoint2 = -1; // initialized to unassigned
|
|
|
|
if (cmd == 1) // assign source of path
|
|
{
|
|
waypoint1 = WaypointFindNearest(pEntity, 50.0, -1);
|
|
|
|
if (waypoint1 == -1)
|
|
{
|
|
// play "cancelled" sound...
|
|
EMIT_SOUND_DYN2(pEntity, CHAN_WEAPON, "common/wpn_moveselect.wav", 1.0,
|
|
ATTN_NORM, 0, 100);
|
|
|
|
return;
|
|
}
|
|
|
|
// play "start" sound...
|
|
EMIT_SOUND_DYN2(pEntity, CHAN_WEAPON, "common/wpn_hudoff.wav", 1.0,
|
|
ATTN_NORM, 0, 100);
|
|
|
|
return;
|
|
}
|
|
|
|
if (cmd == 2) // assign dest of path and make path
|
|
{
|
|
waypoint2 = WaypointFindNearest(pEntity, 50.0, -1);
|
|
|
|
if ((waypoint1 == -1) || (waypoint2 == -1))
|
|
{
|
|
// play "error" sound...
|
|
EMIT_SOUND_DYN2(pEntity, CHAN_WEAPON, "common/wpn_denyselect.wav", 1.0,
|
|
ATTN_NORM, 0, 100);
|
|
|
|
return;
|
|
}
|
|
|
|
WaypointDeletePath(waypoint1, waypoint2);
|
|
|
|
// play "done" sound...
|
|
EMIT_SOUND_DYN2(pEntity, CHAN_WEAPON, "common/wpn_hudon.wav", 1.0,
|
|
ATTN_NORM, 0, 100);
|
|
}
|
|
}
|
|
|
|
|
|
bool WaypointLoad(edict_t *pEntity)
|
|
{
|
|
char mapname[64];
|
|
char filename[256];
|
|
WAYPOINT_HDR header;
|
|
char msg[80];
|
|
int index, i;
|
|
short int num;
|
|
short int path_index;
|
|
|
|
strcpy(mapname, STRING(gpGlobals->mapname));
|
|
strcat(mapname, ".wpt");
|
|
|
|
UTIL_BuildFileName(filename, "maps", mapname);
|
|
|
|
if (IS_DEDICATED_SERVER())
|
|
printf("loading waypoint file: %s\n", filename);
|
|
|
|
FILE *bfp = fopen(filename, "rb");
|
|
|
|
// if file exists, read the waypoint structure from it
|
|
if (bfp != NULL)
|
|
{
|
|
fread(&header, sizeof(header), 1, bfp);
|
|
|
|
header.filetype[7] = 0;
|
|
if (strcmp(header.filetype, "HPB_bot") == 0)
|
|
{
|
|
if (header.waypoint_file_version != WAYPOINT_VERSION)
|
|
{
|
|
if (pEntity)
|
|
ClientPrint(pEntity, HUD_PRINTNOTIFY, "Incompatible HPB bot waypoint file version!\nWaypoints not loaded!\n");
|
|
|
|
fclose(bfp);
|
|
return FALSE;
|
|
}
|
|
|
|
header.mapname[31] = 0;
|
|
|
|
if (strcmp(header.mapname, STRING(gpGlobals->mapname)) == 0)
|
|
{
|
|
WaypointInit(); // remove any existing waypoints
|
|
|
|
for (i=0; i < header.number_of_waypoints; i++)
|
|
{
|
|
fread(&waypoints[i], sizeof(waypoints[0]), 1, bfp);
|
|
num_waypoints++;
|
|
}
|
|
|
|
// read and add waypoint paths...
|
|
for (index=0; index < num_waypoints; index++)
|
|
{
|
|
// read the number of paths from this node...
|
|
fread(&num, sizeof(num), 1, bfp);
|
|
|
|
for (i=0; i < num; i++)
|
|
{
|
|
fread(&path_index, sizeof(path_index), 1, bfp);
|
|
|
|
WaypointAddPath(index, path_index);
|
|
}
|
|
}
|
|
|
|
g_waypoint_paths = TRUE; // keep track so path can be freed
|
|
}
|
|
else
|
|
{
|
|
if (pEntity)
|
|
{
|
|
sprintf(msg, "%s HPB bot waypoints are not for this map!\n", filename);
|
|
ClientPrint(pEntity, HUD_PRINTNOTIFY, msg);
|
|
}
|
|
|
|
fclose(bfp);
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pEntity)
|
|
{
|
|
sprintf(msg, "%s is not a HPB bot waypoint file!\n", filename);
|
|
ClientPrint(pEntity, HUD_PRINTNOTIFY, msg);
|
|
}
|
|
|
|
fclose(bfp);
|
|
return FALSE;
|
|
}
|
|
|
|
fclose(bfp);
|
|
|
|
WaypointRouteInit();
|
|
}
|
|
else
|
|
{
|
|
if (pEntity)
|
|
{
|
|
sprintf(msg, "Waypoint file %s does not exist!\n", filename);
|
|
ClientPrint(pEntity, HUD_PRINTNOTIFY, msg);
|
|
}
|
|
|
|
if (IS_DEDICATED_SERVER())
|
|
printf("waypoint file %s not found!\n", filename);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void WaypointSave(void)
|
|
{
|
|
char filename[256];
|
|
char mapname[64];
|
|
WAYPOINT_HDR header;
|
|
int index, i;
|
|
short int num;
|
|
PATH *p;
|
|
|
|
strcpy(header.filetype, "HPB_bot");
|
|
|
|
header.waypoint_file_version = WAYPOINT_VERSION;
|
|
|
|
header.waypoint_file_flags = 0; // not currently used
|
|
|
|
header.number_of_waypoints = num_waypoints;
|
|
|
|
memset(header.mapname, 0, sizeof(header.mapname));
|
|
strncpy(header.mapname, STRING(gpGlobals->mapname), 31);
|
|
header.mapname[31] = 0;
|
|
|
|
strcpy(mapname, STRING(gpGlobals->mapname));
|
|
strcat(mapname, ".wpt");
|
|
|
|
UTIL_BuildFileName(filename, "maps", mapname);
|
|
|
|
FILE *bfp = fopen(filename, "wb");
|
|
|
|
// write the waypoint header to the file...
|
|
fwrite(&header, sizeof(header), 1, bfp);
|
|
|
|
// write the waypoint data to the file...
|
|
for (index=0; index < num_waypoints; index++)
|
|
{
|
|
fwrite(&waypoints[index], sizeof(waypoints[0]), 1, bfp);
|
|
}
|
|
|
|
// save the waypoint paths...
|
|
for (index=0; index < num_waypoints; index++)
|
|
{
|
|
// count the number of paths from this node...
|
|
|
|
p = paths[index];
|
|
num = 0;
|
|
|
|
while (p != NULL)
|
|
{
|
|
i = 0;
|
|
|
|
while (i < MAX_PATH_INDEX)
|
|
{
|
|
if (p->index[i] != -1)
|
|
num++; // count path node if it's used
|
|
|
|
i++;
|
|
}
|
|
|
|
p = p->next; // go to next node in linked list
|
|
}
|
|
|
|
fwrite(&num, sizeof(num), 1, bfp); // write the count
|
|
|
|
// now write out each path index...
|
|
|
|
p = paths[index];
|
|
|
|
while (p != NULL)
|
|
{
|
|
i = 0;
|
|
|
|
while (i < MAX_PATH_INDEX)
|
|
{
|
|
if (p->index[i] != -1) // save path node if it's used
|
|
fwrite(&p->index[i], sizeof(p->index[0]), 1, bfp);
|
|
|
|
i++;
|
|
}
|
|
|
|
p = p->next; // go to next node in linked list
|
|
}
|
|
}
|
|
|
|
fclose(bfp);
|
|
}
|
|
|
|
|
|
bool WaypointReachable(Vector v_src, Vector v_dest, edict_t *pEntity)
|
|
{
|
|
TraceResult tr;
|
|
float curr_height, last_height;
|
|
|
|
float distance = (v_dest - v_src).Length();
|
|
|
|
// is the destination close enough?
|
|
if (distance < REACHABLE_RANGE)
|
|
{
|
|
// check if this waypoint is "visible"...
|
|
|
|
UTIL_TraceLine( v_src, v_dest, ignore_monsters,
|
|
pEntity->v.pContainingEntity, &tr );
|
|
|
|
// if waypoint is visible from current position (even behind head)...
|
|
if (tr.flFraction >= 1.0)
|
|
{
|
|
// check for special case of both waypoints being underwater...
|
|
if ((POINT_CONTENTS( v_src ) == CONTENTS_WATER) &&
|
|
(POINT_CONTENTS( v_dest ) == CONTENTS_WATER))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
// check for special case of waypoint being suspended in mid-air...
|
|
|
|
// is dest waypoint higher than src? (45 is max jump height)
|
|
if (v_dest.z > (v_src.z + 45.0))
|
|
{
|
|
Vector v_new_src = v_dest;
|
|
Vector v_new_dest = v_dest;
|
|
|
|
v_new_dest.z = v_new_dest.z - 50; // straight down 50 units
|
|
|
|
UTIL_TraceLine(v_new_src, v_new_dest, dont_ignore_monsters,
|
|
pEntity->v.pContainingEntity, &tr);
|
|
|
|
// check if we didn't hit anything, if not then it's in mid-air
|
|
if (tr.flFraction >= 1.0)
|
|
{
|
|
return FALSE; // can't reach this one
|
|
}
|
|
}
|
|
|
|
// check if distance to ground increases more than jump height
|
|
// at points between source and destination...
|
|
|
|
Vector v_direction = (v_dest - v_src).Normalize(); // 1 unit long
|
|
Vector v_check = v_src;
|
|
Vector v_down = v_src;
|
|
|
|
v_down.z = v_down.z - 1000.0; // straight down 1000 units
|
|
|
|
UTIL_TraceLine(v_check, v_down, ignore_monsters,
|
|
pEntity->v.pContainingEntity, &tr);
|
|
|
|
last_height = tr.flFraction * 1000.0; // height from ground
|
|
|
|
distance = (v_dest - v_check).Length(); // distance from goal
|
|
|
|
while (distance > 10.0)
|
|
{
|
|
// move 10 units closer to the goal...
|
|
v_check = v_check + (v_direction * 10.0);
|
|
|
|
v_down = v_check;
|
|
v_down.z = v_down.z - 1000.0; // straight down 1000 units
|
|
|
|
UTIL_TraceLine(v_check, v_down, ignore_monsters,
|
|
pEntity->v.pContainingEntity, &tr);
|
|
|
|
curr_height = tr.flFraction * 1000.0; // height from ground
|
|
|
|
// is the difference in the last height and the current height
|
|
// higher that the jump height?
|
|
if ((last_height - curr_height) > 45.0)
|
|
{
|
|
// can't get there from here...
|
|
return FALSE;
|
|
}
|
|
|
|
last_height = curr_height;
|
|
|
|
distance = (v_dest - v_check).Length(); // distance from goal
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
// find the nearest reachable waypoint
|
|
int WaypointFindReachable(edict_t *pEntity, float range, int team)
|
|
{
|
|
int i, min_index;
|
|
float distance;
|
|
float min_distance;
|
|
TraceResult tr;
|
|
|
|
// find the nearest waypoint...
|
|
|
|
min_distance = 9999.0;
|
|
|
|
for (i=0; i < num_waypoints; i++)
|
|
{
|
|
if (waypoints[i].flags & W_FL_DELETED)
|
|
continue; // skip any deleted waypoints
|
|
|
|
if (waypoints[i].flags & W_FL_AIMING)
|
|
continue; // skip any aiming waypoints
|
|
|
|
// skip this waypoint if it's team specific and teams don't match...
|
|
if ((team != -1) && (waypoints[i].flags & W_FL_TEAM_SPECIFIC) &&
|
|
((waypoints[i].flags & W_FL_TEAM) != team))
|
|
continue;
|
|
|
|
distance = (waypoints[i].origin - pEntity->v.origin).Length();
|
|
|
|
if (distance < min_distance)
|
|
{
|
|
// if waypoint is visible from current position (even behind head)...
|
|
UTIL_TraceLine( pEntity->v.origin + pEntity->v.view_ofs, waypoints[i].origin,
|
|
ignore_monsters, pEntity->v.pContainingEntity, &tr );
|
|
|
|
if (tr.flFraction >= 1.0)
|
|
{
|
|
if (WaypointReachable(pEntity->v.origin, waypoints[i].origin, pEntity))
|
|
{
|
|
min_index = i;
|
|
min_distance = distance;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// if not close enough to a waypoint then just return
|
|
if (min_distance > range)
|
|
return -1;
|
|
|
|
return min_index;
|
|
|
|
}
|
|
|
|
|
|
void WaypointPrintInfo(edict_t *pEntity)
|
|
{
|
|
char msg[80];
|
|
int index;
|
|
int flags;
|
|
|
|
// find the nearest waypoint...
|
|
index = WaypointFindNearest(pEntity, 50.0, -1);
|
|
|
|
if (index == -1)
|
|
return;
|
|
|
|
sprintf(msg,"Waypoint %d of %d total\n", index, num_waypoints);
|
|
ClientPrint(pEntity, HUD_PRINTNOTIFY, msg);
|
|
|
|
flags = waypoints[index].flags;
|
|
|
|
if (flags & W_FL_TEAM_SPECIFIC)
|
|
{
|
|
if (mod_id == FRONTLINE_DLL)
|
|
{
|
|
if ((flags & W_FL_TEAM) == 0)
|
|
strcpy(msg, "Waypoint is for Attackers\n");
|
|
else if ((flags & W_FL_TEAM) == 1)
|
|
strcpy(msg, "Waypoint is for Defenders\n");
|
|
}
|
|
else
|
|
{
|
|
if ((flags & W_FL_TEAM) == 0)
|
|
strcpy(msg, "Waypoint is for TEAM 1\n");
|
|
else if ((flags & W_FL_TEAM) == 1)
|
|
strcpy(msg, "Waypoint is for TEAM 2\n");
|
|
else if ((flags & W_FL_TEAM) == 2)
|
|
strcpy(msg, "Waypoint is for TEAM 3\n");
|
|
else if ((flags & W_FL_TEAM) == 3)
|
|
strcpy(msg, "Waypoint is for TEAM 4\n");
|
|
}
|
|
|
|
ClientPrint(pEntity, HUD_PRINTNOTIFY, msg);
|
|
}
|
|
|
|
if (flags & W_FL_LIFT)
|
|
ClientPrint(pEntity, HUD_PRINTNOTIFY, "Bot will wait for lift before approaching\n");
|
|
|
|
if (flags & W_FL_LADDER)
|
|
ClientPrint(pEntity, HUD_PRINTNOTIFY, "This waypoint is on a ladder\n");
|
|
|
|
if (flags & W_FL_DOOR)
|
|
ClientPrint(pEntity, HUD_PRINTNOTIFY, "This is a door waypoint\n");
|
|
|
|
if (flags & W_FL_HEALTH)
|
|
ClientPrint(pEntity, HUD_PRINTNOTIFY, "There is health near this waypoint\n");
|
|
|
|
if (flags & W_FL_ARMOR)
|
|
ClientPrint(pEntity, HUD_PRINTNOTIFY, "There is armor near this waypoint\n");
|
|
|
|
if (flags & W_FL_AMMO)
|
|
ClientPrint(pEntity, HUD_PRINTNOTIFY, "There is ammo near this waypoint\n");
|
|
|
|
if (flags & W_FL_SNIPER)
|
|
ClientPrint(pEntity, HUD_PRINTNOTIFY, "This is a sniper waypoint\n");
|
|
|
|
if (flags & W_FL_TFC_FLAG)
|
|
{
|
|
if (mod_id == FRONTLINE_DLL)
|
|
ClientPrint(pEntity, HUD_PRINTNOTIFY, "There is a capture point near this waypoint\n");
|
|
else
|
|
ClientPrint(pEntity, HUD_PRINTNOTIFY, "There is a flag near this waypoint\n");
|
|
}
|
|
|
|
if (flags & W_FL_TFC_FLAG_GOAL)
|
|
{
|
|
if (mod_id == FRONTLINE_DLL)
|
|
ClientPrint(pEntity, HUD_PRINTNOTIFY, "This is a defender location\n");
|
|
else
|
|
ClientPrint(pEntity, HUD_PRINTNOTIFY, "There is a flag goal near this waypoint\n");
|
|
}
|
|
|
|
if (flags & W_FL_PRONE)
|
|
ClientPrint(pEntity, HUD_PRINTNOTIFY, "Bot will go prone here\n");
|
|
}
|
|
|
|
|
|
void WaypointThink(edict_t *pEntity)
|
|
{
|
|
float distance, min_distance;
|
|
Vector start, end;
|
|
int i, index;
|
|
|
|
if (g_auto_waypoint) // is auto waypoint on?
|
|
{
|
|
// find the distance from the last used waypoint
|
|
distance = (last_waypoint - pEntity->v.origin).Length();
|
|
|
|
if (distance > 200)
|
|
{
|
|
min_distance = 9999.0;
|
|
|
|
// check that no other reachable waypoints are nearby...
|
|
for (i=0; i < num_waypoints; i++)
|
|
{
|
|
if (waypoints[i].flags & W_FL_DELETED)
|
|
continue;
|
|
|
|
if (waypoints[i].flags & W_FL_AIMING)
|
|
continue;
|
|
|
|
if (WaypointReachable(pEntity->v.origin, waypoints[i].origin, pEntity))
|
|
{
|
|
distance = (waypoints[i].origin - pEntity->v.origin).Length();
|
|
|
|
if (distance < min_distance)
|
|
min_distance = distance;
|
|
}
|
|
}
|
|
|
|
// make sure nearest waypoint is far enough away...
|
|
if (min_distance >= 200)
|
|
WaypointAdd(pEntity); // place a waypoint here
|
|
}
|
|
}
|
|
|
|
min_distance = 9999.0;
|
|
|
|
if (g_waypoint_on) // display the waypoints if turned on...
|
|
{
|
|
for (i=0; i < num_waypoints; i++)
|
|
{
|
|
if ((waypoints[i].flags & W_FL_DELETED) == W_FL_DELETED)
|
|
continue;
|
|
|
|
distance = (waypoints[i].origin - pEntity->v.origin).Length();
|
|
|
|
if (distance < 500)
|
|
{
|
|
if (distance < min_distance)
|
|
{
|
|
index = i; // store index of nearest waypoint
|
|
min_distance = distance;
|
|
}
|
|
|
|
if ((wp_display_time[i] + 1.0) < gpGlobals->time)
|
|
{
|
|
if (waypoints[i].flags & W_FL_CROUCH)
|
|
{
|
|
start = waypoints[i].origin - Vector(0, 0, 17);
|
|
end = start + Vector(0, 0, 34);
|
|
}
|
|
else if (waypoints[i].flags & W_FL_AIMING)
|
|
{
|
|
start = waypoints[i].origin + Vector(0, 0, 10);
|
|
end = start + Vector(0, 0, 14);
|
|
}
|
|
else
|
|
{
|
|
start = waypoints[i].origin - Vector(0, 0, 34);
|
|
end = start + Vector(0, 0, 68);
|
|
}
|
|
|
|
// draw a blue waypoint
|
|
WaypointDrawBeam(pEntity, start, end, 30, 0, 0, 0, 255, 250, 5);
|
|
|
|
wp_display_time[i] = gpGlobals->time;
|
|
}
|
|
}
|
|
}
|
|
|
|
// check if path waypointing is on...
|
|
if (g_path_waypoint)
|
|
{
|
|
// check if player is close enough to a waypoint and time to draw path...
|
|
if ((min_distance <= 50) && (f_path_time <= gpGlobals->time))
|
|
{
|
|
PATH *p;
|
|
|
|
f_path_time = gpGlobals->time + 1.0;
|
|
|
|
p = paths[index];
|
|
|
|
while (p != NULL)
|
|
{
|
|
i = 0;
|
|
|
|
while (i < MAX_PATH_INDEX)
|
|
{
|
|
if (p->index[i] != -1)
|
|
{
|
|
Vector v_src = waypoints[index].origin;
|
|
Vector v_dest = waypoints[p->index[i]].origin;
|
|
|
|
// draw a white line to this index's waypoint
|
|
WaypointDrawBeam(pEntity, v_src, v_dest, 10, 2, 250, 250, 250, 200, 10);
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
p = p->next; // go to next node in linked list
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// run Floyd's algorithm on the waypoint list to generate the least cost
|
|
// path matrix...
|
|
void WaypointFloyds(unsigned short *shortest_path, unsigned short *from_to)
|
|
{
|
|
unsigned int x, y, z;
|
|
int changed = 1;
|
|
int distance;
|
|
|
|
for (y=0; y < route_num_waypoints; y++)
|
|
{
|
|
for (z=0; z < route_num_waypoints; z++)
|
|
{
|
|
from_to[y * route_num_waypoints + z] = z;
|
|
}
|
|
}
|
|
|
|
while (changed)
|
|
{
|
|
changed = 0;
|
|
|
|
for (x=0; x < route_num_waypoints; x++)
|
|
{
|
|
for (y=0; y < route_num_waypoints; y++)
|
|
{
|
|
for (z=0; z < route_num_waypoints; z++)
|
|
{
|
|
if ((shortest_path[y * route_num_waypoints + x] == WAYPOINT_UNREACHABLE) ||
|
|
(shortest_path[x * route_num_waypoints + z] == WAYPOINT_UNREACHABLE))
|
|
continue;
|
|
|
|
distance = shortest_path[y * route_num_waypoints + x] +
|
|
shortest_path[x * route_num_waypoints + z];
|
|
|
|
if (distance > WAYPOINT_MAX_DISTANCE)
|
|
distance = WAYPOINT_MAX_DISTANCE;
|
|
|
|
if ((distance < shortest_path[y * route_num_waypoints + z]) ||
|
|
(shortest_path[y * route_num_waypoints + z] == WAYPOINT_UNREACHABLE))
|
|
{
|
|
shortest_path[y * route_num_waypoints + z] = distance;
|
|
from_to[y * route_num_waypoints + z] = from_to[y * route_num_waypoints + x];
|
|
changed = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// load the waypoint route files (.wp1, .wp2, etc.) or generate them if
|
|
// they don't exist...
|
|
void WaypointRouteInit(void)
|
|
{
|
|
unsigned int index;
|
|
bool build_matrix[4];
|
|
int matrix;
|
|
unsigned int array_size;
|
|
unsigned int row;
|
|
int i, offset;
|
|
unsigned int a, b;
|
|
float distance;
|
|
unsigned short *pShortestPath, *pFromTo;
|
|
char msg[80];
|
|
unsigned int num_items;
|
|
FILE *bfp;
|
|
char filename[256];
|
|
char filename2[256];
|
|
char mapname[64];
|
|
|
|
if (num_waypoints == 0)
|
|
return;
|
|
|
|
// save number of current waypoints in case waypoints get added later
|
|
route_num_waypoints = num_waypoints;
|
|
|
|
strcpy(mapname, STRING(gpGlobals->mapname));
|
|
strcat(mapname, ".wpt");
|
|
|
|
UTIL_BuildFileName(filename, "maps", mapname);
|
|
|
|
build_matrix[0] = TRUE; // always build matrix 0 (non-team and team 1)
|
|
build_matrix[1] = FALSE;
|
|
build_matrix[2] = FALSE;
|
|
build_matrix[3] = FALSE;
|
|
|
|
// find out how many route matrixes to create...
|
|
for (index=0; index < route_num_waypoints; index++)
|
|
{
|
|
if (waypoints[index].flags & W_FL_TEAM_SPECIFIC)
|
|
{
|
|
if ((waypoints[index].flags & W_FL_TEAM) == 0x01) // team 2?
|
|
build_matrix[1] = TRUE;
|
|
|
|
if ((waypoints[index].flags & W_FL_TEAM) == 0x02) // team 3?
|
|
build_matrix[2] = TRUE;
|
|
|
|
if ((waypoints[index].flags & W_FL_TEAM) == 0x03) // team 4?
|
|
build_matrix[3] = TRUE;
|
|
}
|
|
}
|
|
|
|
array_size = route_num_waypoints * route_num_waypoints;
|
|
|
|
for (matrix=0; matrix < 4; matrix++)
|
|
{
|
|
if (build_matrix[matrix])
|
|
{
|
|
char ext_str[5]; // ".wpX\0"
|
|
int file1, file2;
|
|
struct stat stat1, stat2;
|
|
|
|
sprintf(ext_str, ".wp%d", matrix+1);
|
|
|
|
strcpy(mapname, STRING(gpGlobals->mapname));
|
|
strcat(mapname, ext_str);
|
|
|
|
UTIL_BuildFileName(filename2, "maps", mapname);
|
|
|
|
if (access(filename2, 0) == 0) // does the .wpX file exist?
|
|
{
|
|
file1 = open(filename, O_RDONLY);
|
|
file2 = open(filename2, O_RDONLY);
|
|
|
|
fstat(file1, &stat1);
|
|
fstat(file2, &stat2);
|
|
|
|
close(file1);
|
|
close(file2);
|
|
|
|
if (stat1.st_mtime < stat2.st_mtime) // is .wpt older than .wpX file?
|
|
{
|
|
sprintf(msg, "loading HPB bot waypoint paths for team %d\n", matrix+1);
|
|
ALERT(at_console, msg);
|
|
|
|
shortest_path[matrix] = (unsigned short *)malloc(sizeof(unsigned short) * array_size);
|
|
|
|
if (shortest_path[matrix] == NULL)
|
|
ALERT(at_error, "HPB_bot - Error allocating memory for shortest path!");
|
|
|
|
from_to[matrix] = (unsigned short *)malloc(sizeof(unsigned short) * array_size);
|
|
|
|
if (from_to[matrix] == NULL)
|
|
ALERT(at_error, "HPB_bot - Error allocating memory for from to matrix!");
|
|
|
|
bfp = fopen(filename2, "rb");
|
|
|
|
if (bfp != NULL)
|
|
{
|
|
num_items = fread(shortest_path[matrix], sizeof(unsigned short), array_size, bfp);
|
|
|
|
if (num_items != array_size)
|
|
{
|
|
// if couldn't read enough data, free memory to recalculate it
|
|
|
|
ALERT(at_console, "error reading enough path items, recalculating...\n");
|
|
|
|
free(shortest_path[matrix]);
|
|
shortest_path[matrix] = NULL;
|
|
|
|
free(from_to[matrix]);
|
|
from_to[matrix] = NULL;
|
|
}
|
|
else
|
|
{
|
|
num_items = fread(from_to[matrix], sizeof(unsigned short), array_size, bfp);
|
|
|
|
if (num_items != array_size)
|
|
{
|
|
// if couldn't read enough data, free memory to recalculate it
|
|
|
|
ALERT(at_console, "error reading enough path items, recalculating...\n");
|
|
|
|
free(shortest_path[matrix]);
|
|
shortest_path[matrix] = NULL;
|
|
|
|
free(from_to[matrix]);
|
|
from_to[matrix] = NULL;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ALERT(at_console, "HPB_bot - Error reading waypoint paths!\n");
|
|
|
|
free(shortest_path[matrix]);
|
|
shortest_path[matrix] = NULL;
|
|
|
|
free(from_to[matrix]);
|
|
from_to[matrix] = NULL;
|
|
}
|
|
|
|
fclose(bfp);
|
|
}
|
|
}
|
|
|
|
if (shortest_path[matrix] == NULL)
|
|
{
|
|
sprintf(msg, "calculating HPB bot waypoint paths for team %d...\n", matrix+1);
|
|
ALERT(at_console, msg);
|
|
|
|
shortest_path[matrix] = (unsigned short *)malloc(sizeof(unsigned short) * array_size);
|
|
|
|
if (shortest_path[matrix] == NULL)
|
|
ALERT(at_error, "HPB_bot - Error allocating memory for shortest path!");
|
|
|
|
from_to[matrix] = (unsigned short *)malloc(sizeof(unsigned short) * array_size);
|
|
|
|
if (from_to[matrix] == NULL)
|
|
ALERT(at_error, "HPB_bot - Error allocating memory for from to matrix!");
|
|
|
|
pShortestPath = shortest_path[matrix];
|
|
pFromTo = from_to[matrix];
|
|
|
|
for (index=0; index < array_size; index++)
|
|
pShortestPath[index] = WAYPOINT_UNREACHABLE;
|
|
|
|
for (index=0; index < route_num_waypoints; index++)
|
|
pShortestPath[index * route_num_waypoints + index] = 0; // zero diagonal
|
|
|
|
for (row=0; row < route_num_waypoints; row++)
|
|
{
|
|
if (paths[row] != NULL)
|
|
{
|
|
PATH *p = paths[row];
|
|
|
|
while (p)
|
|
{
|
|
i = 0;
|
|
|
|
while (i < MAX_PATH_INDEX)
|
|
{
|
|
if (p->index[i] != -1)
|
|
{
|
|
index = p->index[i];
|
|
|
|
// check if this is NOT team specific OR matches this team
|
|
if (!(waypoints[index].flags & W_FL_TEAM_SPECIFIC) ||
|
|
((waypoints[index].flags & W_FL_TEAM) == matrix))
|
|
{
|
|
distance = (waypoints[row].origin - waypoints[index].origin).Length();
|
|
|
|
if (distance > (float)WAYPOINT_MAX_DISTANCE)
|
|
distance = (float)WAYPOINT_MAX_DISTANCE;
|
|
|
|
if (distance > REACHABLE_RANGE)
|
|
{
|
|
sprintf(msg, "Waypoint path distance > %4.1f at from %d to %d\n",
|
|
REACHABLE_RANGE, row, index);
|
|
ALERT(at_console, msg);
|
|
}
|
|
else
|
|
{
|
|
offset = row * route_num_waypoints + index;
|
|
|
|
pShortestPath[offset] = (unsigned short)distance;
|
|
}
|
|
}
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
p = p->next; // go to next node in linked list
|
|
}
|
|
}
|
|
}
|
|
|
|
// run Floyd's Algorithm to generate the from_to matrix...
|
|
WaypointFloyds(pShortestPath, pFromTo);
|
|
|
|
for (a=0; a < route_num_waypoints; a++)
|
|
{
|
|
for (b=0; b < route_num_waypoints; b++)
|
|
if (pShortestPath[a * route_num_waypoints + b] == WAYPOINT_UNREACHABLE)
|
|
pFromTo[a * route_num_waypoints + b] = WAYPOINT_UNREACHABLE;
|
|
}
|
|
|
|
bfp = fopen(filename2, "wb");
|
|
|
|
if (bfp != NULL)
|
|
{
|
|
num_items = fwrite(shortest_path[matrix], sizeof(unsigned short), array_size, bfp);
|
|
|
|
if (num_items != array_size)
|
|
{
|
|
// if couldn't write enough data, close file and delete it
|
|
|
|
fclose(bfp);
|
|
unlink(filename2);
|
|
}
|
|
else
|
|
{
|
|
num_items = fwrite(from_to[matrix], sizeof(unsigned short), array_size, bfp);
|
|
|
|
fclose(bfp);
|
|
|
|
if (num_items != array_size)
|
|
{
|
|
// if couldn't write enough data, delete file
|
|
unlink(filename2);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ALERT(at_console, "HPB_bot - Error writing waypoint paths!\n");
|
|
}
|
|
|
|
sprintf(msg, "HPB bot waypoint path calculations for team %d complete!\n",matrix+1);
|
|
ALERT(at_console, msg);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// return the next waypoint index for a path from the Floyd matrix when
|
|
// going from a source waypoint index (src) to a destination waypoint
|
|
// index (dest)...
|
|
unsigned short WaypointRouteFromTo(int src, int dest, int team)
|
|
{
|
|
unsigned short *pFromTo;
|
|
|
|
if ((team < -1) || (team > 3))
|
|
return -1;
|
|
|
|
if (team == -1) // -1 means non-team play
|
|
team = 0;
|
|
|
|
if (from_to[team] == NULL) // if no team specific waypoints use team 0
|
|
team = 0;
|
|
|
|
if (from_to[team] == NULL) // if no route information just return
|
|
return -1;
|
|
|
|
pFromTo = from_to[team];
|
|
|
|
return pFromTo[src * route_num_waypoints + dest];
|
|
}
|
|
|
|
|
|
// return the total distance (based on the Floyd matrix) of a path from
|
|
// the source waypoint index (src) to the destination waypoint index
|
|
// (dest)...
|
|
int WaypointDistanceFromTo(int src, int dest, int team)
|
|
{
|
|
unsigned short *pShortestPath;
|
|
|
|
if ((team < -1) || (team > 3))
|
|
return -1;
|
|
|
|
if (team == -1) // -1 means non-team play
|
|
team = 0;
|
|
|
|
if (from_to[team] == NULL) // if no team specific waypoints use team 0
|
|
team = 0;
|
|
|
|
if (from_to[team] == NULL) // if no route information just return
|
|
return -1;
|
|
|
|
pShortestPath = shortest_path[team];
|
|
|
|
return (int)(pShortestPath[src * route_num_waypoints + dest]);
|
|
}
|
|
|