From d4e1fdb4fbe98f07815f30b06a990ebfa1840590 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Tue, 7 May 2019 15:50:08 +0300 Subject: [PATCH] Fixed radio commands that should be issued only when chatter enabled. Implemented useless waypoint cleaner (yb wp clean ). --- include/yapb.h | 6 +- source/basecode.cpp | 8 +- source/combat.cpp | 4 +- source/interface.cpp | 24 +++ source/waypoint.cpp | 375 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 410 insertions(+), 7 deletions(-) diff --git a/include/yapb.h b/include/yapb.h index f3c1af9..b5209d3 100644 --- a/include/yapb.h +++ b/include/yapb.h @@ -445,7 +445,8 @@ enum WaypointFlag { FLAG_DOUBLEJUMP = (1 << 9), // bot help's another bot (requster) to get somewhere (using djump) FLAG_SNIPER = (1 << 28), // it's a specific sniper point FLAG_TF_ONLY = (1 << 29), // it's a specific terrorist point - FLAG_CF_ONLY = (1 << 30) // it's a specific ct point + FLAG_CF_ONLY = (1 << 30), // it's a specific ct point + FLAG_LOW_LIGHT = (1 << 31) // specifies that waypoints that have low amount of light (for flashlights) }; // defines for waypoint connection flags field (16 bits are available) @@ -1432,7 +1433,7 @@ private: float m_waypointDisplayTime[MAX_WAYPOINTS]; int m_findWPIndex; int m_facingAtIndex; - char m_infoBuffer[256]; + char m_infoBuffer[MAX_PRINT_BUFFER]; int *m_distMatrix; int *m_pathMatrix; @@ -1487,6 +1488,7 @@ public: bool load (void); void save (void); void cleanupPathMemory (void); + int removeUselessConnections (int index, bool outputToConsole = true); bool isReachable (Bot *bot, int index); bool isNodeReacheable (const Vector &src, const Vector &destination); diff --git a/source/basecode.cpp b/source/basecode.cpp index c95c543..4518cbb 100644 --- a/source/basecode.cpp +++ b/source/basecode.cpp @@ -2427,7 +2427,7 @@ void Bot::checkRadioQueue (void) { if (rng.getInt (0, 100) < 45 && yb_communication_type.integer () == 2) { pushChatterMessage (CHATTER_ON_MY_WAY); } - else if (m_radioOrder == RADIO_NEED_BACKUP && yb_communication_type.integer () != 2) { + else if (m_radioOrder == RADIO_NEED_BACKUP && yb_communication_type.integer () != 2 && rng.getInt (0, 100) < 50) { pushRadioMessage (RADIO_AFFIRMATIVE); } tryHeadTowardRadioMessage (); @@ -2891,7 +2891,7 @@ void Bot::frameThink (void) { m_voteMap = 0; } } - else if (m_notKilled && m_buyingFinished && !(pev->maxspeed < 10.0f && taskId () != TASK_PLANTBOMB && taskId () != TASK_DEFUSEBOMB) && !yb_freeze_bots.boolean () && !waypoints.hasChanged ()) { + else if (m_buyingFinished && !(pev->maxspeed < 10.0f && taskId () != TASK_PLANTBOMB && taskId () != TASK_DEFUSEBOMB) && !yb_freeze_bots.boolean () && !waypoints.hasChanged ()) { botMovement = true; } checkMsgQueue (); // check for pending messages @@ -3015,7 +3015,7 @@ void Bot::normal_ (void) { if (processNavigation ()) { // if we're reached the goal, and there is not enemies, notify the team - if (!g_bombPlanted && m_currentWaypointIndex != INVALID_WAYPOINT_INDEX && (m_currentPath->flags & FLAG_GOAL) && rng.getInt (0, 100) < 30 && numEnemiesNear (pev->origin, 650.0f) == 0) { + if (!g_bombPlanted && m_currentWaypointIndex != INVALID_WAYPOINT_INDEX && (m_currentPath->flags & FLAG_GOAL) && rng.getInt (0, 100) < 15 && numEnemiesNear (pev->origin, 650.0f) == 0) { pushRadioMessage (RADIO_SECTOR_CLEAR); } @@ -3194,7 +3194,7 @@ void Bot::normal_ (void) { } // bot hasn't seen anything in a long time and is asking his teammates to report in - if (m_seeEnemyTime + rng.getFloat (45.0f, 80.0f) < engine.timebase () && g_lastRadio[m_team] != RADIO_REPORT_TEAM && rng.getInt (0, 100) < 30 && g_timeRoundStart + 20.0f < engine.timebase () && m_askCheckTime < engine.timebase () && numFriendsNear (pev->origin, 1024.0f) == 0) { + if (yb_communication_type.integer () > 1 && m_seeEnemyTime + rng.getFloat (45.0f, 80.0f) < engine.timebase () && g_lastRadio[m_team] != RADIO_REPORT_TEAM && rng.getInt (0, 100) < 30 && g_timeRoundStart + 20.0f < engine.timebase () && m_askCheckTime < engine.timebase () && numFriendsNear (pev->origin, 1024.0f) == 0) { pushRadioMessage (RADIO_REPORT_TEAM); m_askCheckTime = engine.timebase () + rng.getFloat (45.0f, 80.0f); diff --git a/source/combat.cpp b/source/combat.cpp index b1fccf9..9a1e455 100644 --- a/source/combat.cpp +++ b/source/combat.cpp @@ -905,7 +905,9 @@ void Bot::fireWeapons (void) { m_reloadState = RELOAD_PRIMARY; m_reloadCheckTime = engine.timebase (); - pushRadioMessage (RADIO_NEED_BACKUP); + if (rng.getInt (0, 100) < cr::abs (m_difficulty * 25 - 100)) { + pushRadioMessage (RADIO_NEED_BACKUP); + } } return; } diff --git a/source/interface.cpp b/source/interface.cpp index 62f4128..b4624ee 100644 --- a/source/interface.cpp +++ b/source/interface.cpp @@ -161,6 +161,7 @@ int handleBotCommands (edict_t *ent, const char *arg0, const char *arg1, const c engine.print ("%s wp cache\t - cache nearest waypoint.", self); engine.print ("%s wp teleport\t - teleport hostile to specified waypoint.", self); engine.print ("%s wp setradius\t - manually sets the wayzone radius for this waypoint.", self); + engine.print ("%s wp clean \t - cleans useless paths for specified or all waypoints.", self); engine.print ("%s path autodistance - opens menu for setting autopath maximum distance.", self); engine.print ("%s path cache\t - remember the nearest to player waypoint.", self); engine.print ("%s path create\t - opens menu for path creation.", self); @@ -351,6 +352,29 @@ int handleBotCommands (edict_t *ent, const char *arg0, const char *arg1, const c waypoints.cachePoint (); } + // do a cleanup + else if (stricmp (arg1, "clean") == 0) { + if (!isEmptyStr (arg2)) { + if (stricmp (arg2, "all") == 0) { + int totalCleared = 0; + + for (int i = 0; i < waypoints.length (); i++) { + totalCleared += waypoints.removeUselessConnections (i, true); + } + engine.print ("Done. Processed %d waypoints. %d useless paths cleared.", totalCleared); + } + else { + int clearIndex = atoi (arg2), totalCleared = 0; + + if (waypoints.exists (clearIndex)) { + totalCleared += waypoints.removeUselessConnections (clearIndex); + + engine.print ("Done. Waypoint %d had %d useless paths.", clearIndex, totalCleared); + } + } + } + } + // teleport player to specified waypoint else if (stricmp (arg1, "teleport") == 0) { int teleportPoint = atoi (arg2); diff --git a/source/waypoint.cpp b/source/waypoint.cpp index 8abcf1d..aa3aa37 100644 --- a/source/waypoint.cpp +++ b/source/waypoint.cpp @@ -39,6 +39,380 @@ void Waypoint::cleanupPathMemory (void) { } } +int Waypoint::removeUselessConnections (int index, bool outputToConsole) { + // this function removes the useless paths connections from and to waypoint pointed by index. This is based on code from POD-bot MM from KWo + + if (!exists (index)) { + return 0; + } + int numConnectionsFixed = 0; + + if (bots.getBotCount () > 0) { + bots.kickEveryone (true); + } + const int INFINITE_DISTANCE = 99999; + + auto printInfo = [&outputToConsole](const char *fmt, ...) { + if (!outputToConsole) { + return; + } + char buffer[MAX_PRINT_BUFFER]; + + va_list ap; + va_start (ap, fmt); + vsnprintf (buffer, MAX_PRINT_BUFFER - 1, fmt, ap); + va_end (ap); + + engine.print (buffer); + }; + + struct Connection { + int index; + int number; + int distance; + float angles; + + public: + Connection (void) { + reset (); + } + + public: + void reset (void) { + index = INVALID_WAYPOINT_INDEX; + number = INVALID_WAYPOINT_INDEX; + distance = INFINITE_DISTANCE; + angles = 0.0f; + } + }; + + Connection sorted[MAX_PATH_INDEX]; + Connection top; + + for (int i = 0; i < MAX_PATH_INDEX; i++) { + auto &cur = sorted[i]; + + cur.number = i; + cur.index = m_paths[index]->index[i]; + cur.distance = m_paths[index]->distances[i]; + + if (cur.index == INVALID_WAYPOINT_INDEX) { + cur.distance = INFINITE_DISTANCE; + } + + if (cur.distance < top.distance) { + top.distance = m_paths[index]->distances[i]; + top.number = i; + top.index = cur.index; + } + } + + if (top.number == INVALID_WAYPOINT_INDEX) { + printInfo ("Cannot find path to the closest connected waypoint to waypoint number %d!\n", index); + return numConnectionsFixed; + } + bool sorting = false; + + // sort paths from the closest waypoint to the farest away one... + do { + sorting = false; + + for (int i = 0; i < MAX_PATH_INDEX - 1; i++) { + if (sorted[i].distance > sorted[i + 1].distance) { + cr::swap (sorted[i], sorted[i + 1]); + sorting = true; + } + } + } while (sorting); + + // calculate angles related to the angle of the closeset connected waypoint + for (int i = 0; i < MAX_PATH_INDEX; i++) { + auto &cur = sorted[i]; + + if (cur.index == INVALID_WAYPOINT_INDEX) { + cur.distance = INFINITE_DISTANCE; + cur.angles = 360.0f; + } + else if (exists (cur.index)) { + cur.angles = ((m_paths[cur.index]->origin - m_paths[index]->origin).toAngles () - (m_paths[sorted[0].index]->origin - m_paths[index]->origin).toAngles ()).y; + + if (cur.angles < 0.0f) { + cur.angles += 360.0f; + } + } + } + + // sort the paths from the lowest to the highest angle (related to the vector closest waypoint - checked index)... + do { + sorting = false; + + for (int i = 0; i < MAX_PATH_INDEX - 1; i++) { + if (sorted[i].index != INVALID_WAYPOINT_INDEX && sorted[i].angles > sorted[i + 1].angles) { + cr::swap (sorted[i], sorted[i + 1]); + sorting = true; + } + } + } while (sorting); + + // reset top state + top.reset (); + + auto unassignPath = [&](const int id1, const int id2) { + m_waypointsChanged = true; + + m_paths[id1]->index[id2] = INVALID_WAYPOINT_INDEX; + m_paths[id1]->distances[id2] = 0; + m_paths[id1]->connectionFlags[id2] = 0; + m_paths[id1]->connectionVelocity[id2].nullify (); + + m_waypointsChanged = true; + g_waypointOn = true; + + numConnectionsFixed++; + }; + + // check pass 0 + auto inspect_p0 = [&](const int id, const auto &self) -> bool { + if (id < 2) { + return false; + } + auto &cur = sorted[id], &prev = sorted[id - 1], &prev2 = sorted[id - 2]; + + if (cur.index == INVALID_WAYPOINT_INDEX || prev.index == INVALID_WAYPOINT_INDEX || prev2.index == INVALID_WAYPOINT_INDEX) { + return false; + } + + // store the highest index which should be tested later... + top.index = cur.index; + top.distance = cur.distance; + top.angles = cur.angles; + + if (cur.angles - prev2.angles < 80.0f) { + + // leave alone ladder connections and don't remove jump connections.. + if (((m_paths[index]->flags & FLAG_LADDER) && (m_paths[prev.index]->flags & FLAG_LADDER)) || (m_paths[index]->connectionFlags[prev.number] & PATHFLAG_JUMP)) { + return false; + } + + if ((cur.distance + prev2.distance) * 1.1f / 2.0f < static_cast (prev.distance)) { + if (m_paths[index]->index[prev.number] == prev.index) { + printInfo ("Removing a useless (P.0.1) connection from index = %d to %d.", index, prev.index); + + // unassign this path + unassignPath (index, prev.number); + + for (int j = 0; j < MAX_PATH_INDEX; j++) { + if (m_paths[prev.index]->index[j] == index && !(m_paths[prev.index]->connectionFlags[j] & PATHFLAG_JUMP)) { + printInfo ("Removing a useless (P.0.2) connection from index = %d to %d.", prev.index, index); + + // unassign this path + unassignPath (prev.index, j); + } + } + prev.index = INVALID_WAYPOINT_INDEX; + + for (int j = id - 1; j < MAX_PATH_INDEX - 1; j++) { + sorted[j] = cr::move (sorted[j + 1]); + } + sorted[MAX_PATH_INDEX - 1].index = INVALID_WAYPOINT_INDEX; + + // do a second check + return self (id, self); + } + else { + printInfo ("Failed to remove a useless (P.0) connection from index = %d to %d.", index, prev.index); + return false; + } + } + } + return true; + }; + + + for (int i = 2; i < MAX_PATH_INDEX; i++) { + inspect_p0 (i, inspect_p0); + } + + // check pass 1 + if (exists (top.index) && exists (sorted[0].index) && exists (sorted[1].index)) { + if ((sorted[1].angles - top.angles < 80.0f || 360.0f - (sorted[1].angles - top.angles) < 80.0f) && (!(m_paths[sorted[0].index]->flags & FLAG_LADDER) || !(m_paths[index]->flags & FLAG_LADDER)) && !(m_paths[index]->connectionFlags[sorted[0].number] & PATHFLAG_JUMP)) { + if ((sorted[1].distance + top.distance) * 1.1f / 2.0f < static_cast (sorted[0].distance)) { + if (m_paths[index]->index[sorted[0].number] == sorted[0].index) { + printInfo ("Removing a useless (P.1.1) connection from index = %d to %d.", index, sorted[0].index); + + // unassign this path + unassignPath (index, sorted[0].number); + + for (int j = 0; j < MAX_PATH_INDEX; j++) { + if (m_paths[sorted[0].index]->index[j] == index && !(m_paths[sorted[0].index]->connectionFlags[j] & PATHFLAG_JUMP)) { + printInfo ("Removing a useless (P.1.2) connection from index = %d to %d.", sorted[0].index, index); + + // unassign this path + unassignPath (sorted[0].index, j); + } + } + sorted[0].index = INVALID_WAYPOINT_INDEX; + + for (int j = 0; j < MAX_PATH_INDEX - 1; j++) { + sorted[j] = cr::move (sorted[j + 1]); + } + sorted[MAX_PATH_INDEX - 1].index = INVALID_WAYPOINT_INDEX; + } + else { + printInfo ("Failed to remove a useless (P.1) connection from index = %d to %d.", sorted[0].index, index); + } + } + } + } + top.reset (); + + // check pass 2 + auto inspect_p2 = [&](const int id, const auto &self) -> bool { + if (id < 1) { + return false; + } + auto &cur = sorted[id], &prev = sorted[id - 1]; + + if (cur.index == INVALID_WAYPOINT_INDEX || prev.index == INVALID_WAYPOINT_INDEX) { + return false; + } + + if (cur.angles - prev.angles < 40.0f) { + if (prev.distance < static_cast (cur.distance * 1.1f)) { + + // leave alone ladder connections and don't remove jump connections.. + if (((m_paths[index]->flags & FLAG_LADDER) && (m_paths[cur.index]->flags & FLAG_LADDER)) || (m_paths[index]->connectionFlags[cur.number] & PATHFLAG_JUMP)) { + return false; + } + + if (m_paths[index]->index[cur.number] == cur.index) { + printInfo ("Removing a useless (P.2.1) connection from index = %d to %d.", index, cur.index); + + // unassign this path + unassignPath (index, cur.number); + + for (int j = 0; j < MAX_PATH_INDEX; j++) { + if (m_paths[cur.index]->index[j] == index && !(m_paths[cur.index]->connectionFlags[j] & PATHFLAG_JUMP)) { + printInfo ("Removing a useless (P.2.2) connection from index = %d to %d.", cur.index, index); + + // unassign this path + unassignPath (cur.index, j); + } + } + cur.index = INVALID_WAYPOINT_INDEX; + + for (int j = id - 1; j < MAX_PATH_INDEX - 1; j++) { + sorted[j] = cr::move (sorted[j + 1]); + } + sorted[MAX_PATH_INDEX - 1].index = INVALID_WAYPOINT_INDEX; + + // do a second check + return self (id, self); + } + else { + printInfo ("Failed to remove a useless (P.2) connection from index = %d to %d.", index, cur.index); + } + } + else if (cur.distance < static_cast (prev.distance * 1.1f)) { + // leave alone ladder connections and don't remove jump connections.. + if (((m_paths[index]->flags & FLAG_LADDER) && (m_paths[prev.index]->flags & FLAG_LADDER)) || (m_paths[index]->connectionFlags[prev.number] & PATHFLAG_JUMP)) { + return false; + } + + if (m_paths[index]->index[prev.number] == prev.index) { + printInfo ("Removing a useless (P.2.3) connection from index = %d to %d.", index, prev.index); + + // unassign this path + unassignPath (index, prev.number); + + for (int j = 0; j < MAX_PATH_INDEX; j++) { + if (m_paths[prev.index]->index[j] == index && !(m_paths[prev.index]->connectionFlags[j] & PATHFLAG_JUMP)) { + printInfo ("Removing a useless (P.2.4) connection from index = %d to %d.", prev.index, index); + + // unassign this path + unassignPath (prev.index, j); + } + } + prev.index = INVALID_WAYPOINT_INDEX; + + for (int j = id - 1; j < MAX_PATH_INDEX - 1; j++) { + sorted[j] = cr::move (sorted[j + 1]); + } + sorted[MAX_PATH_INDEX - 1].index = INVALID_WAYPOINT_INDEX; + + // do a second check + return self (id, self); + } + else { + printInfo ("Failed to remove a useless (P.2) connection from index = %d to %d.", index, prev.index); + } + } + } + else { + top = cur; + } + return true; + }; + + for (int i = 1; i < MAX_PATH_INDEX; i++) { + inspect_p2 (i, inspect_p2); + } + + // check pass 3 + if (exists (top.index) && exists (sorted[0].index)) { + if ((top.angles - sorted[0].angles < 40.0f || (360.0f - top.angles - sorted[0].angles) < 40.0f) && (!(m_paths[sorted[0].index]->flags & FLAG_LADDER) || !(m_paths[index]->flags & FLAG_LADDER)) && !(m_paths[index]->connectionFlags[sorted[0].number] & PATHFLAG_JUMP)) { + if (top.distance * 1.1f < static_cast (sorted[0].distance)) { + if (m_paths[index]->index[sorted[0].number] == sorted[0].index) { + printInfo ("Removing a useless (P.3.1) connection from index = %d to %d.", index, sorted[0].index); + + // unassign this path + unassignPath (index, sorted[0].number); + + for (int j = 0; j < MAX_PATH_INDEX; j++) { + if (m_paths[sorted[0].index]->index[j] == index && !(m_paths[sorted[0].index]->connectionFlags[j] & PATHFLAG_JUMP)) { + printInfo ("Removing a useless (P.3.2) connection from index = %d to %d.", sorted[0].index, index); + + // unassign this path + unassignPath (sorted[0].index, j); + } + } + sorted[0].index = INVALID_WAYPOINT_INDEX; + + for (int j = 0; j < MAX_PATH_INDEX - 1; j++) { + sorted[j] = cr::move (sorted[j + 1]); + } + sorted[MAX_PATH_INDEX - 1].index = INVALID_WAYPOINT_INDEX; + } + else { + printInfo ("Failed to remove a useless (P.3) connection from index = %d to %d.", sorted[0].index, index); + } + } + else if (sorted[0].distance * 1.1f < static_cast (top.distance) && !(m_paths[index]->connectionFlags[top.number] & PATHFLAG_JUMP)) { + if (m_paths[index]->index[top.number] == top.index) { + printInfo ("Removing a useless (P.3.3) connection from index = %d to %d.", index, sorted[0].index); + + // unassign this path + unassignPath (index, top.number); + + for (int j = 0; j < MAX_PATH_INDEX; j++) { + if (m_paths[top.index]->index[j] == index && !(m_paths[top.index]->connectionFlags[j] & PATHFLAG_JUMP)) { + printInfo ("Removing a useless (P.3.4) connection from index = %d to %d.", sorted[0].index, index); + + // unassign this path + unassignPath (top.index, j); + } + } + sorted[0].index = INVALID_WAYPOINT_INDEX; + } + else { + printInfo ("Failed to remove a useless (P.3) connection from index = %d to %d.", sorted[0].index, index); + } + } + } + } + return numConnectionsFixed; +} + void Waypoint::addPath (int addIndex, int pathIndex, float distance) { if (!exists (addIndex) || !exists (pathIndex)) { return; @@ -450,6 +824,7 @@ void Waypoint::push (int flags, const Vector &waypointOrigin) { addPath (i, index, distance); } } + removeUselessConnections (index, false); } engine.playSound (g_hostEntity, "weapons/xbow_hit1.wav"); calculatePathRadius (index); // calculate the wayzone of this waypoint