// // C++ Implementation: bot_waypoint // // Description: Waypoint class for bots // // // Author: Rick // #include "bot.h" vec v_debuggoal = g_vecZero; #if defined AC_CUBE CACWaypointClass WaypointClass; #elif defined VANILLA_CUBE CCubeWaypointClass WaypointClass; #endif VAR(xhairwpsel, 0, 1, 1); extern block sel; #define curselection (xhairwpsel ? vec(sel.x, sel.y, S(sel.x, sel.y)->floor+2.0f) : vec(player1->o.x, player1->o.y, player1->o.z)) // Waypoint class begin CWaypointClass::CWaypointClass(void) : m_bDrawWaypoints(false), m_bDrawWPPaths(true), m_bAutoWaypoint(false), m_bAutoPlacePaths(true), m_bDrawWPText(false), m_vLastCreatedWP(g_vecZero), m_bFlooding(false), m_bFilteringNodes(false), m_iFloodStartTime(0), m_iCurFloodX(0), m_iCurFloodY(0), m_iFloodSize(0), m_iFilteredNodes(0), m_iWaypointCount(0) { loopi(MAX_MAP_GRIDS) { loopj(MAX_MAP_GRIDS) { while(!m_Waypoints[i][j].Empty()) delete m_Waypoints[i][j].Pop(); } } m_szMapName[0] = 0; } void CWaypointClass::Init() { Clear(); // Free previously allocated path memory loopi(MAX_MAP_GRIDS) { loopj(MAX_MAP_GRIDS) { while(!m_Waypoints[i][j].Empty()) delete m_Waypoints[i][j].Pop(); } } m_fPathDrawTime = 0.0; m_iWaypointCount = 0; m_vLastCreatedWP = g_vecZero; } void CWaypointClass::Clear() { for(int i=0;iConnectedWPs.DeleteAllNodes(); p->ConnectedWPsWithMe.DeleteAllNodes(); BotManager.DelWaypoint(p); delete p; } } } } //fixme TLinkedList::node_s *testx; // returns true if waypoints succesfull loaded bool CWaypointClass::LoadWaypoints() { FILE *bfp; char szWPFileName[64]; char filename[256]; waypoint_header_s header; short num, triggernr, yaw; int i, j, flags; vec from, to; strcpy(szWPFileName, m_szMapName); strcat(szWPFileName, ".wpt"); BotManager.MakeBotFileName(szWPFileName, "waypoints", NULL, filename); bfp = fopen(filename, "rb"); BotManager.m_sCurrentTriggerNr = -1; // if file exists, read the waypoint structure from it if (bfp != NULL) { fread(&header, sizeof(header), 1, bfp); header.szFileType[10] = 0; if (strcmp(header.szFileType, "cube_bot") == 0) { header.szMapName[31] = 0; if (strcmp(header.szMapName, m_szMapName) == 0) { Init(); // remove any existing waypoints // Check which waypoint file version this is, handle each of them different if (header.iFileVersion == 1) { // First version, works with an big array and has a limit waypoint_version_1_s WPs[1024]; int path_index; m_iWaypointCount = header.iWPCount; for (i=0; i < header.iWPCount; i++) { fread(&WPs[i], sizeof(WPs[0]), 1, bfp); WPs[i].ConnectedWPs.Reset(); // We get garbage when we read this from // a file, so just clear it // Convert to new waypoint structure node_s *pNode = new node_s(WPs[i].v_origin, WPs[i].iFlags, 0); short x, y; GetNodeIndexes(pNode->v_origin, &x, &y); m_Waypoints[x][y].AddNode(pNode); BotManager.AddWaypoint(pNode); } // read and add waypoint paths... for (i=0; i < m_iWaypointCount; i++) { // read the number of paths from this node... fread(&num, sizeof(num), 1, bfp); // See which waypoint the current is node_s *pCurrent = GetWaypointFromVec(WPs[i].v_origin); if (!pCurrent) { conoutf("Error: NULL path in waypoint file"); continue; } for (j=0; j < num; j++) { fread(&path_index, sizeof(path_index), 1, bfp); // See which waypoint this is node_s *pTo = GetWaypointFromVec(WPs[path_index].v_origin); if (!pTo) { conoutf("Error: NULL path in waypoint file"); continue; } AddPath(pCurrent, pTo); } } conoutf("Old waypoint version(%d) converted to new version(%d)", header.iFileVersion, WAYPOINT_VERSION); conoutf("Use the wpsave command to convert permently"); } else if (header.iFileVersion == 2) { m_iWaypointCount = header.iWPCount; for (i=0; i < header.iWPCount; i++) { fread(&from, sizeof(from), 1, bfp); // Read origin fread(&flags, sizeof(flags), 1, bfp); // Read waypoint flags fread(&triggernr, sizeof(triggernr), 1, bfp); // Read trigger nr fread(&yaw, sizeof(yaw), 1, bfp); // Read target yaw node_s *pNode = new node_s(from, flags, triggernr, yaw); short x, y; GetNodeIndexes(pNode->v_origin, &x, &y); m_Waypoints[x][y].AddNode(pNode); BotManager.AddWaypoint(pNode); if ((BotManager.m_sCurrentTriggerNr == -1) || (triggernr < BotManager.m_sCurrentTriggerNr)) BotManager.m_sCurrentTriggerNr = triggernr; } for (i=0; i < header.iWPCount; i++) { // read the number of paths from this node... fread(&num, sizeof(num), 1, bfp); fread(&from, sizeof(from), 1, bfp); if (!num) continue; node_s *pCurrent = GetWaypointFromVec(from); if (!pCurrent) { conoutf("Error: NULL path in waypoint file"); for(j=0;j WAYPOINT_VERSION) { conoutf("Error: Waypoint file is newer then current, upgrade cube bot."); fclose(bfp); return false; } else { conoutf("Error: Unknown waypoint version for cube bot"); fclose(bfp); return false; } } else { conoutf("Waypoints aren't for map %s but for map %s", m_szMapName, header.szMapName); fclose(bfp); return false; } } else { conoutf("Waypoint file isn't for cube bot"); //conoutf("Header FileType: %s", int(header.szFileType)); fclose(bfp); return false; } fclose(bfp); //RouteInit(); } else { conoutf("Waypoint file %s does not exist", (filename)); return false; } if (BotManager.m_sCurrentTriggerNr == -1) BotManager.m_sCurrentTriggerNr = 0; ReCalcCosts(); conoutf("Waypoints for map %s loaded", (m_szMapName)); testx = m_Waypoints[1][1].pNodeList; return true; } void CWaypointClass::SaveWaypoints() { char filename[256]; char mapname[64]; waypoint_header_s header; short int num; TLinkedList::node_s *pPath; strcpy(header.szFileType, "cube_bot"); header.iFileVersion = WAYPOINT_VERSION; header.iFileFlags = 0; // not currently used header.iWPCount = m_iWaypointCount; memset(header.szMapName, 0, sizeof(header.szMapName)); strncpy(header.szMapName, m_szMapName, 31); header.szMapName[31] = 0; strcpy(mapname, m_szMapName); strcat(mapname, ".wpt"); BotManager.MakeBotFileName(mapname, "waypoints", NULL, filename); FILE *bfp = fopen(filename, "wb"); if (!bfp) { conoutf("Error writing waypoint file, check if the directory \"bot/waypoint\" exists and " "the right permissions are set"); return; } // write the waypoint header to the file... fwrite(&header, sizeof(header), 1, bfp); // write the waypoint data to the file... loopi(MAX_MAP_GRIDS) { loopj(MAX_MAP_GRIDS) { TLinkedList::node_s *p = m_Waypoints[i][j].GetFirst(); while(p) { fwrite(&p->Entry->v_origin, sizeof(p->Entry->v_origin), 1, bfp); // Write origin fwrite(&p->Entry->iFlags, sizeof(p->Entry->iFlags), 1, bfp); // Write waypoint flags fwrite(&p->Entry->sTriggerNr, sizeof(p->Entry->sTriggerNr), 1, bfp); // Write trigger nr fwrite(&p->Entry->sYaw, sizeof(p->Entry->sYaw), 1, bfp); // Write target yaw p = p->next; } } } loopi(MAX_MAP_GRIDS) { loopj(MAX_MAP_GRIDS) { TLinkedList::node_s *p = m_Waypoints[i][j].GetFirst(); while(p) { // save the waypoint paths... // count the number of paths from this node... pPath = p->Entry->ConnectedWPs.GetFirst(); num = p->Entry->ConnectedWPs.NodeCount(); fwrite(&num, sizeof(num), 1, bfp); // write the count fwrite(&p->Entry->v_origin, sizeof(p->Entry->v_origin), 1, bfp); // write the origin of this path // now write out each path... while (pPath != NULL) { fwrite(&pPath->Entry->v_origin, sizeof(pPath->Entry->v_origin), 1, bfp); pPath = pPath->next; // go to next node in linked list } p = p->next; } } } fclose(bfp); } bool CWaypointClass::LoadWPExpFile() { FILE *bfp; char szWPFileName[64]; char filename[256]; waypoint_header_s header; short int num; int i, j; vec from, to; if (m_iWaypointCount == 0) return false; strcpy(szWPFileName, m_szMapName); strcat(szWPFileName, ".exp"); BotManager.MakeBotFileName(szWPFileName, "waypoints", NULL, filename); bfp = fopen(filename, "rb"); // if file exists, read the waypoint structure from it if (bfp != NULL) { fread(&header, sizeof(header), 1, bfp); header.szFileType[10] = 0; if (strcmp(header.szFileType, "cube_bot") == 0) { header.szMapName[31] = 0; if (strcmp(header.szMapName, m_szMapName) == 0) { // Check which waypoint file version this is, handle each of them different if (header.iFileVersion == 1) { for (i=0; i < header.iWPCount; i++) { // read the number of paths from this node... fread(&num, sizeof(num), 1, bfp); fread(&from, sizeof(vec), 1, bfp); if (!num) continue; node_s *pCurrent = GetWaypointFromVec(from); if (!pCurrent) { conoutf("Error: NULL node in waypoint experience file"); continue; } for (j=0; j < num; j++) { fread(&to, sizeof(vec), 1, bfp); node_s *p = GetWaypointFromVec(to); if (p) { pCurrent->FailedGoalList.AddNode(p); } } } } else if (header.iFileVersion > EXP_WP_VERSION) { conoutf("Error: Waypoint experience file is newer then current, upgrade cube bot."); fclose(bfp); return false; } else { conoutf("Error: Unknown waypoint experience file version for cube bot"); fclose(bfp); return false; } } else { conoutf("Waypoint experience file isn't for map %s but for map %s", (m_szMapName), (header.szMapName)); fclose(bfp); return false; } } else { conoutf("Waypoint experience file isn't for cube bot"); //conoutf("Header FileType: %s", int(header.szFileType)); fclose(bfp); return false; } fclose(bfp); //RouteInit(); } else { conoutf("Waypoint experience file %s does not exist", (filename)); return false; } conoutf("Waypoint experience file for map %s loaded", (m_szMapName)); return true; } void CWaypointClass::SaveWPExpFile() { if (!m_iWaypointCount) return; char filename[256]; char mapname[64]; waypoint_header_s header; short int num; strcpy(header.szFileType, "cube_bot"); header.iFileVersion = EXP_WP_VERSION; header.iFileFlags = 0; // not currently used header.iWPCount = m_iWaypointCount; memset(header.szMapName, 0, sizeof(header.szMapName)); strncpy(header.szMapName, m_szMapName, 31); header.szMapName[31] = 0; strcpy(mapname, m_szMapName); strcat(mapname, ".exp"); BotManager.MakeBotFileName(mapname, "waypoints", NULL, filename); FILE *bfp = fopen(filename, "wb"); if (!bfp) { conoutf("Error writing waypoint experience file, check if the directory \"bot/waypoint\" exists and " "the right permissions are set"); return; } // write the waypoint header to the file... fwrite(&header, sizeof(header), 1, bfp); // save the waypoint experience data... loopi(MAX_MAP_GRIDS) { loopj(MAX_MAP_GRIDS) { TLinkedList::node_s *p = m_Waypoints[i][j].GetFirst(); while(p) { // count the number of paths from this node... TLinkedList::node_s *p2 = p->Entry->FailedGoalList.GetFirst(); num = p->Entry->FailedGoalList.NodeCount(); if (!num || !p2) continue; fwrite(&num, sizeof(num), 1, bfp); // write the count fwrite(&p->Entry->v_origin, sizeof(vec), 1, bfp); // write the origin of this node while (p2 != NULL) { // Write out the node which a bot can't reach with the current node fwrite(&p2->Entry->v_origin, sizeof(vec), 1, bfp); p2 = p2->next; // go to next node in linked list } p = p->next; } } } fclose(bfp); } void CWaypointClass::Think() { if (dedserv) return; #ifdef WP_FLOOD FloodThink(); #endif if (m_bAutoWaypoint) // is auto waypoint on? { // find the distance from the last used waypoint float flDistance = GetDistance(m_vLastCreatedWP, player1->o); bool NoLastCreatedWP = ((m_vLastCreatedWP.x == 0) && (m_vLastCreatedWP.y == 0) && (m_vLastCreatedWP.z == 0)); if ((flDistance > 3.0f) || NoLastCreatedWP) { // check that no other reachable waypoints are nearby... if (!GetNearestWaypoint(player1, 10.0f)) { AddWaypoint(player1->o, true); // place a waypoint here } } } // Draw info for nearest waypoint? if (m_bDrawWPText) { node_s *nearestwp = GetNearestWaypoint(player1, 20.0f); #ifdef WP_FLOOD if (!nearestwp) nearestwp = GetNearestFloodWP(player1, 10.0f); #endif if (nearestwp) { char szWPInfo[256]; sprintf(szWPInfo, "Distance nearest waypoint: %f", GetDistance(player1->o, nearestwp->v_origin)); AddScreenText(szWPInfo); strcpy(szWPInfo, "Flags: "); if (nearestwp->iFlags & W_FL_TELEPORT) strcat(szWPInfo, "Teleport "); if (nearestwp->iFlags & W_FL_TELEPORTDEST) strcat(szWPInfo, "Teleport destination "); if (nearestwp->iFlags & W_FL_JUMP) strcat(szWPInfo, "Jump "); if (nearestwp->iFlags & W_FL_TRIGGER) { char sz[32]; sprintf(sz, "Trigger(nr %d) ", nearestwp->sTriggerNr); strcat(szWPInfo, sz); } if (nearestwp->iFlags & W_FL_INTAG) strcat(szWPInfo, "In tagged cube(s) "); if (strlen(szWPInfo) == 7) strcat(szWPInfo, "None"); AddScreenText(szWPInfo); if (nearestwp->sYaw != -1) AddScreenText("yaw %d", nearestwp->sYaw); sprintf(szWPInfo, "Waypoint has %d connections", nearestwp->ConnectedWPs.NodeCount()); AddScreenText(szWPInfo); AddScreenText("Base Cost: %d", nearestwp->sCost); } } if (m_bDrawWaypoints) // display the waypoints if turned on... { DrawNearWaypoints(); } } void CWaypointClass::DrawNearWaypoints() { TLinkedList::node_s *pNode; node_s *pNearest = NULL; short i, j, MinI, MaxI, MinJ, MaxJ, Offset = (short)ceilf(15.0f / MAX_MAP_GRIDS); float flNearestDist = 9999.99f, flDist; GetNodeIndexes(player1->o, &i, &j); MinI = i - Offset; MaxI = i + Offset; MinJ = j - Offset; MaxJ = j + Offset; if (MinI < 0) MinI = 0; if (MaxI > MAX_MAP_GRIDS - 1) MaxI = MAX_MAP_GRIDS - 1; if (MinJ < 0) MinJ = 0; if (MaxJ > MAX_MAP_GRIDS - 1) MaxJ = MAX_MAP_GRIDS - 1; node_s *nearestwp = WaypointClass.GetNearestWaypoint(curselection, 20.0f); #ifdef WP_FLOOD if (!nearestwp) nearestwp = GetNearestFloodWP(player1, 20.0f); #endif for (int x=MinI;x<=MaxI;x++) { for(int y=MinJ;y<=MaxJ;y++) { pNode = m_Waypoints[x][y].GetFirst(); while(pNode) { flDist = GetDistance(worldpos, pNode->Entry->v_origin); if (flDist <= 15) { vec o = pNode->Entry->v_origin; vec e = o; o.z -= 2; e.z += 2; if (pNode->Entry->iFlags & W_FL_JUMP) { // draw a red waypoint linestyle(2.5f, 0xFF, 0x40, 0x40); } else if (nearestwp == pNode->Entry) { // draw a green waypoint linestyle(2.5f, 0x40, 0xFF, 0x40); } else { // draw a blue waypoint linestyle(2.5f, 0x40, 0x40, 0xFF); } line(int(o.x), int(o.y), int(o.z), int(e.x), int(e.y), int(e.z)); if (flNearestDist > flDist) { flNearestDist = flDist; pNearest = pNode->Entry; } } pNode = pNode->next; } } } if (pNearest) { pNode = pNearest->ConnectedWPs.GetFirst(); linestyle(2.0f, 0xFF, 0xFF, 0xFF); while(pNode) { vec o = pNode->Entry->v_origin; vec e = pNearest->v_origin; line(int(o.x), int(o.y), int(o.z), int(e.x), int(e.y), int(e.z)); pNode = pNode->next; } // Has this waypoint an target yaw? if (pNearest->sYaw != -1) { vec angles(0, pNearest->sYaw, 0); vec from = pNearest->v_origin, end = pNearest->v_origin; vec forward, right, up; from.z = end.z = (pNearest->v_origin.z-1.0f); AnglesToVectors(angles, forward, right, up); forward.mul(5.0f); end.add(forward); linestyle(2.0f, 0xFF, 0x40, 0x40); line(int(from.x), int(from.y), int(from.z), int(end.x), int(end.y), int(end.z)); } } #ifndef RELEASE_BUILD // check if path waypointing is on... if (m_bDrawWPPaths) { // Draw path from first bot if (bots.length() && bots[0] && bots[0]->pBot && bots[0]->pBot->m_pCurrentWaypoint && bots[0]->pBot->m_pCurrentGoalWaypoint) { CBot *pB = bots[0]->pBot; if (!pB->m_bCalculatingAStarPath && !pB->m_AStarNodeList.Empty()) { TLinkedList::node_s *pNode = pB->m_AStarNodeList.GetFirst(), *pNext; linestyle(2.5f, 0xFF, 0x40, 0x40); line((int)pB->m_pCurrentWaypoint->pNode->v_origin.x, (int)pB->m_pCurrentWaypoint->pNode->v_origin.y, (int)pB->m_pCurrentWaypoint->pNode->v_origin.z, (int)pNode->Entry->pNode->v_origin.x, (int)pNode->Entry->pNode->v_origin.y, (int)pNode->Entry->pNode->v_origin.z); while(pNode && pNode->next) { pNext = pNode->next; vec &v1 = pNode->Entry->pNode->v_origin; vec &v2 = pNext->Entry->pNode->v_origin; line(int(v1.x), int(v1.y), int(v1.z), int(v2.x), int(v2.y), int(v2.z)); pNode = pNode->next; } } } } if (intermission) return; /*for(int i=0;iPrevLocations.prevloc[i]==g_vecZero) continue; vec v1 = player1->PrevLocations.prevloc[i]; v1.z -= 1.0f; vec v2 = v1; v2.z += 2.0f; linestyle(2.5f, 0xFF, 0x40, 0x40); line(int(v1.x), int(v1.y), int(v1.z), int(v2.x), int(v2.y), int(v2.z)); }*/ #endif } // Add waypoint at location o, returns pointer of created wp node_s *CWaypointClass::AddWaypoint(vec o, bool connectwp) { short x, y; int flags = 0; if (S((int)o.x, (int)o.y)->tag) flags |= W_FL_INTAG; node_s *pNode = new node_s(o, flags, 0); m_vLastCreatedWP = o; GetNodeIndexes(o, &x, &y); m_Waypoints[x][y].AddNode(pNode); BotManager.AddWaypoint(pNode); m_iWaypointCount++; if (connectwp && m_bAutoPlacePaths) { // Connect new waypoint with other near waypoints. loopi(MAX_MAP_GRIDS) { loopj(MAX_MAP_GRIDS) { TLinkedList::node_s *p = m_Waypoints[i][j].GetFirst(); while(p) { if (p->Entry == pNode) { p = p->next; continue; // skip the waypoint that was just added } // check if the waypoint is reachable from the new one (one-way) if (WPIsReachable(o, p->Entry->v_origin)) AddPath(pNode, p->Entry); // Add a path from a to b if (WPIsReachable(p->Entry->v_origin, pNode->v_origin)) AddPath(p->Entry, pNode); // Add a path from b to a p = p->next; } } } } return pNode; } void CWaypointClass::DeleteWaypoint(vec v_src) { node_s *pWP; pWP = GetNearestWaypoint(v_src, 7.0f); if (!pWP) { conoutf("Error: Couldnīt find near waypoint"); return; } BotManager.DelWaypoint(pWP); // delete any paths that lead to this index... DeletePath(pWP); short x, y; GetNodeIndexes(pWP->v_origin, &x, &y); m_Waypoints[x][y].DeleteEntry(pWP); pWP->ConnectedWPs.DeleteAllNodes(); pWP->ConnectedWPsWithMe.DeleteAllNodes(); m_iWaypointCount--; delete pWP; } void CWaypointClass::AddPath(node_s *pWP1, node_s *pWP2) { pWP1->ConnectedWPs.AddNode(pWP2); pWP2->ConnectedWPsWithMe.AddNode(pWP1); } // Deletes all paths connected to the given waypoint void CWaypointClass::DeletePath(node_s *pWP) { TLinkedList::node_s *p; loopi(MAX_MAP_GRIDS) { loopj(MAX_MAP_GRIDS) { p = m_Waypoints[i][j].GetFirst(); while (p) { p->Entry->ConnectedWPs.DeleteEntry(pWP); pWP->ConnectedWPsWithMe.DeleteEntry(p->Entry); p = p->next; } } } } // Deletes path between 2 waypoints(1 way) void CWaypointClass::DeletePath(node_s *pWP1, node_s *pWP2) { pWP1->ConnectedWPs.DeleteEntry(pWP2); pWP2->ConnectedWPsWithMe.DeleteEntry(pWP1); } void CWaypointClass::ManuallyCreatePath(vec v_src, int iCmd, bool TwoWay) { static node_s *waypoint1 = NULL; // initialized to unassigned static node_s *waypoint2 = NULL; // initialized to unassigned if (iCmd == 1) // assign source of path { waypoint1 = GetNearestWaypoint(v_src, 7.0f); if (!waypoint1) { conoutf("Error: Couldn't find near waypoint"); return; } return; } if (iCmd == 2) // assign dest of path and make path { if (!waypoint1) { conoutf("Error: First waypoint unset"); return; } waypoint2 = GetNearestWaypoint(v_src, 7.0f); if (!waypoint2) { conoutf("Error: Couldn't find near waypoint"); return; } AddPath(waypoint1, waypoint2); if (TwoWay) AddPath(waypoint2, waypoint1); } } void CWaypointClass::ManuallyDeletePath(vec v_src, int iCmd, bool TwoWay) { static node_s *waypoint1 = NULL; // initialized to unassigned static node_s *waypoint2 = NULL; // initialized to unassigned if (iCmd == 1) // assign source of path { waypoint1 = GetNearestWaypoint(v_src, 7.0f); if (!waypoint1) { conoutf("Error: Couldn't find near waypoint"); return; } return; } if (iCmd == 2) // assign dest of path and delete path { if (!waypoint1) { conoutf("Error: First waypoint unset"); return; } waypoint2 = GetNearestWaypoint(v_src, 7.0f); if (!waypoint2) { conoutf("Error: Couldn't find near waypoint"); return; } DeletePath(waypoint1, waypoint2); if (TwoWay) DeletePath(waypoint2, waypoint1); } } bool CWaypointClass::WPIsReachable(vec from, vec to) { traceresult_s tr; float curr_height, last_height; float distance = GetDistance(from, to); // is the destination close enough? if (distance < REACHABLE_RANGE) { if (IsVisible(from, to)) { if (UnderWater(from) && UnderWater(to)) { // No need to worry about heights in water return true; } /* if (to.z > (from.z + JUMP_HEIGHT)) { vec v_new_src = to; vec v_new_dest = to; v_new_dest.z = v_new_dest.z - (JUMP_HEIGHT + 1.0f); // check if we didn't hit anything, if so then it's in mid-air if (::IsVisible(v_new_src, v_new_dest, NULL)) { conoutf("to is in midair"); debugbeam(from, to); return false; // can't reach this one } } */ // check if distance to ground increases more than jump height // at points between from and to... vec v_temp = to; v_temp.sub(from); vec v_direction = Normalize(v_temp); // 1 unit long vec v_check = from; vec v_down = from; v_down.z = v_down.z - 100.0f; // straight down TraceLine(v_check, v_down, NULL, false, &tr); // height from ground last_height = GetDistance(v_check, tr.end); distance = GetDistance(to, v_check); // distance from goal while (distance > 2.0f) { // move 2 units closer to the goal v_temp = v_direction; v_temp.mul(2.0f); v_check.add(v_temp); v_down = v_check; v_down.z = v_down.z - 100.0f; TraceLine(v_check, v_down, NULL, false, &tr); curr_height = GetDistance(v_check, tr.end); // is the difference in the last height and the current height // higher that the jump height? if ((last_height - curr_height) >= JUMP_HEIGHT) { // can't get there from here... //conoutf("traces failed to to"); return false; } last_height = curr_height; distance = GetDistance(to, v_check); // distance from goal } return true; } } return false; } node_s *CWaypointClass::GetNearestWaypoint(vec v_src, float flRange) { TLinkedList::node_s *pNode; node_s *pNearest = NULL; short i, j, MinI, MaxI, MinJ, MaxJ, Offset = (short)ceil(flRange / MAX_MAP_GRIDS); float flNearestDist = 9999.99f, flDist; GetNodeIndexes(v_src, &i, &j); MinI = i - Offset; MaxI = i + Offset; MinJ = j - Offset; MaxJ = j + Offset; if (MinI < 0) MinI = 0; if (MaxI > MAX_MAP_GRIDS - 1) MaxI = MAX_MAP_GRIDS - 1; if (MinJ < 0) MinJ = 0; if (MaxJ > MAX_MAP_GRIDS - 1) MaxJ = MAX_MAP_GRIDS - 1; for (int x=MinI;x<=MaxI;x++) { for(int y=MinJ;y<=MaxJ;y++) { pNode = m_Waypoints[x][y].GetFirst(); while(pNode) { flDist = GetDistance(v_src, pNode->Entry->v_origin); if ((flDist < flNearestDist) && (flDist <= flRange)) { if (IsVisible(v_src, pNode->Entry->v_origin, NULL)) { pNearest = pNode->Entry; flNearestDist = flDist; } } pNode = pNode->next; } } } return pNearest; } node_s *CWaypointClass::GetNearestTriggerWaypoint(vec v_src, float flRange) { TLinkedList::node_s *pNode; node_s *pNearest = NULL; short i, j, MinI, MaxI, MinJ, MaxJ, Offset = (short)ceil(flRange / MAX_MAP_GRIDS); float flNearestDist = 9999.99f, flDist; GetNodeIndexes(v_src, &i, &j); MinI = i - Offset; MaxI = i + Offset; MinJ = j - Offset; MaxJ = j + Offset; if (MinI < 0) MinI = 0; if (MaxI > MAX_MAP_GRIDS - 1) MaxI = MAX_MAP_GRIDS - 1; if (MinJ < 0) MinJ = 0; if (MaxJ > MAX_MAP_GRIDS - 1) MaxJ = MAX_MAP_GRIDS - 1; for (int x=MinI;x<=MaxI;x++) { for(int y=MinJ;y<=MaxJ;y++) { pNode = m_Waypoints[x][y].GetFirst(); while(pNode) { if ((pNode->Entry->iFlags & W_FL_FLOOD) || !(pNode->Entry->iFlags & W_FL_TRIGGER)) { pNode = pNode->next; continue; } flDist = GetDistance(v_src, pNode->Entry->v_origin); if ((flDist < flNearestDist) && (flDist <= flRange)) { if (IsVisible(v_src, pNode->Entry->v_origin, NULL)) { pNearest = pNode->Entry; flNearestDist = flDist; } } pNode = pNode->next; } } } return pNearest; } node_s *CWaypointClass::GetWaypointFromVec(const vec &v_src) { static TLinkedList::node_s *pNode; static short x, y; GetNodeIndexes(v_src, &x, &y); pNode = m_Waypoints[x][y].GetFirst(); while(pNode) { if (pNode->Entry->v_origin==v_src) return pNode->Entry; pNode = pNode->next; } return NULL; } void CWaypointClass::CalcCost(node_s *pNode) { float flCost = 10.0f; // Check nearby cubes... int x = int(pNode->v_origin.x); int y = int(pNode->v_origin.y); int a, b, row; for (row=0;row<=1;row++) { if (row==0) b = y - 6; else b = y + 6; for (a=(x-6);a<=(x+6);a++) { if (OUTBORD(a, b)) continue; vec to(a, b, GetCubeFloor(a, b) + 1.0f); // See if there is a obstacle(cube or mapmodel) nearby traceresult_s tr; TraceLine(pNode->v_origin, to, NULL, false, &tr); if (tr.collided) { float flFraction = (GetDistance(pNode->v_origin, tr.end) / GetDistance(pNode->v_origin, to)); flCost += (1.0f-flFraction)*0.5f; } if ((abs(a) > 4) || (abs(b) > 4)) continue; vec from = to; to.z -= (JUMP_HEIGHT - 1.0f); TraceLine(from, to, NULL, false, &tr); if (!tr.collided) flCost += 0.5f; } if (row==0) a = x - 6; else a = x + 6; for (b=(y-6);b<=(y+6);b++) { if (OUTBORD(a, b)) continue; vec to(a, b, GetCubeFloor(a, b) + 1.0f); // See if there is a obstacle(cube or mapmodel) nearby traceresult_s tr; TraceLine(pNode->v_origin, to, NULL, false, &tr); if (tr.collided) { float flFraction = (GetDistance(pNode->v_origin, tr.end) / GetDistance(pNode->v_origin, to)); flCost += (1.0f-flFraction)*0.5f; } if ((abs(a) > 4) || (abs(b) > 4)) continue; vec from = to; to.z -= (JUMP_HEIGHT - 1.0f); TraceLine(from, to, NULL, false, &tr); if (!tr.collided) flCost += 0.5f; } } if (UnderWater(pNode->v_origin)) // Water is annoying flCost += 5.0f; pNode->sCost = (short)flCost; } void CWaypointClass::ReCalcCosts(void) { loopi(MAX_MAP_GRIDS) { loopj(MAX_MAP_GRIDS) { TLinkedList::node_s *p = m_Waypoints[i][j].GetFirst(); while(p) { CalcCost(p->Entry); p = p->next; } } } } #ifdef WP_FLOOD void CWaypointClass::StartFlood() { if (m_bFlooding) return; conoutf("Starting flood, this may take a while on large maps...."); m_bFlooding = true; m_iFloodStartTime = SDL_GetTicks(); m_iCurFloodX = m_iCurFloodY = MINBORD; m_iFloodSize = 0; } void CWaypointClass::FloodThink() { if (!m_bFlooding) return; static int x, y, count; count = 0; if (!m_bFilteringNodes) { // loop through ALL cubes and check if we should add a waypoint here for (x=m_iCurFloodX; x<(ssize-MINBORD); x+=4) { if (count >= 3) { AddScreenText("Flooding map with waypoints... %.2f %%", ((float)x / float(ssize-MINBORD)) * 100.0f); m_iCurFloodX = x; return; } count++; for (y=MINBORD; y<(ssize-MINBORD); y+=4) { vec from(x, y, GetCubeFloor(x, y)+2.0f); bool bFound = CanPlaceNodeHere(from); if (!bFound) { for (int a=x-2;a<=(x+2);a++) { for (int b=y-2;b<=(y+2);b++) { if (OUTBORD(a, b)) continue; if ((a==x) && (b==y)) continue; makevec(&from, a, b, GetCubeFloor(a, b) + 2.0f); if (CanPlaceNodeHere(from)) { bFound = true; break; } } if (bFound) break; } } if (!bFound) continue; // Add WP int flags = W_FL_FLOOD; if (S((int)from.x, (int)from.y)->tag) flags |= W_FL_INTAG; node_s *pWP = new node_s(from, flags, 0); short i, j; GetNodeIndexes(from, &i, &j); m_Waypoints[i][j].PushNode(pWP); BotManager.AddWaypoint(pWP); m_iFloodSize += sizeof(node_s); m_iWaypointCount++; // Connect with other nearby nodes ConnectFloodWP(pWP); } } } if (!m_bFilteringNodes) { m_bFilteringNodes = true; m_iCurFloodX = m_iCurFloodY = 0; m_iFilteredNodes = 0; } count = 0; // Filter all nodes which aren't connected to any other nodes for (x=m_iCurFloodX;x 3) { AddScreenText("Filtering useless waypoints and"); AddScreenText("adding costs... %.2f %%", ((float)x / float(MAX_MAP_GRIDS)) * 100.0f); m_iCurFloodX = x; return; } count++; for (y=0;y::node_s *pNode = m_Waypoints[x][y].GetFirst(), *pTemp; while(pNode) { if (pNode->Entry->ConnectedWPs.Empty() || pNode->Entry->ConnectedWPsWithMe.Empty()) { BotManager.DelWaypoint(pNode->Entry); pTemp = pNode; pNode = pNode->next; delete pTemp->Entry; m_Waypoints[x][y].DeleteNode(pTemp); m_iFilteredNodes++; m_iFloodSize -= sizeof(node_s); m_iWaypointCount--; continue; } else CalcCost(pNode->Entry); pNode = pNode->next; } } } // Done with flooding m_bFlooding = false; m_bFilteringNodes = false; //ReCalcCosts(); BotManager.PickNextTrigger(); m_iFloodSize += sizeof(m_Waypoints); conoutf("Added %d wps in %d milliseconds", m_iWaypointCount, SDL_GetTicks()-m_iFloodStartTime); conoutf("Filtered %d wps", m_iFilteredNodes); BotManager.CalculateMaxAStarCount(); char szSize[64]; sprintf(szSize, "Total size: %.2f Kb", float(m_iFloodSize)/1024.0f); conoutf(szSize); } bool CWaypointClass::CanPlaceNodeHere(const vec &from) { static short x, y, a, b; static traceresult_s tr; static vec to, v1, v2; x = short(from.x); y = short(from.y); sqr *s = S(x, y); if (SOLID(s)) { return false; } if (fabs((float)(s->ceil - s->floor)) < player1->radius) { return false; } if (GetNearestFloodWP(from, 2.0f, NULL)) return false; for (a=(x-1);a<=(x+1);a++) { for (b=(y-1);b<=(y+1);b++) { if ((x==a) && (y==b)) continue; if (OUTBORD(a, b)) return false; makevec(&v1, a, b, from.z); v2 = v1; v2.z -= 1000.0f; TraceLine(v1, v2, NULL, false, &tr, true); to = tr.end; if ((a >= (x-1)) && (a <= (x+1)) && (b >= (y-1)) && (b <= (y+1))) { if (!tr.collided || (to.z < (from.z-4.0f))) { return false; } } to.z += 2.0f; if (from.z < (to.z-JUMP_HEIGHT)) { return false; } TraceLine(from, to, NULL, false, &tr, true); if (tr.collided) return false; } } return true; } void CWaypointClass::ConnectFloodWP(node_s *pWP) { if (!pWP) return; static float flRange; static TLinkedList::node_s *pNode; static short i, j, MinI, MaxI, MinJ, MaxJ, x, y, Offset; static float flDist; static node_s *p; // Calculate range, based on distance to nearest node p = GetNearestFloodWP(pWP->v_origin, 15.0f, pWP, true); if (p) { flDist = GetDistance(pWP->v_origin, p->v_origin); flRange = min(flDist+2.0f, 15.0f); if (flRange < 5.0f) flRange = 5.0f; } else return; Offset = (short)ceil(flRange / MAX_MAP_GRIDS); GetNodeIndexes(pWP->v_origin, &i, &j); MinI = i - Offset; MaxI = i + Offset; MinJ = j - Offset; MaxJ = j + Offset; if (MinI < 0) MinI = 0; if (MaxI > MAX_MAP_GRIDS - 1) MaxI = MAX_MAP_GRIDS - 1; if (MinJ < 0) MinJ = 0; if (MaxJ > MAX_MAP_GRIDS - 1) MaxJ = MAX_MAP_GRIDS - 1; for (x=MinI;x<=MaxI;x++) { for(y=MinJ;y<=MaxJ;y++) { pNode = m_Waypoints[x][y].GetFirst(); while(pNode) { if (pNode->Entry == pWP) { pNode = pNode->next; continue; } flDist = GetDistance(pWP->v_origin, pNode->Entry->v_origin); if (flDist <= flRange) { if (IsVisible(pWP->v_origin, pNode->Entry->v_origin, NULL, true)) { // Connect a with b pWP->ConnectedWPs.AddNode(pNode->Entry); pNode->Entry->ConnectedWPsWithMe.AddNode(pWP); // Connect b with a pNode->Entry->ConnectedWPs.AddNode(pWP); pWP->ConnectedWPsWithMe.AddNode(pNode->Entry); m_iFloodSize += (2 * sizeof(node_s *)); } } pNode = pNode->next; } } } } node_s *CWaypointClass::GetNearestFloodWP(vec v_origin, float flRange, node_s *pIgnore, bool SkipTags) { TLinkedList::node_s *p; node_s *pNearest = NULL; short i, j, MinI, MaxI, MinJ, MaxJ, Offset = (short)ceil(flRange / MAX_MAP_GRIDS); float flNearestDist = 9999.99f, flDist; GetNodeIndexes(v_origin, &i, &j); MinI = i - Offset; MaxI = i + Offset; MinJ = j - Offset; MaxJ = j + Offset; if (MinI < 0) MinI = 0; if (MaxI > MAX_MAP_GRIDS - 1) MaxI = MAX_MAP_GRIDS - 1; if (MinJ < 0) MinJ = 0; if (MaxJ > MAX_MAP_GRIDS - 1) MaxJ = MAX_MAP_GRIDS - 1; for (int x=MinI;x<=MaxI;x++) { for(int y=MinJ;y<=MaxJ;y++) { p = m_Waypoints[x][y].GetFirst(); while(p) { if ((p->Entry == pIgnore) || (!(p->Entry->iFlags & W_FL_FLOOD))) { p = p->next; continue; } flDist = GetDistance(v_origin, p->Entry->v_origin); if ((flDist < flNearestDist) && (flDist <= flRange)) { if (IsVisible(v_origin, p->Entry->v_origin, NULL, SkipTags)) { pNearest = p->Entry; flNearestDist = flDist; } } p = p->next; } } } return pNearest; } node_s *CWaypointClass::GetNearestTriggerFloodWP(vec v_origin, float flRange) { TLinkedList::node_s *p; node_s *pNearest = NULL; short i, j, MinI, MaxI, MinJ, MaxJ, Offset = (short)ceil(flRange / MAX_MAP_GRIDS); float flNearestDist = 9999.99f, flDist; GetNodeIndexes(v_origin, &i, &j); MinI = i - Offset; MaxI = i + Offset; MinJ = j - Offset; MaxJ = j + Offset; if (MinI < 0) MinI = 0; if (MaxI > MAX_MAP_GRIDS - 1) MaxI = MAX_MAP_GRIDS - 1; if (MinJ < 0) MinJ = 0; if (MaxJ > MAX_MAP_GRIDS - 1) MaxJ = MAX_MAP_GRIDS - 1; for (int x=MinI;x<=MaxI;x++) { for(int y=MinJ;y<=MaxJ;y++) { p = m_Waypoints[x][y].GetFirst(); while(p) { if (!(p->Entry->iFlags & (W_FL_FLOOD | W_FL_TRIGGER))) { p = p->next; continue; } flDist = GetDistance(v_origin, p->Entry->v_origin); if ((flDist < flNearestDist) && (flDist <= flRange)) { if (IsVisible(v_origin, p->Entry->v_origin, NULL)) { pNearest = p->Entry; flNearestDist = flDist; } } p = p->next; } } } return pNearest; } void CWaypointClass::GetNodeIndexes(const vec &v_origin, short *i, short *j) { // Function code by cheesy and PMB //*i = abs((int)((int)(v_origin.x + (2*ssize)) / SECTOR_SIZE)); //*j = abs((int)((int)(v_origin.y + (2*ssize)) / SECTOR_SIZE)); //*i = (int)((v_origin.x) / ssize * MAX_MAP_GRIDS); //*j = (int)((v_origin.y) / ssize * MAX_MAP_GRIDS); *i = abs((int)((v_origin.x) / MAX_MAP_GRIDS)); *j = abs((int)((v_origin.y) / MAX_MAP_GRIDS)); if (*i > MAX_MAP_GRIDS - 1) *i = MAX_MAP_GRIDS - 1; if (*j > MAX_MAP_GRIDS - 1) *j = MAX_MAP_GRIDS - 1; } #endif // WP_FLOOD // Waypoint class end #if defined AC_CUBE // AC waypoint class begin void CACWaypointClass::StartFlood() { // UNDONE? CWaypointClass::StartFlood(); } // AC waypoint class end #elif defined VANILLA_CUBE // Cube waypoint class begin void CCubeWaypointClass::StartFlood() { CWaypointClass::StartFlood(); // Add wps at triggers and teleporters and their destination loopv(ents) { entity &e = ents[i]; if (OUTBORD(e.x, e.y)) continue; if (e.type == TELEPORT) { vec telepos = { e.x, e.y, S(e.x, e.y)->floor+player1->eyeheight }, teledestpos = g_vecZero; // Find the teleport destination int n = -1, tag = e.attr1, beenhere = -1; for(;;) { n = findentity(TELEDEST, n+1); if(n==beenhere || n<0) { conoutf("no teleport destination for tag %d", tag); break; }; if(beenhere<0) beenhere = n; if(ents[n].attr2==tag) { teledestpos.x = ents[n].x; teledestpos.y = ents[n].y; teledestpos.z = S(ents[n].x, ents[n].y)->floor+player1->eyeheight; break; } } if (vis(teledestpos, g_vecZero)) continue; int flags = (W_FL_FLOOD | W_FL_TELEPORT); if (S((int)telepos.x, (int)telepos.y)->tag) flags |= W_FL_INTAG; // Add waypoint at teleporter and teleport destination node_s *pWP = new node_s(telepos, flags, 0); short i, j; GetNodeIndexes(telepos, &i, &j); m_Waypoints[i][j].PushNode(pWP); BotManager.AddWaypoint(pWP); m_iFloodSize += sizeof(node_s); m_iWaypointCount++; flags = (W_FL_FLOOD | W_FL_TELEPORTDEST); if (S((int)teledestpos.x, (int)teledestpos.y)->tag) flags |= W_FL_INTAG; node_s *pWP2 = new node_s(teledestpos, flags, 0); GetNodeIndexes(teledestpos, &i, &j); m_Waypoints[i][j].PushNode(pWP2); BotManager.AddWaypoint(pWP2); m_iFloodSize += sizeof(node_s); m_iWaypointCount++; // Connect the teleporter waypoint with the teleport-destination waypoint(1 way) AddPath(pWP, pWP2); // Connect with other nearby nodes ConnectFloodWP(pWP); } else if (e.type == CARROT) { vec pos = { e.x, e.y, S(e.x, e.y)->floor+player1->eyeheight }; int flags = (W_FL_FLOOD | W_FL_TRIGGER); if (S(e.x, e.y)->tag) flags |= W_FL_INTAG; node_s *pWP = new node_s(pos, flags, 0); short i, j; GetNodeIndexes(pos, &i, &j); m_Waypoints[i][j].PushNode(pWP); BotManager.AddWaypoint(pWP); m_iFloodSize += sizeof(node_s); m_iWaypointCount++; // Connect with other nearby nodes ConnectFloodWP(pWP); } else if (e.type == MAPMODEL) { mapmodelinfo &mmi = getmminfo(e.attr2); if(!&mmi || !mmi.h || !mmi.rad) continue; float floor = (float)(S(e.x, e.y)->floor+mmi.zoff+e.attr3)+mmi.h; float x1 = e.x - mmi.rad; float x2 = e.x + mmi.rad; float y1 = e.y - mmi.rad; float y2 = e.y + mmi.rad; // UNDONE? for (float x=(x1+1.0f);x<=(x2-1.0f);x++) { for (float y=(y1+1.0f);y<=(y2-1.0f);y++) { vec from = { x, y, floor+2.0f }; if (GetNearestFloodWP(from, 2.0f, NULL)) continue; // Add WP int flags = W_FL_FLOOD; if (S((int)x, (int)y)->tag) flags |= W_FL_INTAG; node_s *pWP = new node_s(from, flags, 0); short i, j; GetNodeIndexes(from, &i, &j); m_Waypoints[i][j].PushNode(pWP); BotManager.AddWaypoint(pWP); m_iFloodSize += sizeof(node_s); m_iWaypointCount++; // Connect with other nearby nodes ConnectFloodWP(pWP); } } } } CWaypointClass::StartFlood(); } void CCubeWaypointClass::CreateWPsAtTeleporters() { loopv(ents) { entity &e = ents[i]; if (e.type != TELEPORT) continue; if (OUTBORD(e.x, e.y)) continue; vec telepos = { e.x, e.y, S(e.x, e.y)->floor+player1->eyeheight }, teledestpos = g_vecZero; // Find the teleport destination int n = -1, tag = e.attr1, beenhere = -1; for(;;) { n = findentity(TELEDEST, n+1); if(n==beenhere || n<0) { conoutf("no teleport destination for tag %d", tag); continue; }; if(beenhere<0) beenhere = n; if(ents[n].attr2==tag) { teledestpos.x = ents[n].x; teledestpos.y = ents[n].y; teledestpos.z = S(ents[n].x, ents[n].y)->floor+player1->eyeheight; break; } } if (vis(teledestpos, g_vecZero)) continue; // Add waypoint at teleporter and teleport destination node_s *telewp = AddWaypoint(telepos, false); node_s *teledestwp = AddWaypoint(teledestpos, false); if (telewp && teledestwp) { // Connect the teleporter waypoint with the teleport-destination waypoint(1 way) AddPath(telewp, teledestwp); // Flag waypoints telewp->iFlags = W_FL_TELEPORT; teledestwp->iFlags = W_FL_TELEPORTDEST; } } } void CCubeWaypointClass::CreateWPsAtTriggers() { loopv(ents) { entity &e = ents[i]; if (e.type != CARROT) continue; if (OUTBORD(e.x, e.y)) continue; vec pos = { e.x, e.y, S(e.x, e.y)->floor+player1->eyeheight }; node_s *wp = AddWaypoint(pos, false); if (wp) { // Flag waypoints wp->iFlags = W_FL_TRIGGER; } } } #endif // Cube waypoint class end // Waypoint commands begin void addwp(int autoconnect) { WaypointClass.SetWaypointsVisible(true); WaypointClass.AddWaypoint(curselection, autoconnect!=0); } COMMAND(addwp, ARG_1INT); void delwp(void) { WaypointClass.SetWaypointsVisible(true); WaypointClass.DeleteWaypoint(curselection); } COMMAND(delwp, ARG_NONE); void wpvisible(int on) { WaypointClass.SetWaypointsVisible(on!=0); } COMMAND(wpvisible, ARG_1INT); void wpsave(void) { WaypointClass.SaveWaypoints(); } COMMAND(wpsave, ARG_NONE); void wpload(void) { WaypointClass.LoadWaypoints(); } COMMAND(wpload, ARG_NONE); void wpclear(void) { WaypointClass.Clear(); } COMMAND(wpclear, ARG_NONE); void autowp(int on) { WaypointClass.SetWaypointsVisible(true); WaypointClass.SetAutoWaypoint(on!=0); } COMMAND(autowp, ARG_1INT); void wpinfo(int on) { WaypointClass.SetWaypointsVisible(true); WaypointClass.SetWPInfo(on!=0); } COMMAND(wpinfo, ARG_1INT); void addpath1way1(void) { WaypointClass.SetWaypointsVisible(true); WaypointClass.ManuallyCreatePath(curselection, 1, false); } COMMAND(addpath1way1, ARG_NONE); void addpath1way2(void) { WaypointClass.SetWaypointsVisible(true); WaypointClass.ManuallyCreatePath(curselection, 2, false); } COMMAND(addpath1way2, ARG_NONE); void addpath2way1(void) { WaypointClass.SetWaypointsVisible(true); WaypointClass.ManuallyCreatePath(curselection, 1, true); } COMMAND(addpath2way1, ARG_NONE); void addpath2way2(void) { WaypointClass.SetWaypointsVisible(true); WaypointClass.ManuallyCreatePath(curselection, 2, true); } COMMAND(addpath2way2, ARG_NONE); void delpath1way1(void) { WaypointClass.SetWaypointsVisible(true); WaypointClass.ManuallyDeletePath(curselection, 1, false); } COMMAND(delpath1way1, ARG_NONE); void delpath1way2(void) { WaypointClass.SetWaypointsVisible(true); WaypointClass.ManuallyDeletePath(curselection, 2, false); } COMMAND(delpath1way2, ARG_NONE); void delpath2way1(void) { WaypointClass.SetWaypointsVisible(true); WaypointClass.ManuallyDeletePath(curselection, 1, true); } COMMAND(delpath2way1, ARG_NONE); void delpath2way2(void) { WaypointClass.SetWaypointsVisible(true); WaypointClass.ManuallyDeletePath(curselection, 2, true); } COMMAND(delpath2way2, ARG_NONE); void setjumpwp(void) { node_s *wp = WaypointClass.GetNearestWaypoint(curselection, 20.0f); if (wp) { WaypointClass.SetWPFlags(wp, W_FL_JUMP); } } COMMAND(setjumpwp, ARG_NONE); void unsetjumpwp(void) { node_s *wp = WaypointClass.GetNearestWaypoint(curselection, 20.0f); if (wp) { WaypointClass.UnsetWPFlags(wp, W_FL_JUMP); } } COMMAND(unsetjumpwp, ARG_NONE); void setwptriggernr(int nr) { node_s *wp = WaypointClass.GetNearestWaypoint(curselection, 20.0f); if (wp) { WaypointClass.SetWPTriggerNr(wp, nr); } } COMMAND(setwptriggernr, ARG_1INT); void setwpyaw(void) { node_s *wp = WaypointClass.GetNearestWaypoint(curselection, 20.0f); if (wp) { WaypointClass.SetWPYaw(wp, short(player1->yaw)); } } COMMAND(setwpyaw, ARG_NONE); #ifdef WP_FLOOD void wpflood(void) { WaypointClass.StartFlood(); } COMMAND(wpflood, ARG_NONE); #endif #ifdef VANILLA_CUBE // Commands specific for cube void addtelewps(void) { WaypointClass.SetWaypointsVisible(true); WaypointClass.CreateWPsAtTeleporters(); } COMMAND(addtelewps, ARG_NONE); void addtriggerwps(void) { WaypointClass.SetWaypointsVisible(true); WaypointClass.CreateWPsAtTriggers(); } COMMAND(addtriggerwps, ARG_NONE); #endif // Debug functions #ifdef WP_FLOOD #ifndef RELEASE_BUILD void botsheadtome(void) { loopv(bots) { if (!bots[i] || !bots[i]->pBot) continue; bots[i]->pBot->GoToDebugGoal(player1->o); } } COMMAND(botsheadtome, ARG_NONE); void setdebuggoal(void) { v_debuggoal = player1->o; }; COMMAND(setdebuggoal, ARG_NONE); void botsheadtodebuggoal(void) { loopv(bots) { if (!bots[i] || !bots[i]->pBot) continue; bots[i]->pBot->GoToDebugGoal(v_debuggoal); } } COMMAND(botsheadtodebuggoal, ARG_NONE); #endif // RELEASE_BUILD #endif // WP_FLOOD // End debug functions // Waypoint commands end // Bot class begin bool CBot::FindWaypoint() { waypoint_s *wp, *wpselect; int index; float distance, min_distance[3]; waypoint_s *min_wp[3]; for (index=0; index < 3; index++) { min_distance[index] = 9999.0; min_wp[index] = NULL; } TLinkedList::node_s *pNode = m_pCurrentWaypoint->pNode->ConnectedWPs.GetFirst(); while (pNode) { if ((pNode->Entry->iFlags & W_FL_INTAG) && SOLID(S((int)pNode->Entry->v_origin.x, (int)pNode->Entry->v_origin.y))) { pNode = pNode->next; continue; } wp = GetWPFromNode(pNode->Entry); if (!wp) { pNode = pNode->next; continue; } // if index is not a current or recent previous waypoint... if ((wp != m_pCurrentWaypoint) && (wp != m_pPrevWaypoints[0]) && (wp != m_pPrevWaypoints[1]) && (wp != m_pPrevWaypoints[2]) && (wp != m_pPrevWaypoints[3]) && (wp != m_pPrevWaypoints[4])) { // find the distance from the bot to this waypoint distance = GetDistance(wp->pNode->v_origin); if (distance < min_distance[0]) { min_distance[2] = min_distance[1]; min_wp[2] = min_wp[1]; min_distance[1] = min_distance[0]; min_wp[1] = min_wp[0]; min_distance[0] = distance; min_wp[0] = wp; } else if (distance < min_distance [1]) { min_distance[2] = min_distance[1]; min_wp[2] = min_wp[1]; min_distance[1] = distance; min_wp[1] = wp; } else if (distance < min_distance[2]) { min_distance[2] = distance; min_wp[2] = wp; } } pNode = pNode->next; } wpselect = NULL; // about 20% of the time choose a waypoint at random // (don't do this any more often than every 10 seconds) if ((RandomLong(1, 100) <= 20) && (m_iRandomWaypointTime <= lastmillis)) { m_iRandomWaypointTime = lastmillis + 10000; if (min_wp[2]) index = RandomLong(0, 2); else if (min_wp[1]) index = RandomLong(0, 1); else if (min_wp[0]) index = 0; else return false; // no waypoints found! wpselect = min_wp[index]; } else { // use the closest waypoint that has been recently used wpselect = min_wp[0]; } if (wpselect) // was a waypoint found? { m_pPrevWaypoints[4] = m_pPrevWaypoints[3]; m_pPrevWaypoints[3] = m_pPrevWaypoints[2]; m_pPrevWaypoints[2] = m_pPrevWaypoints[1]; m_pPrevWaypoints[1] = m_pPrevWaypoints[0]; m_pPrevWaypoints[0] = m_pCurrentWaypoint; SetCurrentWaypoint(wpselect); return true; } return false; // couldn't find a waypoint } bool CBot::HeadToWaypoint() { if (!m_pCurrentWaypoint) return false; // Can't head to waypoint bool Touching = false; float WPDist = GetDistance(m_pCurrentWaypoint->pNode->v_origin); #ifndef RELEASE_BUILD if (m_pCurrentGoalWaypoint && m_vGoal==g_vecZero) condebug("Warning: m_vGoal unset"); #endif // did the bot run past the waypoint? (prevent the loop-the-loop problem) if ((m_fPrevWaypointDistance > 1.0) && (WPDist > m_fPrevWaypointDistance) && (WPDist <= 5.0f)) Touching = true; // bot needs to be close for jump and trigger waypoints else if ((m_pCurrentWaypoint->pNode->iFlags & W_FL_JUMP) || (m_pCurrentWaypoint->pNode->iFlags & W_FL_TRIGGER)) Touching = (WPDist <= 1.5f); else if (m_pCurrentWaypoint->pNode->iFlags & W_FL_TELEPORT) Touching = (WPDist <= 4.0f); // are we close enough to a target waypoint... else if (WPDist <= 3.0f) { if (!m_pCurrentGoalWaypoint || (m_pCurrentWaypoint != m_pCurrentGoalWaypoint) || IsVisible(m_vGoal) || (WPDist <= 1.5f)) Touching = true; // If the bot has a goal check if he can see his next wp if (m_pCurrentGoalWaypoint && (m_pCurrentGoalWaypoint != m_pCurrentWaypoint) && !m_AStarNodeList.Empty() && (WPDist >= 1.0f) && !IsVisible(m_AStarNodeList.GetFirst()->Entry->pNode->v_origin)) Touching = false; } // save current distance as previous m_fPrevWaypointDistance = WPDist; // Reached the waypoint? if (Touching) { // Does this waypoint has a targetyaw? if (m_pCurrentWaypoint->pNode->sYaw != -1) { // UNDONE: Inhuman m_pMyEnt->yaw = m_pMyEnt->targetyaw = m_pCurrentWaypoint->pNode->sYaw; } // Reached a jump waypoint? if (m_pCurrentWaypoint->pNode->iFlags & W_FL_JUMP) m_pMyEnt->jumpnext = true; m_fPrevWaypointDistance = 0.0f; // Does the bot has a goal? if (m_pCurrentGoalWaypoint) { if (m_pCurrentWaypoint != m_pCurrentGoalWaypoint) { if (m_AStarNodeList.Empty()) { if (!AStar()) { // Bot is calculating a new path, just stand still for now ResetMoveSpeed(); m_iWaypointTime += 200; return true; } else { if (m_AStarNodeList.Empty()) { //m_UnreachableNodes.PushNode(unreachable_node_s(m_pCurrentGoalWaypoint, gpGlobals->time)); return false; // Couldn't get a new wp to go to } } } m_pCurrentWaypoint = m_AStarNodeList.Pop(); if (!IsVisible(m_pCurrentWaypoint->pNode->v_origin)) //(!(m_pCurrentWaypoint->iFlags & W_FL_TELEPORT))) { //m_UnreachableNodes.PushNode(unreachable_node_s(m_pCurrentGoalWaypoint, gpGlobals->time)); condebug("Next WP not visible"); return false; } SetCurrentWaypoint(m_pCurrentWaypoint); } else { // Bot reached the goal waypoint but couldn't reach the goal itself // (otherwise we wouldn't be in this function) //m_UnreachableNodes.PushNode(unreachable_node_s(m_pCurrentGoalWaypoint, gpGlobals->time)); return false; } } else { short index = 4; bool status; // try to find the next waypoint while (((status = FindWaypoint()) == false) && (index > 0)) { // if waypoint not found, clear oldest previous index and try again m_pPrevWaypoints[index] = NULL; index--; } if (status == false) { ResetWaypointVars(); condebug("Couldn't find new random waypoint"); return false; } } m_iWaypointHeadPauseTime = lastmillis + 75; m_iWaypointTime += 75; } // keep turning towards the waypoint... /*if (m_pCurrentWaypoint->pNode->iFlags & W_FL_FLOOD) {UNDONE? vec aim = m_pCurrentWaypoint->pNode->v_origin; aim.x+=0.5f; aim.y+=0.5f; AimToVec(aim); } else*/ AimToVec(m_pCurrentWaypoint->pNode->v_origin); if (m_fYawToTurn <= 25.0f) m_iWaypointHeadLastTurnLessTime = lastmillis; // Bot had to turn much for a while? if ((m_iWaypointHeadLastTurnLessTime > 0) && (m_iWaypointHeadLastTurnLessTime < (lastmillis - 1000))) { m_iWaypointHeadPauseTime = lastmillis + 200; m_iWaypointTime += 200; } if (m_iWaypointHeadPauseTime >= lastmillis) { m_pMyEnt->move = 0; //conoutf("Pause in HeadToWaypoint()"); } else { // Check if bot has to jump vec from = m_pMyEnt->o; from.z -= (m_pMyEnt->eyeheight - 1.25f); float flEndDist; if (!IsVisible(from, FORWARD, 3.0f, false, &flEndDist) && (GetDistance(from, m_pCurrentWaypoint->pNode->v_origin) > flEndDist)) { m_pMyEnt->jumpnext = true; condebug("Low wall in HeadToWaypoint()"); } // Check if bot has to strafe if (m_iStrafeTime > lastmillis) SetMoveDir(m_iMoveDir, true); else { m_iStrafeTime = 0; m_iMoveDir = DIR_NONE; vec forward, up, right; AnglesToVectors(GetViewAngles(), forward, right, up); float flLeftDist = -1.0f, flRightDist = -1.0f; vec dir = right; bool bStrafe = false; int iStrafeDir = 0; dir.mul(m_pMyEnt->radius); // Check left from = m_pMyEnt->o; from.sub(dir); if (IsVisible(from, FORWARD, 3.0f, false, &flLeftDist)) flLeftDist = -1.0f; // Check right from = m_pMyEnt->o; from.add(dir); if (IsVisible(from, FORWARD, 3.0f, false, &flRightDist)) flRightDist = -1.0f; if ((flLeftDist != -1.0f) && (flRightDist != -1.0f)) { if (flLeftDist < flRightDist) { // Strafe right bStrafe = true; iStrafeDir = RIGHT; } else if (flRightDist < flLeftDist) { // Strafe left bStrafe = true; iStrafeDir = LEFT; } else { // Randomly choose a strafe direction bStrafe = true; if (RandomLong(0, 1)) iStrafeDir = LEFT; else iStrafeDir = RIGHT; } } else if (flLeftDist != -1.0f) { // Strafe right bStrafe = true; iStrafeDir = RIGHT; } else if (flRightDist != -1.0f) { // Strafe left bStrafe = true; iStrafeDir = LEFT; } if (bStrafe) { SetMoveDir(iStrafeDir, true); m_iMoveDir = iStrafeDir; m_iStrafeTime = lastmillis + RandomLong(30, 50); } } } return true; } // returns true when done or failure bool CBot::HeadToGoal() { // Does the bot has a goal(waypoint)? if (m_pCurrentGoalWaypoint) { if (ReachedGoalWP()) { return false; } else { if (CurrentWPIsValid()) { if (m_bCalculatingAStarPath) { // Bot is calculating a new path, just stand still for now ResetMoveSpeed(); m_iWaypointTime += 200; // Done calculating the path? if (AStar()) { if (m_AStarNodeList.Empty()) { return false; // Couldn't find a path } //else // SetCurrentWaypoint(m_AStarNodeList.Pop()); } else return true; // else just wait a little bit longer } //ResetMoveSpeed(); //return true; } else { // Current waypoint isn't reachable, search new one waypoint_s *pWP = NULL; #ifdef WP_FLOOD if (m_pCurrentGoalWaypoint->pNode->iFlags & W_FL_FLOOD) pWP = GetNearestFloodWP(8.0f); else #endif pWP = GetNearestWaypoint(15.0f); if (!pWP || (pWP == m_pCurrentWaypoint)) return false; SetCurrentWaypoint(pWP); if (AStar()) { if (m_AStarNodeList.Empty()) return false; } else { m_iWaypointTime += 200; ResetMoveSpeed(); return true; } } //debugbeam(m_pMyEnt->o, m_pCurrentWaypoint->pNode->v_origin); } } else return false; return HeadToWaypoint(); } // return true when done calculating bool CBot::AStar() { if (!m_pCurrentGoalWaypoint || !m_pCurrentWaypoint) { if (m_bCalculatingAStarPath) { m_bCalculatingAStarPath = false; BotManager.m_sUsingAStarBotsCount--; } return true; } // Ideas by PMB : // * Make locals static to speed up a bit // * MaxCycles per frame and make it fps dependent static int iMaxCycles; static int iCurrentCycles; static short newg; static waypoint_s *n, *n2; static TLinkedList::node_s *pPath = NULL; static bool bPathFailed; iMaxCycles = BotManager.m_iFrameTime / 10; if (iMaxCycles < 10) iMaxCycles = 10; //condebug("MaxCycles: %d", iMaxCycles); iCurrentCycles = 0; bPathFailed = false; if (!m_bCalculatingAStarPath) { if ((BotManager.m_sUsingAStarBotsCount+1) > BotManager.m_sMaxAStarBots) { return true; } BotManager.m_sUsingAStarBotsCount++; CleanAStarLists(false); m_pCurrentWaypoint->g[0] = m_pCurrentWaypoint->g[1] = 0; m_pCurrentWaypoint->pParent[0] = m_pCurrentWaypoint->pParent[1] = NULL; m_pCurrentGoalWaypoint->g[0] = m_pCurrentGoalWaypoint->g[1] = 0; m_pCurrentGoalWaypoint->pParent[0] = m_pCurrentGoalWaypoint->pParent[1] = NULL; m_AStarNodeList.DeleteAllNodes(); m_AStarOpenList[0].Clear(); m_AStarOpenList[1].Clear(); m_AStarClosedList[0].DeleteAllNodes(); m_AStarClosedList[1].DeleteAllNodes(); m_AStarOpenList[0].AddEntry(m_pCurrentWaypoint, GetDistance(m_pCurrentGoalWaypoint->pNode->v_origin)); m_AStarOpenList[1].AddEntry(m_pCurrentGoalWaypoint, GetDistance(m_pCurrentGoalWaypoint->pNode->v_origin)); m_pCurrentWaypoint->bIsOpen[0] = m_pCurrentGoalWaypoint->bIsOpen[1] = true; m_pCurrentWaypoint->bIsOpen[1] = m_pCurrentGoalWaypoint->bIsOpen[0] = false; m_pCurrentWaypoint->bIsClosed[0] = m_pCurrentGoalWaypoint->bIsClosed[1] = false; m_pCurrentWaypoint->bIsClosed[1] = m_pCurrentGoalWaypoint->bIsClosed[0] = false; } while(!m_AStarOpenList[0].Empty()) { if (iCurrentCycles >= (iMaxCycles/2)) { m_bCalculatingAStarPath = true; break; } n = m_AStarOpenList[0].Pop(); n->bIsOpen[0] = false; if (n->pNode->FailedGoalList.IsInList(m_pCurrentGoalWaypoint->pNode)) { bPathFailed = true; break; // Can't make path to goal } // Done with calculating if (n == m_pCurrentGoalWaypoint) { m_bCalculatingAStarPath = false; BotManager.m_sUsingAStarBotsCount--; while(n) { m_AStarNodeList.PushNode(n); n = n->pParent[0]; } CleanAStarLists(false); return true; } pPath = n->pNode->ConnectedWPs.GetFirst(); while(pPath) { if ((pPath->Entry->iFlags & W_FL_INTAG) && SOLID(S((int)pPath->Entry->v_origin.x, (int)pPath->Entry->v_origin.y))) { pPath = pPath->next; continue; } n2 = GetWPFromNode(pPath->Entry); if (!n2) { pPath = pPath->next; continue; } if (n2->bIsClosed[1]) { m_bCalculatingAStarPath = false; BotManager.m_sUsingAStarBotsCount--; while(n2) { m_AStarNodeList.AddNode(n2); n2 = n2->pParent[1]; } while(n) { m_AStarNodeList.PushNode(n); n = n->pParent[0]; } CleanAStarLists(false); return true; } newg = n->g[0] + (short)AStarCost(n, n2); if ((n2->g[0] <= newg) && (n2->bIsOpen[0] || n2->bIsClosed[0])) { pPath = pPath->next; continue; } n2->pParent[0] = n; n2->g[0] = newg; n2->bIsClosed[0] = false; if (!n2->bIsOpen[0]) { m_AStarOpenList[0].AddEntry(n2, n2->g[1] + GetDistance(n2->pNode->v_origin, m_pCurrentGoalWaypoint->pNode->v_origin)); n2->bIsOpen[0] = true; } pPath = pPath->next; } m_AStarClosedList[0].PushNode(n); n->bIsClosed[0] = true; iCurrentCycles++; } if (!bPathFailed) { while(!m_AStarOpenList[1].Empty()) { if (iCurrentCycles >= iMaxCycles) { m_bCalculatingAStarPath = true; return false; } n = m_AStarOpenList[1].Pop(); n->bIsOpen[1] = false; if (n->pNode->FailedGoalList.IsInList(m_pCurrentWaypoint->pNode)) { bPathFailed = true; break; // Can't make path to goal } // Done with calculating if (n == m_pCurrentWaypoint) { m_bCalculatingAStarPath = false; BotManager.m_sUsingAStarBotsCount--; while(n) { m_AStarNodeList.AddNode(n); n = n->pParent[1]; } CleanAStarLists(false); return true; } pPath = n->pNode->ConnectedWPsWithMe.GetFirst(); while(pPath) { if ((pPath->Entry->iFlags & W_FL_INTAG) && SOLID(S((int)pPath->Entry->v_origin.x, (int)pPath->Entry->v_origin.y))) { pPath = pPath->next; continue; } n2 = GetWPFromNode(pPath->Entry); if (!n2) { pPath = pPath->next; continue; } if (n2->bIsClosed[0]) { m_bCalculatingAStarPath = false; BotManager.m_sUsingAStarBotsCount--; while(n2) { m_AStarNodeList.PushNode(n2); n2 = n2->pParent[0]; } while(n) { m_AStarNodeList.AddNode(n); n = n->pParent[1]; } CleanAStarLists(false); return true; } newg = n->g[1] + (short)AStarCost(n, n2); if ((n2->g[1] <= newg) && (n2->bIsOpen[1] || n2->bIsClosed[1])) { pPath = pPath->next; continue; } n2->pParent[1] = n; n2->g[1] = newg; n2->bIsClosed[1] = false; if (!n2->bIsOpen[1]) { m_AStarOpenList[1].AddEntry(n2, n2->g[1] + GetDistance(n2->pNode->v_origin, m_pCurrentWaypoint->pNode->v_origin)); n2->bIsOpen[1] = true; } pPath = pPath->next; } m_AStarClosedList[1].PushNode(n); n->bIsClosed[1] = true; iCurrentCycles++; } } // Failed making path condebug("Path failed"); CleanAStarLists(true); m_bCalculatingAStarPath = false; BotManager.m_sUsingAStarBotsCount--; return true; } float CBot::AStarCost(waypoint_s *pWP1, waypoint_s *pWP2) { // UNDONE? return (GetDistance(pWP1->pNode->v_origin, pWP2->pNode->v_origin) * pWP2->pNode->sCost); } void CBot::CleanAStarLists(bool bPathFailed) { while(m_AStarOpenList[0].Empty() == false) { waypoint_s *p = m_AStarOpenList[0].Pop(); p->bIsOpen[0] = p->bIsOpen[1] = false; p->bIsClosed[0] = p->bIsClosed[1] = false; p->pParent[0] = p->pParent[1] = NULL; p->g[0] = p->g[1] = 0; } while(m_AStarOpenList[1].Empty() == false) { waypoint_s *p = m_AStarOpenList[1].Pop(); p->bIsOpen[0] = p->bIsOpen[1] = false; p->bIsClosed[0] = p->bIsClosed[1] = false; p->pParent[0] = p->pParent[1] = NULL; p->g[0] = p->g[1] = 0; } while(m_AStarClosedList[0].Empty() == false) { waypoint_s *p = m_AStarClosedList[0].Pop(); p->bIsOpen[0] = p->bIsOpen[1] = false; p->bIsClosed[0] = p->bIsClosed[1] = false; p->pParent[0] = p->pParent[1] = NULL; p->g[0] = p->g[1] = 0; if (bPathFailed) p->pNode->FailedGoalList.AddNode(m_pCurrentGoalWaypoint->pNode); } while(m_AStarClosedList[1].Empty() == false) { waypoint_s *p = m_AStarClosedList[1].Pop(); p->bIsOpen[0] = p->bIsOpen[1] = false; p->bIsClosed[0] = p->bIsClosed[1] = false; p->pParent[0] = p->pParent[1] = NULL; p->g[0] = p->g[1] = 0; if (bPathFailed) p->pNode->FailedGoalList.AddNode(m_pCurrentWaypoint->pNode); } } void CBot::ResetWaypointVars() { m_iWaypointTime = 0; m_pCurrentWaypoint = NULL; m_pCurrentGoalWaypoint = NULL; m_pPrevWaypoints[0] = NULL; m_pPrevWaypoints[1] = NULL; m_pPrevWaypoints[2] = NULL; m_pPrevWaypoints[3] = NULL; m_pPrevWaypoints[4] = NULL; m_fPrevWaypointDistance = 0; m_iWaypointHeadLastTurnLessTime = 0; m_iWaypointHeadPauseTime = 0; if (m_bCalculatingAStarPath) { m_bCalculatingAStarPath = false; BotManager.m_sUsingAStarBotsCount--; } m_AStarNodeList.DeleteAllNodes(); CleanAStarLists(false); m_bGoToDebugGoal = false; } void CBot::SetCurrentWaypoint(node_s *pNode) { waypoint_s *pWP = GetWPFromNode(pNode); #ifndef RELEASE_BUILD if (!pWP || !pNode) condebug("NULL WP In SetCurrentWP"); #endif m_pCurrentWaypoint = pWP; m_iWaypointTime = lastmillis; } void CBot::SetCurrentWaypoint(waypoint_s *pWP) { #ifndef RELEASE_BUILD if (!pWP) condebug("NULL WP In SetCurrentWP(2)"); #endif m_pCurrentWaypoint = pWP; m_iWaypointTime = lastmillis; } void CBot::SetCurrentGoalWaypoint(node_s *pNode) { waypoint_s *pWP = GetWPFromNode(pNode); #ifndef RELEASE_BUILD if (!pWP || !pNode) condebug("NULL WP In SetCurrentGoalWP"); #endif m_pCurrentGoalWaypoint = pWP; } void CBot::SetCurrentGoalWaypoint(waypoint_s *pWP) { #ifndef RELEASE_BUILD if (!pWP) condebug("NULL WP In SetCurrentGoalWP(2)"); #endif m_pCurrentGoalWaypoint = pWP; } bool CBot::CurrentWPIsValid() { if (!m_pCurrentWaypoint) { //condebug("Invalid WP: Is NULL"); return false; } // check if the bot has been trying to get to this waypoint for a while... if ((m_iWaypointTime + 5000) < lastmillis) { condebug("Invalid WP: time over"); return false; } #ifndef RELEASE_BUILD if (!IsVisible(m_pCurrentWaypoint->pNode->v_origin)) condebug("Invalid WP: Not visible"); #endif return (IsVisible(m_pCurrentWaypoint->pNode->v_origin)); } bool CBot::ReachedGoalWP() { if ((!m_pCurrentWaypoint) || (!m_pCurrentGoalWaypoint)) return false; if (m_pCurrentWaypoint != m_pCurrentGoalWaypoint) return false; return ((GetDistance(m_pCurrentGoalWaypoint->pNode->v_origin) <= 3.0f) && (IsVisible(m_pCurrentGoalWaypoint->pNode->v_origin))); } waypoint_s *CBot::GetWPFromNode(node_s *pNode) { if (!pNode) return NULL; short x, y; WaypointClass.GetNodeIndexes(pNode->v_origin, &x, &y); TLinkedList::node_s *p = m_WaypointList[x][y].GetFirst(); while(p) { if (p->Entry->pNode == pNode) return p->Entry; p = p->next; } return NULL; } waypoint_s *CBot::GetNearestWaypoint(vec v_src, float flRange) { TLinkedList::node_s *p; waypoint_s *pNearest = NULL; short i, j, MinI, MaxI, MinJ, MaxJ, Offset = (short)ceil(flRange / MAX_MAP_GRIDS); float flNearestDist = 9999.99f, flDist; WaypointClass.GetNodeIndexes(v_src, &i, &j); MinI = i - Offset; MaxI = i + Offset; MinJ = j - Offset; MaxJ = j + Offset; if (MinI < 0) MinI = 0; if (MaxI > MAX_MAP_GRIDS - 1) MaxI = MAX_MAP_GRIDS - 1; if (MinJ < 0) MinJ = 0; if (MaxJ > MAX_MAP_GRIDS - 1) MaxJ = MAX_MAP_GRIDS - 1; for (int x=MinI;x<=MaxI;x++) { for(int y=MinJ;y<=MaxJ;y++) { p = m_WaypointList[x][y].GetFirst(); while(p) { if (p->Entry->pNode->iFlags & W_FL_FLOOD) { p = p->next; continue; } flDist = GetDistance(v_src, p->Entry->pNode->v_origin); if ((flDist < flNearestDist) && (flDist <= flRange)) { if (::IsVisible(v_src, p->Entry->pNode->v_origin, NULL)) { pNearest = p->Entry; flNearestDist = flDist; } } p = p->next; } } } return pNearest; } waypoint_s *CBot::GetNearestTriggerWaypoint(vec v_src, float flRange) { TLinkedList::node_s *p; waypoint_s *pNearest = NULL; short i, j, MinI, MaxI, MinJ, MaxJ, Offset = (short)ceil(flRange / MAX_MAP_GRIDS); float flNearestDist = 9999.99f, flDist; WaypointClass.GetNodeIndexes(v_src, &i, &j); MinI = i - Offset; MaxI = i + Offset; MinJ = j - Offset; MaxJ = j + Offset; if (MinI < 0) MinI = 0; if (MaxI > MAX_MAP_GRIDS - 1) MaxI = MAX_MAP_GRIDS - 1; if (MinJ < 0) MinJ = 0; if (MaxJ > MAX_MAP_GRIDS - 1) MaxJ = MAX_MAP_GRIDS - 1; for (int x=MinI;x<=MaxI;x++) { for(int y=MinJ;y<=MaxJ;y++) { p = m_WaypointList[x][y].GetFirst(); while(p) { if (!(p->Entry->pNode->iFlags & W_FL_TRIGGER)) { p = p->next; continue; } flDist = GetDistance(v_src, p->Entry->pNode->v_origin); if ((flDist < flNearestDist) && (flDist <= flRange)) { if (::IsVisible(v_src, p->Entry->pNode->v_origin, NULL)) { pNearest = p->Entry; flNearestDist = flDist; } } p = p->next; } } } return pNearest; } // Makes a waypoint list for this bot based on the list from WaypointClass void CBot::SyncWaypoints() { short x, y; TLinkedList::node_s *p; // Clean everything first for (x=0;xpNode = p->Entry; m_WaypointList[x][y].AddNode(pWP); #ifndef RELEASE_BUILD if (!GetWPFromNode(p->Entry)) condebug("Error adding bot wp!"); #endif p = p->next; } } } } #ifdef WP_FLOOD waypoint_s *CBot::GetNearestFloodWP(vec v_origin, float flRange) { TLinkedList::node_s *p; waypoint_s *pNearest = NULL; short i, j, MinI, MaxI, MinJ, MaxJ, Offset = (short)ceil(flRange / MAX_MAP_GRIDS); float flNearestDist = 9999.99f, flDist; WaypointClass.GetNodeIndexes(v_origin, &i, &j); MinI = i - Offset; MaxI = i + Offset; MinJ = j - Offset; MaxJ = j + Offset; if (MinI < 0) MinI = 0; if (MaxI > MAX_MAP_GRIDS - 1) MaxI = MAX_MAP_GRIDS - 1; if (MinJ < 0) MinJ = 0; if (MaxJ > MAX_MAP_GRIDS - 1) MaxJ = MAX_MAP_GRIDS - 1; for (int x=MinI;x<=MaxI;x++) { for(int y=MinJ;y<=MaxJ;y++) { p = m_WaypointList[x][y].GetFirst(); while(p) { if (!(p->Entry->pNode->iFlags & W_FL_FLOOD)) { p = p->next; continue; } flDist = GetDistance(v_origin, p->Entry->pNode->v_origin); if ((flDist < flNearestDist) && (flDist <= flRange)) { if (::IsVisible(v_origin, p->Entry->pNode->v_origin, NULL)) { pNearest = p->Entry; flNearestDist = flDist; } } p = p->next; } } } return pNearest; } waypoint_s *CBot::GetNearestTriggerFloodWP(vec v_origin, float flRange) { TLinkedList::node_s *p; waypoint_s *pNearest = NULL; short i, j, MinI, MaxI, MinJ, MaxJ, Offset = (short)ceil(flRange / MAX_MAP_GRIDS); float flNearestDist = 9999.99f, flDist; WaypointClass.GetNodeIndexes(v_origin, &i, &j); MinI = i - Offset; MaxI = i + Offset; MinJ = j - Offset; MaxJ = j + Offset; if (MinI < 0) MinI = 0; if (MaxI > MAX_MAP_GRIDS - 1) MaxI = MAX_MAP_GRIDS - 1; if (MinJ < 0) MinJ = 0; if (MaxJ > MAX_MAP_GRIDS - 1) MaxJ = MAX_MAP_GRIDS - 1; for (int x=MinI;x<=MaxI;x++) { for(int y=MinJ;y<=MaxJ;y++) { p = m_WaypointList[x][y].GetFirst(); while(p) { if (!(p->Entry->pNode->iFlags & (W_FL_FLOOD | W_FL_TRIGGER))) { p = p->next; continue; } flDist = GetDistance(v_origin, p->Entry->pNode->v_origin); if ((flDist < flNearestDist) && (flDist <= flRange)) { if (::IsVisible(v_origin, p->Entry->pNode->v_origin, NULL)) { pNearest = p->Entry; flNearestDist = flDist; } } p = p->next; } } } return pNearest; } void CBot::GoToDebugGoal(vec o) { ResetWaypointVars(); node_s *wp = WaypointClass.GetNearestWaypoint(m_pMyEnt, 20.0f); node_s *goalwp = WaypointClass.GetNearestWaypoint(player1, 20.0f); if (!wp || !goalwp) { wp = WaypointClass.GetNearestFloodWP(m_pMyEnt, 8.0f); goalwp = WaypointClass.GetNearestFloodWP(player1, 5.0f); } if (!wp || !goalwp) { condebug("No near WP"); return; } SetCurrentWaypoint(wp); SetCurrentGoalWaypoint(goalwp); m_vGoal = o; m_bGoToDebugGoal = true; } #endif // Bot class end