From 52f82b4c64a3f56a18149efded7eeecae230491a Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 30 May 2022 08:26:13 -0400 Subject: [PATCH 1/8] Better bot spindash behaviors For Chrome Gadget --- src/k_bot.c | 50 ++++++++++++++++++++++++-------------------------- src/k_bot.h | 2 +- 2 files changed, 25 insertions(+), 27 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index b2115bbea..4ceb90c21 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -804,9 +804,11 @@ static UINT8 K_TrySpindash(player_t *player) const fixed_t baseAccel = K_GetNewSpeed(player) - oldSpeed; const fixed_t speedDiff = player->speed - player->lastspeed; - if (player->spindashboost || player->tiregrease) + const INT32 angleDiff = AngleDelta(player->mo->angle, K_MomentumAngle(player->mo)); + + if (player->spindashboost || player->tiregrease // You just released a spindash, you don't need to try again yet, jeez. + || P_PlayerInPain(player) || !P_IsObjectOnGround(player->mo)) // Not in a state where we want 'em to spindash. { - // You just released a spindash, you don't need to try again yet, jeez. player->botvars.spindashconfirm = 0; return 0; } @@ -837,36 +839,13 @@ static UINT8 K_TrySpindash(player_t *player) } } - // Logic for normal racing. - if (player->flashing > 0) - { - // Don't bother trying to spindash. - // Trying to spindash while flashing is fine during POSITION, but not during the actual race. - return 0; - } - - if (speedDiff < (baseAccel / 4)) - { - if (player->botvars.spindashconfirm < BOTSPINDASHCONFIRM) - { - player->botvars.spindashconfirm++; - } - } - else - { - if (player->botvars.spindashconfirm > 0) - { - player->botvars.spindashconfirm--; - } - } - if (player->botvars.spindashconfirm >= BOTSPINDASHCONFIRM) { INT32 chargingPoint = (K_GetSpindashChargeTime(player) + difficultyModifier); // Release quicker the higher the difficulty is. // Sounds counter-productive, but that's actually the best strategy after the race has started. - chargingPoint -= player->botvars.difficulty * difficultyModifier; + chargingPoint -= min(DIFFICULTBOT, player->botvars.difficulty) * difficultyModifier; if (player->spindash > chargingPoint) { @@ -876,6 +855,25 @@ static UINT8 K_TrySpindash(player_t *player) return 2; } + else + { + // Logic for normal racing. + if (speedDiff < (baseAccel / 4) // Moving too slowly + || angleDiff > ANG60) // Being pushed backwards + { + if (player->botvars.spindashconfirm < BOTSPINDASHCONFIRM) + { + player->botvars.spindashconfirm++; + } + } + else if (player->botvars.spindashconfirm >= BOTSPINDASHCONFIRM) + { + if (player->botvars.spindashconfirm > 0) + { + player->botvars.spindashconfirm--; + } + } + } // We're doing just fine, we don't need to spindash, thanks. return 0; diff --git a/src/k_bot.h b/src/k_bot.h index f03187205..831f0ab44 100644 --- a/src/k_bot.h +++ b/src/k_bot.h @@ -28,7 +28,7 @@ #define BOTTURNCONFIRM 4 // How many tics without being able to accelerate before we'll let you spindash. -#define BOTSPINDASHCONFIRM (TICRATE/4) +#define BOTSPINDASHCONFIRM (TICRATE) // Point for bots to aim for typedef struct botprediction_s { From 3e4a1feb84ddf756530fa0873bc7d0cd85a42fdc Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 30 May 2022 08:26:38 -0400 Subject: [PATCH 2/8] Better bot controller direction enforce --- src/k_bot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_bot.c b/src/k_bot.c index 4ceb90c21..2f00f0754 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -1320,7 +1320,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) if (botController != NULL && (botController->flags & ML_EFFECT1)) { - const fixed_t dist = (player->mo->radius * 4); + const fixed_t dist = DEFAULT_WAYPOINT_RADIUS * player->mo->scale; // X Offset: Movement direction destangle = FixedAngle(sides[botController->sidenum[0]].textureoffset); From 0e95110136e2b796472beb423f57c680e9d9f32a Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 30 May 2022 19:28:40 -0400 Subject: [PATCH 3/8] Bot prediction uses pathfinding A subtle change, but means the bots are thinking ahead more about the track's design, rather than just what's in front of them. Instead of just "convenient" paths, they'll actively think about which path is the shortest. The most significant thing this effects is making them use shortcuts more often. --- src/k_bot.c | 152 +++++++++++++++++++--------------------------------- src/k_bot.h | 2 +- 2 files changed, 55 insertions(+), 99 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index 2f00f0754..f36943e45 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -639,132 +639,88 @@ fixed_t K_DistanceOfLineFromPoint(fixed_t v1x, fixed_t v1y, fixed_t v2x, fixed_t static botprediction_t *K_CreateBotPrediction(player_t *player) { // Stair janking makes it harder to steer, so attempt to steer harder. - const UINT8 jankDiv = (player->stairjank > 0 ? 2 : 1); + const UINT8 jankDiv = (player->stairjank > 0) ? 2 : 1; const INT16 handling = K_GetKartTurnValue(player, KART_FULLTURN) / jankDiv; // Reduce prediction based on how fast you can turn - const INT16 normal = KART_FULLTURN; // "Standard" handling to compare to - const tic_t futuresight = (TICRATE * normal) / max(1, handling); // How far ahead into the future to try and predict + const tic_t futuresight = (TICRATE * KART_FULLTURN) / max(1, handling); // How far ahead into the future to try and predict const fixed_t speed = K_BotSpeedScaled(player, P_AproxDistance(player->mo->momx, player->mo->momy)); - const INT32 startDist = (768 * mapobjectscale) / FRACUNIT; + const INT32 startDist = (DEFAULT_WAYPOINT_RADIUS * 2 * mapobjectscale) / FRACUNIT; const INT32 distance = ((speed / FRACUNIT) * futuresight) + startDist; - botprediction_t *predict = Z_Calloc(sizeof(botprediction_t), PU_STATIC, NULL); - waypoint_t *wp = player->nextwaypoint; - - INT32 distanceleft = distance; - fixed_t smallestradius = INT32_MAX; - angle_t angletonext = ANGLE_MAX; - // Halves radius when encountering a wall on your way to your destination. fixed_t radreduce = FRACUNIT; - size_t nwp; + INT32 distanceleft = distance; + fixed_t smallestradius = INT32_MAX; + angle_t angletonext = ANGLE_MAX; + INT32 disttonext = INT32_MAX; + + waypoint_t *wp = player->nextwaypoint; + mobj_t *prevwpmobj = player->mo; + + const boolean useshortcuts = K_BotCanTakeCut(player); + const boolean huntbackwards = false; + boolean pathfindsuccess = false; + path_t pathtofinish = {0}; + + botprediction_t *predict = Z_Calloc(sizeof(botprediction_t), PU_STATIC, NULL); size_t i; - // Reduce distance left by your distance to the starting waypoint. - // This prevents looking too far ahead if the closest waypoint is really far away. - distanceleft -= P_AproxDistance(player->mo->x - wp->mobj->x, player->mo->y - wp->mobj->y) / FRACUNIT; + // Init defaults in case of pathfind failure + angletonext = R_PointToAngle2(prevwpmobj->x, prevwpmobj->y, wp->mobj->x, wp->mobj->y); + disttonext = P_AproxDistance(prevwpmobj->x - wp->mobj->x, prevwpmobj->y - wp->mobj->y) / FRACUNIT; - // We don't want to look ahead at all, just go to the first waypoint. - if (distanceleft <= 0) - { - predict->x = wp->mobj->x; - predict->y = wp->mobj->y; - predict->radius = wp->mobj->radius; - return predict; - } - - angletonext = R_PointToAngle2( - player->mo->x, player->mo->y, - wp->mobj->x, wp->mobj->y + pathfindsuccess = K_PathfindToWaypoint( + player->nextwaypoint, K_GetFinishLineWaypoint(), + &pathtofinish, + useshortcuts, huntbackwards ); - // Go through waypoints until we've traveled the distance we wanted to predict ahead! - while (distanceleft > 0) + // Go through the waypoints until we've traveled the distance we wanted to predict ahead! + if (pathfindsuccess == true) { - INT32 disttonext = INT32_MAX; - - if (wp->mobj->radius < smallestradius) + for (i = 0; i < pathtofinish.numnodes; i++) { - smallestradius = wp->mobj->radius; - } + wp = (waypoint_t *)pathtofinish.array[i].nodedata; - if (wp->numnextwaypoints == 0) - { - // Well, this is where I get off. - distanceleft = 0; - break; - } - - // Calculate nextwaypoints index to use - // nextwaypoints[0] by default - nwp = 0; - - // There are multiple nextwaypoints, - // so we need to find the most convenient one to us. - // Let's compare the angle to the player's! - if (wp->numnextwaypoints > 1) - { - angle_t delta = ANGLE_MAX; - angle_t a = ANGLE_MAX; - - for (i = 0; i < wp->numnextwaypoints; i++) + if (i > 0) { - if (K_GetWaypointIsEnabled(wp->nextwaypoints[i]) == false) - { - continue; - } + prevwpmobj = ((waypoint_t *)pathtofinish.array[ i - 1 ].nodedata)->mobj; + } - if (K_GetWaypointIsShortcut(wp->nextwaypoints[i]) == true && K_BotCanTakeCut(player) == false) - { - continue; - } + angletonext = R_PointToAngle2(prevwpmobj->x, prevwpmobj->y, wp->mobj->x, wp->mobj->y); + disttonext = P_AproxDistance(prevwpmobj->x - wp->mobj->x, prevwpmobj->y - wp->mobj->y) / FRACUNIT; - // Unlike the other parts of this function, we're comparing the player's physical position, NOT the position of the waypoint!! - // This should roughly correspond with how players will think about path splits. - a = R_PointToAngle2( - player->mo->x, player->mo->y, - wp->nextwaypoints[i]->mobj->x, wp->nextwaypoints[i]->mobj->y - ); - if (a > ANGLE_180) - { - a = InvAngle(a); - } + if (wp->mobj->radius < smallestradius) + { + smallestradius = wp->mobj->radius; + } - a = player->mo->angle - a; + if (P_TraceBotTraversal(player->mo, wp->mobj) == false) + { + // If we can't get a direct path to this waypoint, predict less. + disttonext <<= 2; + radreduce = FRACUNIT >> 1; + } - if (a < delta) - { - nwp = i; - delta = a; - } + distanceleft -= disttonext; + + if (distanceleft <= 0) + { + // We're done!! + break; } } - - angletonext = R_PointToAngle2( - wp->mobj->x, wp->mobj->y, - wp->nextwaypoints[nwp]->mobj->x, wp->nextwaypoints[nwp]->mobj->y - ); - disttonext = (INT32)wp->nextwaypointdistances[nwp]; - - if (P_TraceBotTraversal(player->mo, wp->nextwaypoints[nwp]->mobj) == false) + if (i == pathtofinish.numnodes) { - // If we can't get a direct path to this waypoint, we don't want to check much further... - disttonext *= 2; - radreduce = FRACUNIT/2; + // Reached the finish!! + distanceleft = 0; } - if (disttonext > distanceleft) - { - break; - } - - distanceleft -= disttonext; - - wp = wp->nextwaypoints[nwp]; + Z_Free(pathtofinish.array); } // Set our predicted point's coordinates, @@ -858,7 +814,7 @@ static UINT8 K_TrySpindash(player_t *player) else { // Logic for normal racing. - if (speedDiff < (baseAccel / 4) // Moving too slowly + if (speedDiff < (baseAccel / 8) // Moving too slowly || angleDiff > ANG60) // Being pushed backwards { if (player->botvars.spindashconfirm < BOTSPINDASHCONFIRM) diff --git a/src/k_bot.h b/src/k_bot.h index 831f0ab44..f476848f0 100644 --- a/src/k_bot.h +++ b/src/k_bot.h @@ -28,7 +28,7 @@ #define BOTTURNCONFIRM 4 // How many tics without being able to accelerate before we'll let you spindash. -#define BOTSPINDASHCONFIRM (TICRATE) +#define BOTSPINDASHCONFIRM (2*TICRATE) // Point for bots to aim for typedef struct botprediction_s { From fae9939a25e33f083f62c26b65d76b5a2bf93f5d Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 30 May 2022 19:29:58 -0400 Subject: [PATCH 4/8] Make slope speed scale more significant Pull that prediction BACK when you're going up slopes! --- src/k_bot.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index f36943e45..73e964f69 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -347,8 +347,8 @@ static fixed_t K_BotSpeedScaled(player_t *player, fixed_t speed) // Going downhill: FRACUNIT*2 slopeMul = FRACUNIT + FINECOSINE(angle >> ANGLETOFINESHIFT); - // Range: 0.9 to 1.1 - result = FixedMul(result, (FRACUNIT*9/10) + (slopeMul/10)); + // Range: 0.5 to 1.5 + result = FixedMul(result, (FRACUNIT>>1) + (slopeMul >> 1)); } } From ee5e96b65eb23394d434ac0329a47591b054bbc7 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 30 May 2022 19:30:17 -0400 Subject: [PATCH 5/8] Simplify angle/speed radius logic --- src/k_bot.c | 71 ++++++++++++++++++++--------------------------------- 1 file changed, 27 insertions(+), 44 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index 73e964f69..a9124d91f 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -961,29 +961,27 @@ static INT32 K_HandleBotTrack(player_t *player, ticcmd_t *cmd, botprediction_t * // Handle steering towards waypoints! INT32 turnamt = 0; SINT8 turnsign = 0; - angle_t moveangle, angle; - INT16 anglediff; + angle_t moveangle; + INT32 anglediff; I_Assert(predict != NULL); moveangle = player->mo->angle; - angle = (moveangle - destangle); + anglediff = AngleDeltaSigned(moveangle, destangle); - if (angle < ANGLE_180) + if (anglediff < 0) { - turnsign = -1; // Turn right - anglediff = AngleFixed(angle)>>FRACBITS; + turnsign = 1; } else { - turnsign = 1; // Turn left - anglediff = 360-(AngleFixed(angle)>>FRACBITS); + turnsign = -1; } anglediff = abs(anglediff); turnamt = KART_FULLTURN * turnsign; - if (anglediff > 90) + if (anglediff > ANGLE_90) { // Wrong way! cmd->forwardmove = -MAXPLMOVE; @@ -992,7 +990,7 @@ static INT32 K_HandleBotTrack(player_t *player, ticcmd_t *cmd, botprediction_t * else { const fixed_t playerwidth = (player->mo->radius * 2); - fixed_t realrad = predict->radius - (playerwidth * 4); // Remove a "safe" distance away from the edges of the road + fixed_t realrad = predict->radius*3/4; // Remove a "safe" distance away from the edges of the road fixed_t rad = realrad; fixed_t dirdist = K_DistanceOfLineFromPoint( player->mo->x, player->mo->y, @@ -1000,19 +998,26 @@ static INT32 K_HandleBotTrack(player_t *player, ticcmd_t *cmd, botprediction_t * predict->x, predict->y ); - if (realrad < player->mo->radius) + if (realrad < playerwidth) { - realrad = player->mo->radius; + realrad = playerwidth; } - if (anglediff > 0) - { - // Become more precise based on how hard you need to turn - // This makes predictions into turns a little nicer - // Facing 90 degrees away from the predicted point gives you a 1/3 radius - rad = FixedMul(rad, ((135 - anglediff) * FRACUNIT) / 135); - } + // Become more precise based on how hard you need to turn + // This makes predictions into turns a little nicer + // Facing 90 degrees away from the predicted point gives you 0 radius + rad = FixedMul(rad, + FixedDiv(max(0, ANGLE_90 - anglediff), ANGLE_90) + ); + // Become more precise the slower you're moving + // Also helps with turns + // Full speed uses full radius + rad = FixedMul(rad, + FixedDiv(K_BotSpeedScaled(player, player->speed), K_GetKartSpeed(player, false, false)) + ); + + // Cap the radius to reasonable bounds if (rad > realrad) { rad = realrad; @@ -1022,36 +1027,14 @@ static INT32 K_HandleBotTrack(player_t *player, ticcmd_t *cmd, botprediction_t * rad = playerwidth; } - cmd->buttons |= BT_ACCELERATE; - // Full speed ahead! + cmd->buttons |= BT_ACCELERATE; cmd->forwardmove = MAXPLMOVE; if (dirdist <= rad) { - fixed_t speedmul = FixedDiv(K_BotSpeedScaled(player, player->speed), K_GetKartSpeed(player, false, false)); - fixed_t speedrad = rad/4; - - if (speedmul > FRACUNIT) - { - speedmul = FRACUNIT; - } - - // Increase radius with speed - // At low speed, the CPU will try to be more accurate - // At high speed, they're more likely to lawnmower - speedrad += FixedMul(speedmul, rad - speedrad); - - if (speedrad < playerwidth) - { - speedrad = playerwidth; - } - - if (dirdist <= speedrad) - { - // Don't turn at all - turnamt = 0; - } + // Going the right way, don't turn at all. + turnamt = 0; } } From bfa3c40033ee2636990297273dc83fcebef50198 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 31 May 2022 03:51:35 -0400 Subject: [PATCH 6/8] Improve bot traversal for the prediction HatesSector was returning false positive for intangible FOFs, making them play really poorly in Desert Palace --- src/k_bot.c | 25 +++++++++++++++---------- src/k_botsearch.c | 22 +++++++++------------- src/p_sight.c | 43 +++++++++++++++++++++---------------------- 3 files changed, 45 insertions(+), 45 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index a9124d91f..4c2bb1123 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -685,11 +685,23 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) { wp = (waypoint_t *)pathtofinish.array[i].nodedata; - if (i > 0) + if (i == 0) + { + prevwpmobj = player->mo; + } + else { prevwpmobj = ((waypoint_t *)pathtofinish.array[ i - 1 ].nodedata)->mobj; } + if (P_TraceBotTraversal(player->mo, wp->mobj) == false) + { + // If we can't get a direct path to this waypoint, stop predicting. + distanceleft = 0; + radreduce = FRACUNIT >> 1; + break; + } + angletonext = R_PointToAngle2(prevwpmobj->x, prevwpmobj->y, wp->mobj->x, wp->mobj->y); disttonext = P_AproxDistance(prevwpmobj->x - wp->mobj->x, prevwpmobj->y - wp->mobj->y) / FRACUNIT; @@ -698,13 +710,6 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) smallestradius = wp->mobj->radius; } - if (P_TraceBotTraversal(player->mo, wp->mobj) == false) - { - // If we can't get a direct path to this waypoint, predict less. - disttonext <<= 2; - radreduce = FRACUNIT >> 1; - } - distanceleft -= disttonext; if (distanceleft <= 0) @@ -733,8 +738,8 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) if (distanceleft > 0) { // Scaled with the leftover anglemul! - predict->x += P_ReturnThrustX(NULL, angletonext, distanceleft * FRACUNIT); - predict->y += P_ReturnThrustY(NULL, angletonext, distanceleft * FRACUNIT); + predict->x += P_ReturnThrustX(NULL, angletonext, min(disttonext, distanceleft) * FRACUNIT); + predict->y += P_ReturnThrustY(NULL, angletonext, min(disttonext, distanceleft) * FRACUNIT); } return predict; diff --git a/src/k_botsearch.c b/src/k_botsearch.c index b62b55852..1192324e3 100644 --- a/src/k_botsearch.c +++ b/src/k_botsearch.c @@ -172,26 +172,24 @@ static boolean K_BotHatesThisSectorsSpecial(player_t *player, sector_t *sec) boolean K_BotHatesThisSector(player_t *player, sector_t *sec, fixed_t x, fixed_t y) { const boolean flip = (player->mo->eflags & MFE_VERTICALFLIP); - INT32 specialflag = 0; fixed_t highestfloor = INT32_MAX; sector_t *bestsector = NULL; ffloor_t *rover; + // TODO: Properly support SF_FLIPSPECIAL_FLOOR / SF_FLIPSPECIAL_CEILING. + // An earlier attempt at it caused lots of false positives and other weird + // quirks with intangible FOFs. + if (flip == true) { - specialflag = SF_FLIPSPECIAL_CEILING; highestfloor = P_GetZAt(sec->c_slope, x, y, sec->ceilingheight); } else { - specialflag = SF_FLIPSPECIAL_FLOOR; highestfloor = P_GetZAt(sec->f_slope, x, y, sec->floorheight); } - if (sec->flags & specialflag) - { - bestsector = sec; - } + bestsector = sec; for (rover = sec->ffloors; rover; rover = rover->next) { @@ -209,15 +207,13 @@ boolean K_BotHatesThisSector(player_t *player, sector_t *sec, fixed_t x, fixed_t if (!(rover->flags & FF_BLOCKPLAYER)) { if ((top >= player->mo->z) && (bottom <= player->mo->z + player->mo->height) - && K_BotHatesThisSectorsSpecial(player, rover->master->frontsector)) + && K_BotHatesThisSectorsSpecial(player, rover->master->frontsector)) { // Bad intangible sector at our height, so we DEFINITELY want to avoid return true; } - } - if ((rover->flags & FF_BLOCKPLAYER) && !(rover->master->frontsector->flags & specialflag)) - { + // Ignore them, we want the one below it. continue; } @@ -225,7 +221,7 @@ boolean K_BotHatesThisSector(player_t *player, sector_t *sec, fixed_t x, fixed_t if (flip == true) { if (bottom < highestfloor - && bottom >= player->mo->z + player->mo->height) + && bottom >= player->mo->z + player->mo->height) { bestsector = rover->master->frontsector; highestfloor = bottom; @@ -234,7 +230,7 @@ boolean K_BotHatesThisSector(player_t *player, sector_t *sec, fixed_t x, fixed_t else { if (top > highestfloor - && top <= player->mo->z) + && top <= player->mo->z) { bestsector = rover->master->frontsector; highestfloor = top; diff --git a/src/p_sight.c b/src/p_sight.c index 4faffcf32..10d670ede 100644 --- a/src/p_sight.c +++ b/src/p_sight.c @@ -516,6 +516,7 @@ typedef struct { divline_t strace; // from t1 to t2 fixed_t bbox[4]; mobj_t *compareThing; + boolean alreadyHates; } traceblocking_t; static boolean P_CrossBlockingSubsector(size_t num, register traceblocking_t *tb) @@ -664,6 +665,7 @@ boolean P_TraceBlockingLines(mobj_t *t1, mobj_t *t2) tb.bbox[BOXTOP] = t2->y, tb.bbox[BOXBOTTOM] = t1->y; tb.compareThing = t1; + tb.alreadyHates = false; // the head node is the last node output return P_CrossBSPNodeBlocking((INT32)numnodes - 1, &tb); @@ -760,31 +762,17 @@ static boolean P_CrossBotTraversalSubsector(size_t num, register traceblocking_t return false; } - if (tb->compareThing->player != NULL) + if (tb->compareThing->player != NULL && tb->alreadyHates == false) { - // Treat damage sectors like walls - boolean alreadyHates = K_BotHatesThisSector( - tb->compareThing->player, tb->compareThing->subsector->sector, - tb->compareThing->x, tb->compareThing->y - ); + // Treat damage sectors like walls, if you're not already in a bad sector. + vertex_t pos; + P_ClosestPointOnLine(tb->compareThing->x, tb->compareThing->y, line, &pos); - if (alreadyHates == false) + if (K_BotHatesThisSector(tb->compareThing->player, line->frontsector, pos.x, pos.y) + || K_BotHatesThisSector(tb->compareThing->player, line->backsector, pos.x, pos.y)) { - INT32 lineside = 0; - vertex_t pos; - - P_ClosestPointOnLine(tb->compareThing->x, tb->compareThing->y, line, &pos); - lineside = P_PointOnLineSide(tb->compareThing->x, tb->compareThing->y, line); - - if (K_BotHatesThisSector( - tb->compareThing->player, - ((lineside == 1) ? line->frontsector : line->backsector), - pos.x, pos.y - )) - { - // This line does not block us, but we don't want to be in it. - return false; - } + // This line does not block us, but we don't want to be in it. + return false; } } } @@ -864,6 +852,17 @@ boolean P_TraceBotTraversal(mobj_t *t1, mobj_t *t2) tb.bbox[BOXTOP] = t2->y, tb.bbox[BOXBOTTOM] = t1->y; tb.compareThing = t1; + if (t1->player != NULL) + { + tb.alreadyHates = K_BotHatesThisSector( + t1->player, t1->subsector->sector, + t1->x, t1->y + ); + } + else + { + tb.alreadyHates = false; + } // the head node is the last node output return P_CrossBSPNodeBotTraversal((INT32)numnodes - 1, &tb); From 754004e07c64d0a2ca04cf40691b15819b840e1c Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 26 Aug 2022 20:19:13 -0400 Subject: [PATCH 7/8] Fix bots acting weird near the finish line --- src/k_bot.c | 51 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index d27b4d543..605a955c2 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -657,6 +657,7 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) angle_t angletonext = ANGLE_MAX; INT32 disttonext = INT32_MAX; + waypoint_t *finishLine = K_GetFinishLineWaypoint(); waypoint_t *wp = player->nextwaypoint; mobj_t *prevwpmobj = player->mo; @@ -673,7 +674,7 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) disttonext = P_AproxDistance(prevwpmobj->x - wp->mobj->x, prevwpmobj->y - wp->mobj->y) / FRACUNIT; pathfindsuccess = K_PathfindToWaypoint( - player->nextwaypoint, K_GetFinishLineWaypoint(), + player->nextwaypoint, finishLine, &pathtofinish, useshortcuts, huntbackwards ); @@ -694,17 +695,16 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) prevwpmobj = ((waypoint_t *)pathtofinish.array[ i - 1 ].nodedata)->mobj; } - if (P_TraceBotTraversal(player->mo, wp->mobj) == false) - { - // If we can't get a direct path to this waypoint, stop predicting. - distanceleft = 0; - radreduce = FRACUNIT >> 1; - break; - } - angletonext = R_PointToAngle2(prevwpmobj->x, prevwpmobj->y, wp->mobj->x, wp->mobj->y); disttonext = P_AproxDistance(prevwpmobj->x - wp->mobj->x, prevwpmobj->y - wp->mobj->y) / FRACUNIT; + if (P_TraceBotTraversal(player->mo, wp->mobj) == false) + { + // If we can't get a direct path to this waypoint, predict less. + distanceleft -= disttonext; + radreduce = FRACUNIT >> 1; + } + if (wp->mobj->radius < smallestradius) { smallestradius = wp->mobj->radius; @@ -717,12 +717,35 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) // We're done!! break; } - } - if (i == pathtofinish.numnodes) - { - // Reached the finish!! - distanceleft = 0; + if (i == pathtofinish.numnodes-1 && disttonext > 0) + { + // We were pathfinding to the finish, but we want to go past it. + // Set up a new pathfind. + + waypoint_t *next = NULL; + + if (finishLine->numnextwaypoints == 0) + { + distanceleft = 0; + break; + } + + // default to first one + next = wp->nextwaypoints[0]; + + pathfindsuccess = K_PathfindToWaypoint( + next, finishLine, + &pathtofinish, + useshortcuts, huntbackwards + ); + + if (pathfindsuccess == false) + { + distanceleft = 0; + break; + } + } } Z_Free(pathtofinish.array); From 7d67e02ea39523003e7bd319da6ea01d9279af92 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 27 Aug 2022 00:04:33 -0400 Subject: [PATCH 8/8] Bot perfstats Show how long they take to think on all of their main tasks --- src/d_clisrv.c | 10 ++-- src/d_netcmd.c | 9 +++- src/k_bot.c | 7 +++ src/k_botsearch.c | 7 ++- src/lua_hooklib.c | 4 +- src/m_perfstats.c | 126 ++++++++++++++++++++++++++++++++++++++++++++-- src/m_perfstats.h | 22 ++++++++ 7 files changed, 175 insertions(+), 10 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index e26debd91..f26ec7274 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -5092,7 +5092,7 @@ static void SV_Maketic(void) { INT32 i; - ps_botticcmd_time = 0; + PS_ResetBotInfo(); for (i = 0; i < MAXPLAYERS; i++) { @@ -5101,9 +5101,13 @@ static void SV_Maketic(void) if (K_PlayerUsesBotMovement(&players[i])) { - precise_t t = I_GetPreciseTime(); + const precise_t t = I_GetPreciseTime(); + K_BuildBotTiccmd(&players[i], &netcmds[maketic%BACKUPTICS][i]); - ps_botticcmd_time += I_GetPreciseTime() - t; + + ps_bots[i].isBot = true; + ps_bots[i].total = I_GetPreciseTime() - t; + ps_botticcmd_time += ps_bots[i].total; continue; } diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 7e8143427..1fef5acd1 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -61,6 +61,7 @@ #include "k_follower.h" #include "doomstat.h" #include "deh_tables.h" +#include "m_perfstats.h" #ifdef NETGAME_DEVMODE #define CV_RESTRICT CV_NETVAR @@ -511,7 +512,13 @@ consvar_t cv_mute = CVAR_INIT ("mute", "Off", CV_NETVAR|CV_CALL, CV_OnOff, Mute_ consvar_t cv_sleep = CVAR_INIT ("cpusleep", "1", CV_SAVE, sleeping_cons_t, NULL); static CV_PossibleValue_t perfstats_cons_t[] = { - {0, "Off"}, {1, "Rendering"}, {2, "Logic"}, {3, "ThinkFrame"}, {0, NULL}}; + {PS_OFF, "Off"}, + {PS_RENDER, "Rendering"}, + {PS_LOGIC, "Logic"}, + {PS_BOT, "Bots"}, + {PS_THINKFRAME, "ThinkFrame"}, + {0, NULL} +}; consvar_t cv_perfstats = CVAR_INIT ("perfstats", "Off", 0, perfstats_cons_t, NULL); consvar_t cv_director = CVAR_INIT ("director", "Off", 0, CV_OnOff, NULL); diff --git a/src/k_bot.c b/src/k_bot.c index 605a955c2..6bc0f573d 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -28,6 +28,7 @@ #include "r_things.h" // numskins #include "k_race.h" // finishBeamLine #include "k_boss.h" +#include "m_perfstats.h" /*-------------------------------------------------- @@ -638,6 +639,8 @@ fixed_t K_DistanceOfLineFromPoint(fixed_t v1x, fixed_t v1y, fixed_t v2x, fixed_t --------------------------------------------------*/ static botprediction_t *K_CreateBotPrediction(player_t *player) { + const precise_t time = I_GetPreciseTime(); + // Stair janking makes it harder to steer, so attempt to steer harder. const UINT8 jankDiv = (player->stairjank > 0) ? 2 : 1; @@ -765,6 +768,7 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) predict->y += P_ReturnThrustY(NULL, angletonext, min(disttonext, distanceleft) * FRACUNIT); } + ps_bots[player - players].prediction += I_GetPreciseTime() - time; return predict; } @@ -1219,6 +1223,7 @@ static INT32 K_HandleBotReverse(player_t *player, ticcmd_t *cmd, botprediction_t --------------------------------------------------*/ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) { + precise_t t = 0; botprediction_t *predict = NULL; boolean trySpindash = true; angle_t destangle = 0; @@ -1439,7 +1444,9 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) { // Don't pointlessly try to use rings/sneakers while charging a spindash. // TODO: Allowing projectile items like orbinaut while e-braking would be nice, maybe just pass in the spindash variable? + t = I_GetPreciseTime(); K_BotItemUsage(player, cmd, turnamt); + ps_bots[player - players].item = I_GetPreciseTime() - t; } if (turnamt != 0) diff --git a/src/k_botsearch.c b/src/k_botsearch.c index 1192324e3..af7464c30 100644 --- a/src/k_botsearch.c +++ b/src/k_botsearch.c @@ -27,6 +27,7 @@ #include "m_random.h" #include "r_things.h" // numskins #include "p_slopes.h" // P_GetZAt +#include "m_perfstats.h" struct globalsmuggle { @@ -613,6 +614,8 @@ static BlockItReturn_t K_FindObjectsForNudging(mobj_t *thing) --------------------------------------------------*/ void K_NudgePredictionTowardsObjects(botprediction_t *predict, player_t *player) { + const precise_t time = I_GetPreciseTime(); + INT32 xl, xh, yl, yh, bx, by; fixed_t distToPredict = R_PointToDist2(player->mo->x, player->mo->y, predict->x, predict->y); @@ -727,7 +730,7 @@ void K_NudgePredictionTowardsObjects(botprediction_t *predict, player_t *player) // Check if our side is invalid, if so, don't do the code below. if (gotoSide != -1 && globalsmuggle.gotoObjs[gotoSide] == 0) { - // Do not use a side + // Do not use a side gotoSide = -1; } @@ -769,6 +772,8 @@ void K_NudgePredictionTowardsObjects(botprediction_t *predict, player_t *player) //distToPredict = R_PointToDist2(player->mo->x, player->mo->y, predict->x, predict->y); } } + + ps_bots[player - players].nudge += I_GetPreciseTime() - time; } /*-------------------------------------------------- diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c index 0a46674c2..771182a59 100644 --- a/src/lua_hooklib.c +++ b/src/lua_hooklib.c @@ -482,7 +482,7 @@ void LUAh_ThinkFrame(void) if (hookp->type != hook_ThinkFrame) continue; - if (cv_perfstats.value == 3) + if (cv_perfstats.value == PS_THINKFRAME) time_taken = I_GetPreciseTime(); PushHook(gL, hookp); if (lua_pcall(gL, 0, 0, 1)) { @@ -491,7 +491,7 @@ void LUAh_ThinkFrame(void) lua_pop(gL, 1); hookp->error = true; } - if (cv_perfstats.value == 3) + if (cv_perfstats.value == PS_THINKFRAME) { lua_Debug ar; time_taken = I_GetPreciseTime() - time_taken; diff --git a/src/m_perfstats.c b/src/m_perfstats.c index a1d4d0fba..0be2eb805 100644 --- a/src/m_perfstats.c +++ b/src/m_perfstats.c @@ -18,6 +18,7 @@ #include "i_time.h" #include "z_zone.h" #include "p_local.h" +#include "g_game.h" #ifdef HWRENDER #include "hardware/hw_main.h" @@ -62,6 +63,8 @@ ps_hookinfo_t *thinkframe_hooks = NULL; int thinkframe_hooks_length = 0; int thinkframe_hooks_capacity = 16; +ps_botinfo_t ps_bots[MAXPLAYERS]; + static INT32 draw_row; void PS_SetThinkFrameHookInfo(int index, precise_t time_taken, char* short_src) @@ -85,6 +88,12 @@ void PS_SetThinkFrameHookInfo(int index, precise_t time_taken, char* short_src) thinkframe_hooks_length = index + 1; } +void PS_ResetBotInfo(void) +{ + memset(ps_bots, 0, sizeof(ps_bots)); + ps_botticcmd_time = 0; +} + static void PS_SetFrameTime(void) { precise_t currenttime = I_GetPreciseTime(); @@ -486,15 +495,126 @@ void M_DrawPerfStats(void) PS_SetFrameTime(); - if (cv_perfstats.value == 1) // rendering + if (cv_perfstats.value == PS_RENDER) // rendering { M_DrawRenderStats(); } - else if (cv_perfstats.value == 2) // logic + else if (cv_perfstats.value == PS_LOGIC) // logic { M_DrawTickStats(); } - else if (cv_perfstats.value == 3) // lua thinkframe + else if (cv_perfstats.value == PS_BOT) // bot ticcmd + { + if (vid.width < 640 || vid.height < 400) // low resolution + { + // it's not gonna fit very well.. + V_DrawThinString(30, 30, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, "Not available for resolutions below 640x400"); + } + else // high resolution + { + precise_t otherTime = 0; + int i; + + // text writing position + int x = 2; + int y = 4; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (ps_bots[i].isBot == false) + { + continue; + } + + snprintf(s, sizeof s - 1, "Bot %d (%s):", i + 1, player_names[i]); + V_DrawSmallString(x, y, V_MONOSPACE | V_ALLOWLOWERCASE | V_GRAYMAP, s); + + snprintf(s, sizeof s - 1, "%ld", (long)((ps_bots[i].total) / (I_GetPrecisePrecision() / 1000000))); + V_DrawRightAlignedSmallString(x + 98, y, V_MONOSPACE | V_ALLOWLOWERCASE | V_GRAYMAP, s); + + y += 4; // repeated code! + if (y > 192) + { + y = 4; + x += 106; + if (x > 214) + break; + } + + snprintf(s, sizeof s - 1, "Prediction:"); + V_DrawSmallString(x, y, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); + + snprintf(s, sizeof s - 1, "%ld", (long)((ps_bots[i].prediction) / (I_GetPrecisePrecision() / 1000000))); + V_DrawRightAlignedSmallString(x + 98, y, V_MONOSPACE | V_ALLOWLOWERCASE, s); + + y += 4; // repeated code! + if (y > 192) + { + y = 4; + x += 106; + if (x > 214) + break; + } + + snprintf(s, sizeof s - 1, "Nudge:"); + V_DrawSmallString(x, y, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); + + snprintf(s, sizeof s - 1, "%ld", (long)((ps_bots[i].nudge) / (I_GetPrecisePrecision() / 1000000))); + V_DrawRightAlignedSmallString(x + 98, y, V_MONOSPACE | V_ALLOWLOWERCASE, s); + + y += 4; // repeated code! + if (y > 192) + { + y = 4; + x += 106; + if (x > 214) + break; + } + + snprintf(s, sizeof s - 1, "Item:"); + V_DrawSmallString(x, y, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); + + snprintf(s, sizeof s - 1, "%ld", (long)((ps_bots[i].item) / (I_GetPrecisePrecision() / 1000000))); + V_DrawRightAlignedSmallString(x + 98, y, V_MONOSPACE | V_ALLOWLOWERCASE, s); + + y += 4; // repeated code! + if (y > 192) + { + y = 4; + x += 106; + if (x > 214) + break; + } + + snprintf(s, sizeof s - 1, "Other:"); + V_DrawSmallString(x, y, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); + + otherTime = ps_bots[i].total - ps_bots[i].prediction - ps_bots[i].nudge - ps_bots[i].item; + snprintf(s, sizeof s - 1, "%ld", (long)(otherTime / (I_GetPrecisePrecision() / 1000000))); + V_DrawRightAlignedSmallString(x + 98, y, V_MONOSPACE | V_ALLOWLOWERCASE, s); + + y += 4; // repeated code! + if (y > 192) + { + y = 4; + x += 106; + if (x > 214) + break; + } + + // add an extra space + y += 4; // repeated code! + if (y > 192) + { + y = 4; + x += 106; + if (x > 214) + break; + } + } + } + } + else if (cv_perfstats.value == PS_THINKFRAME) // lua thinkframe { if (!(gamestate == GS_LEVEL || (gamestate == GS_TITLESCREEN && titlemapinaction))) return; diff --git a/src/m_perfstats.h b/src/m_perfstats.h index dae2f2030..2c448031c 100644 --- a/src/m_perfstats.h +++ b/src/m_perfstats.h @@ -16,6 +16,15 @@ #include "lua_script.h" #include "p_local.h" +typedef enum +{ + PS_OFF = 0, + PS_RENDER, + PS_LOGIC, + PS_BOT, + PS_THINKFRAME, +} ps_types_t; + extern precise_t ps_tictime; extern precise_t ps_playerthink_time; @@ -37,6 +46,19 @@ typedef struct void PS_SetThinkFrameHookInfo(int index, precise_t time_taken, char* short_src); +typedef struct +{ + boolean isBot; + precise_t total; + precise_t prediction; // K_CreateBotPrediction + precise_t nudge; // K_NudgePredictionTowardsObjects + precise_t item; // K_BotItemUsage +} ps_botinfo_t; + +extern ps_botinfo_t ps_bots[MAXPLAYERS]; + +void PS_ResetBotInfo(void); + void M_DrawPerfStats(void); #endif