NS/dev/hitboxtest/source/HPB_bot/dlls/waypoint.cpp
2005-04-12 13:43:58 +00:00

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 "extdll.h"
#include "enginecallback.h"
#include "util.h"
#include "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]);
}