// HPB bot - botman's High Ping Bastard bot // // (http://planethalflife.com/botman/) // // waypoint.cpp // #ifndef __linux__ #include #endif #include #ifndef __linux__ #include #else #include #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]); }