From b9f91cb7ecadafef72117d417146c3123fe33ee1 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 30 Mar 2022 15:25:55 -0400 Subject: [PATCH 01/16] Increase bot difficulty range to 13 While I finally pushed to do this because of the epic Blue Sphere reference, I was kinda thinking about doing this for a while, because it gives more room for bots to level up during Hard GP. I thought about just making the new level cap go up normally, but I decided on keeping the bots' passive buffs (like top speed & ring boosts) go up linearly, while rubberbanding range is kept the same. I don't think I can properly explain it, but this basically means level 13s are a touch harder than old level 9s before, and new level 9s are touch easier than old level 9s. Basically think of it as more level range than actually making the bots harder. The expanded range means that Hard GP can start off easier, but end harder. (And of course, Master is even more ridiculous c:) --- src/d_clisrv.c | 2 +- src/d_netcmd.c | 6 +++++- src/k_bot.c | 30 ++++++++++++++++++++---------- src/k_bot.h | 5 ++++- src/k_kart.c | 4 ++-- 5 files changed, 32 insertions(+), 15 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 7f6e6f287..f56e07777 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -3413,7 +3413,7 @@ static void Got_AddBot(UINT8 **p, INT32 playernum) { INT16 newplayernum; UINT8 skinnum = 0; - UINT8 difficulty = MAXBOTDIFFICULTY; + UINT8 difficulty = DIFFICULTBOT; if (playernum != serverplayer && !IsPlayerAdmin(playernum)) { diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 51b6b97d6..855353d9f 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -399,9 +399,13 @@ static CV_PossibleValue_t kartbot_cons_t[] = { {7, "Lv.7"}, {8, "Lv.8"}, {9, "Lv.9"}, + {10,"Lv.10"}, + {11,"Lv.11"}, + {12,"Lv.12"}, + {13,"Lv.MAX"}, {0, NULL} }; -consvar_t cv_kartbot = CVAR_INIT ("kartbot", "0", CV_NETVAR|CV_CHEAT, kartbot_cons_t, NULL); +consvar_t cv_kartbot = CVAR_INIT ("kartbot", "0", CV_NETVAR, kartbot_cons_t, NULL); consvar_t cv_karteliminatelast = CVAR_INIT ("karteliminatelast", "Yes", CV_NETVAR|CV_CHEAT|CV_CALL, CV_YesNo, KartEliminateLast_OnChange); diff --git a/src/k_bot.c b/src/k_bot.c index f31317546..7715cec90 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -477,24 +477,34 @@ fixed_t K_BotRubberband(player_t *player) if (wanteddist > player->distancetofinish) { // Whoa, you're too far ahead! Slow back down a little. - rubberband += (MAXBOTDIFFICULTY - player->botvars.difficulty) * (distdiff / 3); + rubberband += (DIFFICULTBOT - min(DIFFICULTBOT, player->botvars.difficulty)) * (distdiff / 3); } else { // Catch up to your position! - rubberband += (2*player->botvars.difficulty) * distdiff; + rubberband += player->botvars.difficulty * distdiff; } } - // Lv. 1: x1.0 max - // Lv. 5: x1.5 max - // Lv. 9: x2.0 max +#if 0 + // Lv. 1: x1.0 max + // Lv. 5: x1.5 max + // Lv. 9: x2.0 max + // Lv. MAX: x2.5 max + max = FRACUNIT + ((FRACUNIT * (player->botvars.difficulty - 1)) / (DIFFICULTBOT - 1)); +#else + // Lv. 1: x1.0 max + // Lv. 5: x1.333 max + // Lv. 9: x1.667 max + // Lv. MAX: x2.0 max max = FRACUNIT + ((FRACUNIT * (player->botvars.difficulty - 1)) / (MAXBOTDIFFICULTY - 1)); +#endif - // Lv. 1: x0.75 min - // Lv. 5: x0.875 min - // Lv. 9: x1.0 min - min = FRACUNIT - (((FRACUNIT/4) * (MAXBOTDIFFICULTY - player->botvars.difficulty)) / (MAXBOTDIFFICULTY - 1)); + // Lv. 1: x0.75 min + // Lv. 5: x0.875 min + // Lv. 9: x1.0 min + // Lv. MAX: x1.0 min + min = FRACUNIT - (((FRACUNIT/4) * (DIFFICULTBOT - min(DIFFICULTBOT, player->botvars.difficulty))) / (DIFFICULTBOT - 1)); if (rubberband > max) { @@ -851,7 +861,7 @@ static UINT8 K_TrySpindash(player_t *player) { INT32 boosthold = starttime - K_GetSpindashChargeTime(player); - boosthold -= (MAXBOTDIFFICULTY - player->botvars.difficulty) * difficultyModifier; + boosthold -= (DIFFICULTBOT - min(DIFFICULTBOT, player->botvars.difficulty)) * difficultyModifier; if (leveltime >= (unsigned)boosthold) { diff --git a/src/k_bot.h b/src/k_bot.h index a04d5c174..be0242449 100644 --- a/src/k_bot.h +++ b/src/k_bot.h @@ -18,7 +18,10 @@ #include "r_defs.h" // Maximum value of botvars.difficulty -#define MAXBOTDIFFICULTY 9 +#define MAXBOTDIFFICULTY 13 + +// Level of a "difficult" bot. The max bot level was increased, but this keeps all of the same calculations. +#define DIFFICULTBOT 9 // How many tics in a row do you need to turn in this direction before we'll let you turn. // Made it as small as possible without making it look like the bots are twitching constantly. diff --git a/src/k_kart.c b/src/k_kart.c index 58cb6fe31..c2f53632c 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -2982,7 +2982,7 @@ fixed_t K_GetKartSpeed(player_t *player, boolean doboostpower) if (K_PlayerUsesBotMovement(player)) { // Increase bot speed by 1-10% depending on difficulty - fixed_t add = (player->botvars.difficulty * (FRACUNIT/10)) / MAXBOTDIFFICULTY; + fixed_t add = (player->botvars.difficulty * (FRACUNIT/10)) / DIFFICULTBOT; finalspeed = FixedMul(finalspeed, FRACUNIT + add); if (player->botvars.rival == true) @@ -7849,7 +7849,7 @@ INT32 K_GetKartRingPower(player_t *player, boolean boosted) if (boosted == true && K_PlayerUsesBotMovement(player)) { // Double for Lv. 9 - ringPower += (player->botvars.difficulty * ringPower) / MAXBOTDIFFICULTY; + ringPower += (player->botvars.difficulty * ringPower) / DIFFICULTBOT; } return ringPower; From 46dad4b7ddb751c9732880580315dd67339ff611 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 30 Mar 2022 20:28:08 -0400 Subject: [PATCH 02/16] Start on better bot position --- src/k_bot.c | 443 +++++++++++++++++++++++++++++++--------------- src/k_bot.h | 15 ++ src/k_botsearch.c | 151 ++++++++++++++++ 3 files changed, 470 insertions(+), 139 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index 7715cec90..8c4028bbe 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -1027,6 +1027,208 @@ static void K_BotTrick(player_t *player, ticcmd_t *cmd, line_t *botController) } } +/*-------------------------------------------------- + static INT32 K_HandleBotTrack(player_t *player, ticcmd_t *cmd, botprediction_t *predict) + + Determines inputs for standard track driving. + + Input Arguments:- + player - Player to generate the ticcmd for. + cmd - The player's ticcmd to modify. + predict - Pointer to the bot's prediction. + + Return:- + New value for turn amount. +--------------------------------------------------*/ +static INT32 K_HandleBotTrack(player_t *player, ticcmd_t *cmd, botprediction_t *predict, angle_t destangle) +{ + // Handle steering towards waypoints! + INT32 turnamt = 0; + SINT8 turnsign = 0; + angle_t moveangle, angle; + INT16 anglediff; + + I_Assert(predict != NULL); + + moveangle = player->mo->angle; + angle = (moveangle - destangle); + + if (angle < ANGLE_180) + { + turnsign = -1; // Turn right + anglediff = AngleFixed(angle)>>FRACBITS; + } + else + { + turnsign = 1; // Turn left + anglediff = 360-(AngleFixed(angle)>>FRACBITS); + } + + anglediff = abs(anglediff); + turnamt = KART_FULLTURN * turnsign; + + if (anglediff > 90) + { + // Wrong way! + cmd->forwardmove = -MAXPLMOVE; + cmd->buttons |= BT_BRAKE; + } + 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 rad = realrad; + fixed_t dirdist = K_DistanceOfLineFromPoint( + player->mo->x, player->mo->y, + player->mo->x + FINECOSINE(moveangle >> ANGLETOFINESHIFT), player->mo->y + FINESINE(moveangle >> ANGLETOFINESHIFT), + predict->x, predict->y + ); + + 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); + } + + if (rad > realrad) + { + rad = realrad; + } + else if (rad < playerwidth) + { + rad = playerwidth; + } + + cmd->buttons |= BT_ACCELERATE; + + // Full speed ahead! + cmd->forwardmove = MAXPLMOVE; + + if (dirdist <= rad) + { + fixed_t speedmul = FixedDiv(player->speed, K_GetKartSpeed(player, 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; + } + } + } + + return turnamt; +} + +/*-------------------------------------------------- + static INT32 K_HandleBotReverse(player_t *player, ticcmd_t *cmd, botprediction_t *predict) + + Determines inputs for reversing. + + Input Arguments:- + player - Player to generate the ticcmd for. + cmd - The player's ticcmd to modify. + predict - Pointer to the bot's prediction. + + Return:- + New value for turn amount. +--------------------------------------------------*/ +static INT32 K_HandleBotReverse(player_t *player, ticcmd_t *cmd, botprediction_t *predict, angle_t destangle) +{ + // Handle steering towards waypoints! + INT32 turnamt = 0; + SINT8 turnsign = 0; + angle_t moveangle, angle; + INT16 anglediff; + + if (predict != NULL) + { + // TODO: Should we reverse through bot controllers? + return K_HandleBotTrack(player, cmd, predict, destangle); + } + + if (player->nextwaypoint == NULL + || player->nextwaypoint->mobj != NULL + || P_MobjWasRemoved(player->nextwaypoint->mobj)) + { + // No data available... + return 0; + } + + if ((player->nextwaypoint->prevwaypoints != NULL) + && (player->nextwaypoint->numprevwaypoints > 0U)) + { + size_t i; + for (i = 0U; i < player->nextwaypoint->numprevwaypoints; i++) + { + if (!K_GetWaypointIsEnabled(player->nextwaypoint->prevwaypoints[i])) + { + continue; + } + + destangle = R_PointToAngle2( + player->nextwaypoint->prevwaypoints[i]->mobj->x, player->nextwaypoint->prevwaypoints[i]->mobj->y, + player->nextwaypoint->mobj->x, player->nextwaypoint->mobj->y + ); + + break; + } + } + + moveangle = player->mo->angle; + angle = (moveangle - destangle); + + if (angle < ANGLE_180) + { + turnsign = -1; // Turn right + anglediff = AngleFixed(angle)>>FRACBITS; + } + else + { + turnsign = 1; // Turn left + anglediff = 360-(AngleFixed(angle)>>FRACBITS); + } + + anglediff = abs(anglediff); + turnamt = KART_FULLTURN * turnsign; + + if (anglediff > 90) + { + // We're not facing the track... + cmd->forwardmove = 0; + cmd->buttons |= BT_ACCELERATE|BT_BRAKE; + } + else + { + if (anglediff < 10) + { + turnamt = 0; + } + + cmd->forwardmove = -MAXPLMOVE; + cmd->buttons |= BT_BRAKE|BT_LOOKBACK; + } + + return turnamt; +} + /*-------------------------------------------------- void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) @@ -1036,6 +1238,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) { botprediction_t *predict = NULL; boolean trySpindash = true; + angle_t destangle = 0; UINT8 spindash = 0; INT32 turnamt = 0; line_t *botController = NULL; @@ -1049,13 +1252,11 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) // Remove any existing controls memset(cmd, 0, sizeof(ticcmd_t)); - if ( - gamestate != GS_LEVEL + if (gamestate != GS_LEVEL || player->mo->scale <= 1 || player->playerstate == PST_DEAD || leveltime <= introtime - || (player->exiting && !(gametyperules & GTR_CIRCUIT)) - ) + || !(gametyperules & GTR_BOTS)) { // No need to do anything else. return; @@ -1077,126 +1278,26 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) return; } - if ((player->nextwaypoint != NULL - && player->nextwaypoint->mobj != NULL - && !P_MobjWasRemoved(player->nextwaypoint->mobj)) - || (botController != NULL)) + destangle = player->mo->angle; + + if (botController != NULL && (botController->flags & ML_EFFECT1)) { - // Handle steering towards waypoints! - SINT8 turnsign = 0; - angle_t destangle, moveangle, angle; - INT16 anglediff; + const fixed_t dist = (player->mo->radius * 4); - if (botController != NULL && (botController->flags & ML_EFFECT1)) - { - const fixed_t dist = (player->mo->radius * 4); + // X Offset: Movement direction + destangle = FixedAngle(sides[botController->sidenum[0]].textureoffset); - // X Offset: Movement direction - destangle = FixedAngle(sides[botController->sidenum[0]].textureoffset); + // Overwritten prediction + predict = Z_Calloc(sizeof(botprediction_t), PU_STATIC, NULL); - // Overwritten prediction - predict = Z_Calloc(sizeof(botprediction_t), PU_STATIC, NULL); - - predict->x = player->mo->x + FixedMul(dist, FINECOSINE(destangle >> ANGLETOFINESHIFT)); - predict->y = player->mo->y + FixedMul(dist, FINESINE(destangle >> ANGLETOFINESHIFT)); - predict->radius = (DEFAULT_WAYPOINT_RADIUS / 4) * mapobjectscale; - } - else - { - predict = K_CreateBotPrediction(player); - - K_NudgePredictionTowardsObjects(predict, player); - - destangle = R_PointToAngle2(player->mo->x, player->mo->y, predict->x, predict->y); - } - - moveangle = player->mo->angle; - angle = (moveangle - destangle); - - if (angle < ANGLE_180) - { - turnsign = -1; // Turn right - anglediff = AngleFixed(angle)>>FRACBITS; - } - else - { - turnsign = 1; // Turn left - anglediff = 360-(AngleFixed(angle)>>FRACBITS); - } - - anglediff = abs(anglediff); - turnamt = KART_FULLTURN * turnsign; - - if (anglediff > 90) - { - // Wrong way! - cmd->forwardmove = -MAXPLMOVE; - cmd->buttons |= BT_BRAKE; - } - 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 rad = realrad; - fixed_t dirdist = K_DistanceOfLineFromPoint( - player->mo->x, player->mo->y, - player->mo->x + FINECOSINE(moveangle >> ANGLETOFINESHIFT), player->mo->y + FINESINE(moveangle >> ANGLETOFINESHIFT), - predict->x, predict->y - ); - - 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); - } - - if (rad > realrad) - { - rad = realrad; - } - else if (rad < playerwidth) - { - rad = playerwidth; - } - - cmd->buttons |= BT_ACCELERATE; - - // Full speed ahead! - cmd->forwardmove = MAXPLMOVE; - - if (dirdist <= rad) - { - fixed_t speedmul = FixedDiv(player->speed, K_GetKartSpeed(player, 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; - } - } - } + predict->x = player->mo->x + FixedMul(dist, FINECOSINE(destangle >> ANGLETOFINESHIFT)); + predict->y = player->mo->y + FixedMul(dist, FINESINE(destangle >> ANGLETOFINESHIFT)); + predict->radius = (DEFAULT_WAYPOINT_RADIUS / 4) * mapobjectscale; } if (leveltime <= starttime && finishBeamLine != NULL) { + // Handle POSITION!! const fixed_t distBase = 384*mapobjectscale; const fixed_t distAdjust = 64*mapobjectscale; @@ -1212,35 +1313,100 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) // Don't run the spindash code at all until we're in the right place trySpindash = false; - // If you're too far, enable spindash & stay still. - // If you're too close, start backing up. +#define QuickDebugPrint(input) \ + if (player - players == displayplayers[0]) \ + CONS_Printf(input); if (distToFinish < closeDist) { - // Silly way of getting us to reverse, but it respects the above code - // where we figure out what the shape of the track looks like. - UINT16 oldButtons = cmd->buttons; - - cmd->buttons &= ~(BT_ACCELERATE|BT_BRAKE); - - if (oldButtons & BT_ACCELERATE) - { - cmd->buttons |= BT_BRAKE; - } - - if (oldButtons & BT_BRAKE) - { - cmd->buttons |= BT_ACCELERATE; - } - - cmd->forwardmove = -cmd->forwardmove; + // We're too close, we need to start backing up. + turnamt = K_HandleBotReverse(player, cmd, predict, destangle); + QuickDebugPrint("Too close\n"); } else if (distToFinish < farDist) { - // We're in about the right place, spindash now. - cmd->forwardmove = 0; - trySpindash = true; + INT32 bullyTurn = INT32_MAX; + + // We're in about the right place, let's do whatever we want to. + + if (player->kartspeed > 3) + { + // Faster characters want to spindash. + // Slower characters will use their momentum. + trySpindash = true; + } + + // Look for characters to bully. + bullyTurn = K_PositionBully(player); + if (bullyTurn == INT32_MAX) + { + // No one to bully, just go for a spindash as anyone. + if (predict == NULL) + { + // Create a prediction. + if (player->nextwaypoint != NULL + && player->nextwaypoint->mobj != NULL + && !P_MobjWasRemoved(player->nextwaypoint->mobj)) + { + predict = K_CreateBotPrediction(player); + K_NudgePredictionTowardsObjects(predict, player); + destangle = R_PointToAngle2(player->mo->x, player->mo->y, predict->x, predict->y); + } + } + + QuickDebugPrint("No one to bully\n"); + turnamt = K_HandleBotTrack(player, cmd, predict, destangle); + cmd->buttons &= ~(BT_ACCELERATE|BT_BRAKE); + cmd->forwardmove = 0; + trySpindash = true; + } + else + { + QuickDebugPrint("Go get 'em\n"); + turnamt = bullyTurn; + + trySpindash = false; + cmd->buttons |= BT_ACCELERATE; + cmd->forwardmove = MAXPLMOVE; + } } + else + { + QuickDebugPrint("Too far\n"); + // Too far away, we need to just drive up. + if (predict == NULL) + { + // Create a prediction. + if (player->nextwaypoint != NULL + && player->nextwaypoint->mobj != NULL + && !P_MobjWasRemoved(player->nextwaypoint->mobj)) + { + predict = K_CreateBotPrediction(player); + K_NudgePredictionTowardsObjects(predict, player); + destangle = R_PointToAngle2(player->mo->x, player->mo->y, predict->x, predict->y); + } + } + + turnamt = K_HandleBotTrack(player, cmd, predict, destangle); + } + } + else + { + // Handle steering towards waypoints! + if (predict == NULL) + { + // Create a prediction. + if (player->nextwaypoint != NULL + && player->nextwaypoint->mobj != NULL + && !P_MobjWasRemoved(player->nextwaypoint->mobj)) + { + predict = K_CreateBotPrediction(player); + K_NudgePredictionTowardsObjects(predict, player); + destangle = R_PointToAngle2(player->mo->x, player->mo->y, predict->x, predict->y); + } + } + + turnamt = K_HandleBotTrack(player, cmd, predict, destangle); } if (trySpindash == true) @@ -1325,4 +1491,3 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) Z_Free(predict); } } - diff --git a/src/k_bot.h b/src/k_bot.h index be0242449..d608fe367 100644 --- a/src/k_bot.h +++ b/src/k_bot.h @@ -223,6 +223,21 @@ boolean K_BotHatesThisSector(player_t *player, sector_t *sec, fixed_t x, fixed_t void K_NudgePredictionTowardsObjects(botprediction_t *predict, player_t *player); +/*-------------------------------------------------- + INT32 K_PositionBully(player_t *player) + + Calculates a turn value to reach a player that can be bullied. + + Input Arguments:- + player - Bot to run this for. + + Return:- + INT32_MAX if couldn't find anything, otherwise a steering value. +--------------------------------------------------*/ + +INT32 K_PositionBully(player_t *player); + + /*-------------------------------------------------- void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd); diff --git a/src/k_botsearch.c b/src/k_botsearch.c index 6e256a9e5..a234bf418 100644 --- a/src/k_botsearch.c +++ b/src/k_botsearch.c @@ -40,6 +40,9 @@ struct globalsmuggle INT64 avoidAvgX[2], avoidAvgY[2]; UINT32 avoidObjs[2]; + fixed_t annoyscore; + mobj_t *annoymo; + fixed_t closestlinedist; fixed_t eggboxx, eggboxy; @@ -771,3 +774,151 @@ void K_NudgePredictionTowardsObjects(botprediction_t *predict, player_t *player) } } } + +/*-------------------------------------------------- + static boolean K_FindPlayersToBully(mobj_t *thing) + + Blockmap search function. + Finds players around the bot to bump. + + Input Arguments:- + thing - Object passed in from iteration. + + Return:- + true continues searching, false ends the search early. +--------------------------------------------------*/ +static boolean K_FindPlayersToBully(mobj_t *thing) +{ + INT16 anglediff; + fixed_t fulldist; + fixed_t ourweight, theirweight, weightdiff; + angle_t ourangle, destangle, angle; + + if (!globalsmuggle.botmo || P_MobjWasRemoved(globalsmuggle.botmo) || !globalsmuggle.botmo->player) + { + return false; + } + + if (thing->health <= 0) + { + return true; + } + + if (!thing->player) + { + return true; + } + + if (globalsmuggle.botmo == thing) + { + return true; + } + + fulldist = R_PointToDist2(globalsmuggle.botmo->x, globalsmuggle.botmo->y, thing->x, thing->y) - thing->radius; + + if (fulldist > globalsmuggle.distancetocheck) + { + return true; + } + + if (P_CheckSight(globalsmuggle.botmo, thing) == false) + { + return true; + } + + ourangle = globalsmuggle.botmo->angle; + destangle = R_PointToAngle2(globalsmuggle.botmo->x, globalsmuggle.botmo->y, thing->x, thing->y); + angle = (ourangle - destangle); + + if (angle < ANGLE_180) + { + anglediff = AngleFixed(angle)>>FRACBITS; + } + else + { + anglediff = 360-(AngleFixed(angle)>>FRACBITS); + } + + anglediff = abs(anglediff); + + ourweight = K_GetMobjWeight(globalsmuggle.botmo, thing); + theirweight = K_GetMobjWeight(thing, globalsmuggle.botmo); + weightdiff = 0; + + if (anglediff >= 90) + { + weightdiff = theirweight - ourweight; + } + else + { + weightdiff = ourweight - theirweight; + } + + if (weightdiff > mapobjectscale && weightdiff > globalsmuggle.annoyscore) + { + globalsmuggle.annoyscore = weightdiff; + globalsmuggle.annoymo = thing; + } + + return true; +} + +/*-------------------------------------------------- + INT32 K_PositionBully(player_t *player) + + See header file for description. +--------------------------------------------------*/ +INT32 K_PositionBully(player_t *player) +{ + INT32 xl, xh, yl, yh, bx, by; + + angle_t ourangle, destangle, angle; + INT16 anglediff; + + globalsmuggle.botmo = player->mo; + globalsmuggle.distancetocheck = 1024*player->mo->scale; + + globalsmuggle.annoymo = NULL; + globalsmuggle.annoyscore = 0; + + xl = (unsigned)(globalsmuggle.botmo->x - globalsmuggle.distancetocheck - bmaporgx)>>MAPBLOCKSHIFT; + xh = (unsigned)(globalsmuggle.botmo->x + globalsmuggle.distancetocheck - bmaporgx)>>MAPBLOCKSHIFT; + yl = (unsigned)(globalsmuggle.botmo->y - globalsmuggle.distancetocheck - bmaporgy)>>MAPBLOCKSHIFT; + yh = (unsigned)(globalsmuggle.botmo->y + globalsmuggle.distancetocheck - bmaporgy)>>MAPBLOCKSHIFT; + + BMBOUNDFIX(xl, xh, yl, yh); + + for (bx = xl; bx <= xh; bx++) + { + for (by = yl; by <= yh; by++) + { + P_BlockThingsIterator(bx, by, K_FindPlayersToBully); + } + } + + if (globalsmuggle.annoymo == NULL) + { + return INT32_MAX; + } + + ourangle = globalsmuggle.botmo->angle; + destangle = R_PointToAngle2(globalsmuggle.botmo->x, globalsmuggle.botmo->y, globalsmuggle.annoymo->x, globalsmuggle.annoymo->y); + angle = (ourangle - destangle); + + if (angle < ANGLE_180) + { + anglediff = AngleFixed(angle)>>FRACBITS; + } + else + { + anglediff = 360-(AngleFixed(angle)>>FRACBITS); + } + + if (anglediff < 30) + return 0; + + if (anglediff < 0) + return -KART_FULLTURN; + + return KART_FULLTURN; +} From 85c03a51d6a98202d64dc60589bcb704b9531e3b Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 30 Mar 2022 22:38:07 -0400 Subject: [PATCH 03/16] Add perfstats for bot ticcmd Cuz bot ticcmd generation is kinda chunky, and was curious how much time it takes. (It's actually a lot less than I thought ... still pretty hefty though) --- src/d_clisrv.c | 4 ++++ src/m_perfstats.c | 2 ++ src/m_perfstats.h | 1 + 3 files changed, 7 insertions(+) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index f56e07777..d8d52220a 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -5092,6 +5092,8 @@ static void SV_Maketic(void) { INT32 i; + ps_botticcmd_time = 0; + for (i = 0; i < MAXPLAYERS; i++) { if (!playeringame[i]) @@ -5099,7 +5101,9 @@ static void SV_Maketic(void) if (K_PlayerUsesBotMovement(&players[i])) { + precise_t t = I_GetPreciseTime(); K_BuildBotTiccmd(&players[i], &netcmds[maketic%BACKUPTICS][i]); + ps_botticcmd_time += I_GetPreciseTime() - t; continue; } diff --git a/src/m_perfstats.c b/src/m_perfstats.c index b58599b6d..03930f495 100644 --- a/src/m_perfstats.c +++ b/src/m_perfstats.c @@ -46,6 +46,7 @@ static precise_t ps_frametime = 0; precise_t ps_tictime = 0; precise_t ps_playerthink_time = 0; +precise_t ps_botticcmd_time = 0; precise_t ps_thinkertime = 0; precise_t ps_thlist_times[NUM_THINKERLISTS]; @@ -363,6 +364,7 @@ static void M_DrawTickStats(void) perfstatrow_t extra_thinker_time_row[] = { {"lthinkf", "LUAh_ThinkFrame:", &ps_lua_thinkframe_time}, + {"botcmd ", "Bot logic: ", &ps_botticcmd_time}, {"other ", "Other: ", &extratime}, {0} }; diff --git a/src/m_perfstats.h b/src/m_perfstats.h index 1ca71957f..dae2f2030 100644 --- a/src/m_perfstats.h +++ b/src/m_perfstats.h @@ -19,6 +19,7 @@ extern precise_t ps_tictime; extern precise_t ps_playerthink_time; +extern precise_t ps_botticcmd_time; extern precise_t ps_thinkertime; extern precise_t ps_thlist_times[]; From 9475c80b9a6c120b6d44ccb79f189492d7e3b3aa Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 31 Mar 2022 15:45:18 -0400 Subject: [PATCH 04/16] Fix typo preventing reversing from working --- 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 8c4028bbe..106415612 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -1165,7 +1165,7 @@ static INT32 K_HandleBotReverse(player_t *player, ticcmd_t *cmd, botprediction_t } if (player->nextwaypoint == NULL - || player->nextwaypoint->mobj != NULL + || player->nextwaypoint->mobj == NULL || P_MobjWasRemoved(player->nextwaypoint->mobj)) { // No data available... From d9b1bdb8516f28339c54f8c448d65d9dc7ad9eb2 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 31 Mar 2022 15:45:50 -0400 Subject: [PATCH 05/16] Remove bots without GTR_BOTS --- 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 106415612..8967e57cb 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -167,7 +167,7 @@ void K_UpdateMatchRaceBots(void) } } - if (difficulty == 0 || bossinfo.boss == true) + if (difficulty == 0 || !(gametyperules & GTR_BOTS) || bossinfo.boss == true) { wantedbots = 0; } From 76bb14bd3e56b59b1b46f9e7a1e75e2a342f6631 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 31 Mar 2022 17:02:12 -0400 Subject: [PATCH 06/16] Scale some bot speed calculations with friction Makes them handle better on ice. Also includes some code to do the same with slopes, but it didn't work for subzero, so I gave up on it. --- src/k_bot.c | 122 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 114 insertions(+), 8 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index 8967e57cb..d34e05131 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -294,6 +294,60 @@ boolean K_BotCanTakeCut(player_t *player) return false; } +/*-------------------------------------------------- + static fixed_t K_BotSpeedScaled(player_t *player, fixed_t speed) + + Gets the bot's speed value, adjusted for predictions. + Mainly to make bots brake earlier when on friction sectors. + + Input Arguments:- + player - The bot player to calculate speed for. + speed - Raw speed value. + + Return:- + The bot's speed value for calculations. +--------------------------------------------------*/ +static fixed_t K_BotSpeedScaled(player_t *player, fixed_t speed) +{ + fixed_t friction = player->mo->friction; + fixed_t result = speed; + +#ifdef SUBZEROFIX // Intended for Subzero Peak's opening, but doesn't work... + // Going uphill: pretend speed is x0 + // Going downhill: pretend speed is x2 + if (player->mo->standingslope != NULL) + { + const pslope_t *slope = player->mo->standingslope; + + if (!(slope->flags & SL_NOPHYSICS) && abs(slope->zdelta) >= FRACUNIT/21) + { + fixed_t slopeMul = FRACUNIT; + angle_t angle = K_MomentumAngle(player->mo) - slope->xydirection; + + if (P_MobjFlip(player->mo) * slope->zdelta < 0) + angle ^= ANGLE_180; + + slopeMul = FRACUNIT + FINECOSINE(angle >> ANGLETOFINESHIFT); + result = FixedMul(result, slopeMul); + } + } +#endif + + // Reverse against friction. Allows for bots to + // acknowledge they'll be moving faster on ice, + // and to steer harder / brake earlier. + + if (friction == 0) + { + // There isn't really a good value to use in this instance. + friction = 1; + } + + result = FixedDiv(speed, friction); + + return result; +} + /*-------------------------------------------------- static line_t *K_FindBotController(mobj_t *mo) @@ -1108,7 +1162,7 @@ static INT32 K_HandleBotTrack(player_t *player, ticcmd_t *cmd, botprediction_t * if (dirdist <= rad) { - fixed_t speedmul = FixedDiv(player->speed, K_GetKartSpeed(player, false)); + fixed_t speedmul = FixedDiv(K_BotSpeedScaled(player, player->speed), K_GetKartSpeed(player, false)); fixed_t speedrad = rad/4; if (speedmul > FRACUNIT) @@ -1156,7 +1210,7 @@ static INT32 K_HandleBotReverse(player_t *player, ticcmd_t *cmd, botprediction_t INT32 turnamt = 0; SINT8 turnsign = 0; angle_t moveangle, angle; - INT16 anglediff; + INT16 anglediff, momdiff; if (predict != NULL) { @@ -1192,6 +1246,7 @@ static INT32 K_HandleBotReverse(player_t *player, ticcmd_t *cmd, botprediction_t } } + // Calculate turn direction first. moveangle = player->mo->angle; angle = (moveangle - destangle); @@ -1209,21 +1264,72 @@ static INT32 K_HandleBotReverse(player_t *player, ticcmd_t *cmd, botprediction_t anglediff = abs(anglediff); turnamt = KART_FULLTURN * turnsign; - if (anglediff > 90) + // Now calculate momentum + momdiff = 180; + if (player->speed > player->mo->scale) { - // We're not facing the track... + momdiff = 0; + moveangle = K_MomentumAngle(player->mo); + angle = (moveangle - destangle); + + if (angle < ANGLE_180) + { + momdiff = AngleFixed(angle)>>FRACBITS; + } + else + { + momdiff = 360-(AngleFixed(angle)>>FRACBITS); + } + + momdiff = abs(momdiff); + } + + if (anglediff > 90 || momdiff < 90) + { + // We're not facing the track, + // or we're going too fast. + // Let's E-Brake. cmd->forwardmove = 0; cmd->buttons |= BT_ACCELERATE|BT_BRAKE; } else { +#ifdef SUBZEROFIX + fixed_t slopeMul = FRACUNIT; + + if (player->mo->standingslope != NULL) + { + const pslope_t *slope = player->mo->standingslope; + + if (!(slope->flags & SL_NOPHYSICS) && abs(slope->zdelta) >= FRACUNIT/21) + { + angle_t sangle = player->mo->angle - slope->xydirection; + + if (P_MobjFlip(player->mo) * slope->zdelta < 0) + sangle ^= ANGLE_180; + + slopeMul = FRACUNIT - FINECOSINE(sangle >> ANGLETOFINESHIFT); + } + } + + + if (slopeMul > (FRACUNIT + (FRACUNIT >> 4))) + { + // Slope is too steep to reverse -- EBrake. + cmd->forwardmove = 0; + cmd->buttons |= BT_ACCELERATE|BT_BRAKE; + } + else +#endif + { + cmd->forwardmove = -MAXPLMOVE; + cmd->buttons |= BT_BRAKE; //|BT_LOOKBACK + } + if (anglediff < 10) { turnamt = 0; } - - cmd->forwardmove = -MAXPLMOVE; - cmd->buttons |= BT_BRAKE|BT_LOOKBACK; } return turnamt; @@ -1308,7 +1414,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) finishBeamLine->v1->x, finishBeamLine->v1->y, finishBeamLine->v2->x, finishBeamLine->v2->y, player->mo->x, player->mo->y - ) - player->speed; + ) - K_BotSpeedScaled(player, player->speed); // Don't run the spindash code at all until we're in the right place trySpindash = false; From af3499c0248a3b18f30e7326952352ed8b7b2be1 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 31 Mar 2022 18:21:03 -0400 Subject: [PATCH 07/16] Make bots RETIRE when they NO CONTEST They get replaced with another bot when they NO CONTEST, swapping their skin and resetting their difficulty & points. --- src/k_grandprix.c | 122 ++++++++++++++++++++++++++++++++++++++++++---- src/k_grandprix.h | 26 ++++++++++ src/y_inter.c | 51 ++++++++++++------- 3 files changed, 173 insertions(+), 26 deletions(-) diff --git a/src/k_grandprix.c b/src/k_grandprix.c index c07bf2a81..be29dbad3 100644 --- a/src/k_grandprix.c +++ b/src/k_grandprix.c @@ -95,6 +95,25 @@ INT16 K_CalculateGPRankPoints(UINT8 position, UINT8 numplayers) return points; } +/*-------------------------------------------------- + SINT8 K_BotDefaultSkin(void) + + See header file for description. +--------------------------------------------------*/ +SINT8 K_BotDefaultSkin(void) +{ + const char *defaultbotskinname = "eggrobo"; + SINT8 defaultbotskin = R_SkinAvailable(defaultbotskinname); + + if (defaultbotskin == -1) + { + // This shouldn't happen, but just in case + defaultbotskin = 0; + } + + return defaultbotskin; +} + /*-------------------------------------------------- void K_InitGrandPrixBots(void) @@ -102,8 +121,7 @@ INT16 K_CalculateGPRankPoints(UINT8 position, UINT8 numplayers) --------------------------------------------------*/ void K_InitGrandPrixBots(void) { - const char *defaultbotskinname = "eggrobo"; - SINT8 defaultbotskin = R_SkinAvailable(defaultbotskinname); + const SINT8 defaultbotskin = K_BotDefaultSkin(); const UINT8 startingdifficulty = K_BotStartingDifficulty(grandprixinfo.gamespeed); UINT8 difficultylevels[MAXPLAYERS]; @@ -121,12 +139,6 @@ void K_InitGrandPrixBots(void) UINT8 newplayernum = 0; UINT8 i, j; - if (defaultbotskin == -1) - { - // This shouldn't happen, but just in case - defaultbotskin = 0; - } - memset(competitors, MAXPLAYERS, sizeof (competitors)); memset(botskinlist, defaultbotskin, sizeof (botskinlist)); @@ -144,7 +156,7 @@ void K_InitGrandPrixBots(void) } #if MAXPLAYERS != 16 - I_Error("GP bot difficulty levels need rebalacned for the new player count!\n"); + I_Error("GP bot difficulty levels need rebalanced for the new player count!\n"); #endif if (grandprixinfo.masterbots) @@ -500,6 +512,98 @@ void K_IncreaseBotDifficulty(player_t *bot) } } +/*-------------------------------------------------- + void K_ReplaceBot(player_t *bot) + + See header file for description. +--------------------------------------------------*/ +void K_ReplaceBot(player_t *bot) +{ + const SINT8 defaultbotskin = K_BotDefaultSkin(); + SINT8 newDifficulty; + + boolean skinusable[MAXSKINS]; + UINT8 skinnum; + UINT8 loops = 0; + + UINT8 i; + + // init usable bot skins list + for (i = 0; i < MAXSKINS; i++) + { + if (i < numskins) + { + skinusable[i] = true; + } + else + { + skinusable[i] = false; + } + } + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && !players[i].spectator) + { + skinusable[players[i].skin] = false; + } + } + + skinnum = P_RandomKey(numskins); + + while (!skinusable[skinnum]) + { + if (loops >= numskins) + { + // no more skins + break; + } + + skinnum++; + + if (skinnum >= numskins) + { + skinnum = 0; + } + + loops++; + } + + if (loops >= numskins) + { + // Use default skin + skinnum = defaultbotskin; + } + + if (!grandprixinfo.gp) // Sure, let's let this happen all the time :) + { + newDifficulty = cv_kartbot.value; + } + else + { + const UINT8 startingdifficulty = K_BotStartingDifficulty(grandprixinfo.gamespeed); + newDifficulty = startingdifficulty - 3 + (grandprixinfo.roundnum - 1); + } + + if (newDifficulty > MAXBOTDIFFICULTY) + { + newDifficulty = MAXBOTDIFFICULTY; + } + else if (newDifficulty < 1) + { + newDifficulty = 1; + } + + bot->botvars.difficulty = newDifficulty; + bot->botvars.diffincrease = 0; + + SetPlayerSkinByNum(bot - players, skinnum); + bot->skincolor = skins[skinnum].prefcolor; + sprintf(player_names[bot - players], "%s", skins[skinnum].realname); + + bot->score = 0; +} + /*-------------------------------------------------- void K_FakeBotResults(player_t *bot) diff --git a/src/k_grandprix.h b/src/k_grandprix.h index 9f27b485b..f951860d8 100644 --- a/src/k_grandprix.h +++ b/src/k_grandprix.h @@ -62,6 +62,16 @@ UINT8 K_BotStartingDifficulty(SINT8 value); INT16 K_CalculateGPRankPoints(UINT8 position, UINT8 numplayers); +/*-------------------------------------------------- + SINT8 K_BotDefaultSkin(void); + + Returns the skin number of the skin the game + uses as a fallback option. +--------------------------------------------------*/ + +SINT8 K_BotDefaultSkin(void); + + /*-------------------------------------------------- void K_InitGrandPrixBots(void); @@ -95,6 +105,22 @@ void K_UpdateGrandPrixBots(void); void K_IncreaseBotDifficulty(player_t *bot); +/*-------------------------------------------------- + void K_ReplaceBot(player_t *bot); + + "Replaces" a bot, by refreshing their difficulty + and changing their skin. + + Input Arguments:- + bot - Player to do this for. + + Return:- + None +--------------------------------------------------*/ + +void K_ReplaceBot(player_t *bot); + + /*-------------------------------------------------- void K_FakeBotResults(player_t *bot); diff --git a/src/y_inter.c b/src/y_inter.c index a1a414d19..69cbf2660 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -316,25 +316,27 @@ static void Y_CalculateMatchData(UINT8 rankingsmode, void (*comparison)(INT32)) data.pos[data.numplayers] = data.numplayers+1; } - if ((powertype == PWRLV_DISABLED) - && (!rankingsmode) - && !(players[i].pflags & PF_NOCONTEST) - && (data.pos[data.numplayers] < (numplayersingame + numgriefers))) + if (!rankingsmode) { - // Online rank is handled further below in this file. - data.increase[i] = K_CalculateGPRankPoints(data.pos[data.numplayers], numplayersingame + numgriefers); - players[i].score += data.increase[i]; - } + if ((powertype == PWRLV_DISABLED) + && !(players[i].pflags & PF_NOCONTEST) + && (data.pos[data.numplayers] < (numplayersingame + numgriefers))) + { + // Online rank is handled further below in this file. + data.increase[i] = K_CalculateGPRankPoints(data.pos[data.numplayers], numplayersingame + numgriefers); + players[i].score += data.increase[i]; + } - if (demo.recording && !rankingsmode) - { - G_WriteStanding( - data.pos[data.numplayers], - data.name[data.numplayers], - *data.character[data.numplayers], - *data.color[data.numplayers], - data.val[data.numplayers] - ); + if (demo.recording) + { + G_WriteStanding( + data.pos[data.numplayers], + data.name[data.numplayers], + *data.character[data.numplayers], + *data.color[data.numplayers], + data.val[data.numplayers] + ); + } } data.numplayers++; @@ -582,6 +584,12 @@ void Y_IntermissionDrawer(void) V_DrawScaledPatch(x+16, y-4, 0, W_CachePatchName(va("K_CHILI%d", cursorframe+1), PU_CACHE)); } + if (!data.rankingsmode && (players[data.num[i]].pflags & PF_NOCONTEST) && players[data.num[i]].bot) + { + // RETIRED!! + V_DrawScaledPatch(x+12, y-7, 0, W_CachePatchName("K_NOBLNS", PU_CACHE)); + } + STRBUFCPY(strtime, data.name[i]); y2 = y; @@ -803,6 +811,15 @@ void Y_Ticker(void) { if (!data.rankingsmode && sorttic != -1 && (intertic >= sorttic + 8)) { + UINT8 i; + for (i = 0; i < MAXPLAYERS; i++) + { + if ((players[i].pflags & PF_NOCONTEST) && players[i].bot) + { + K_ReplaceBot(&players[i]); + } + } + Y_CalculateMatchData(1, Y_CompareRank); } From 3fb1ebbbb03229dc0813922baaa46a007c11aa22 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 1 Apr 2022 16:00:28 -0400 Subject: [PATCH 08/16] Fix Sub Zero Peak opening Bots adjust their speed caluclations with slopes, so they anticipate moving faster going downhill and will stop sooner. They also EBrake instead reversing when trying to reverse up a hill. --- src/k_bot.c | 65 +++++++++++++++++++++++++++-------------------------- 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index d34e05131..dcacf0120 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -309,12 +309,29 @@ boolean K_BotCanTakeCut(player_t *player) --------------------------------------------------*/ static fixed_t K_BotSpeedScaled(player_t *player, fixed_t speed) { - fixed_t friction = player->mo->friction; + fixed_t moveFactor = player->mo->movefactor; fixed_t result = speed; -#ifdef SUBZEROFIX // Intended for Subzero Peak's opening, but doesn't work... - // Going uphill: pretend speed is x0 - // Going downhill: pretend speed is x2 +#if 0 // I still think this is a good idea, but it makes them move way slower on ice. Needs investigating. + if (moveFactor != FRACUNIT) + { + if (moveFactor == 0) + { + moveFactor = 1; + } + + // Reverse against friction. Allows for bots to + // acknowledge they'll be moving faster on ice, + // and to steer harder / brake earlier. + moveFactor = FixedDiv(FRACUNIT, moveFactor); + + // The full value is way too strong, reduce it. + moveFactor -= (moveFactor - FRACUNIT)*3/4; + + result = FixedMul(result, moveFactor); + } +#endif + if (player->mo->standingslope != NULL) { const pslope_t *slope = player->mo->standingslope; @@ -327,23 +344,14 @@ static fixed_t K_BotSpeedScaled(player_t *player, fixed_t speed) if (P_MobjFlip(player->mo) * slope->zdelta < 0) angle ^= ANGLE_180; + // Going uphill: 0 + // Going downhill: FRACUNIT*2 slopeMul = FRACUNIT + FINECOSINE(angle >> ANGLETOFINESHIFT); - result = FixedMul(result, slopeMul); + + // Range: 0.9 to 1.1 + result = FixedMul(result, (FRACUNIT*9/10) + (slopeMul/10)); } } -#endif - - // Reverse against friction. Allows for bots to - // acknowledge they'll be moving faster on ice, - // and to steer harder / brake earlier. - - if (friction == 0) - { - // There isn't really a good value to use in this instance. - friction = 1; - } - - result = FixedDiv(speed, friction); return result; } @@ -737,7 +745,7 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) 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 fixed_t speed = P_AproxDistance(player->mo->momx, player->mo->momy); + const fixed_t speed = K_BotSpeedScaled(player, P_AproxDistance(player->mo->momx, player->mo->momy)); const INT32 startDist = (768 * mapobjectscale) / FRACUNIT; const INT32 distance = ((speed / FRACUNIT) * futuresight) + startDist; @@ -1294,7 +1302,6 @@ static INT32 K_HandleBotReverse(player_t *player, ticcmd_t *cmd, botprediction_t } else { -#ifdef SUBZEROFIX fixed_t slopeMul = FRACUNIT; if (player->mo->standingslope != NULL) @@ -1312,19 +1319,19 @@ static INT32 K_HandleBotReverse(player_t *player, ticcmd_t *cmd, botprediction_t } } - - if (slopeMul > (FRACUNIT + (FRACUNIT >> 4))) +#define STEEP_SLOPE (FRACUNIT*11/10) + if (slopeMul > STEEP_SLOPE) { // Slope is too steep to reverse -- EBrake. cmd->forwardmove = 0; cmd->buttons |= BT_ACCELERATE|BT_BRAKE; } else -#endif { cmd->forwardmove = -MAXPLMOVE; cmd->buttons |= BT_BRAKE; //|BT_LOOKBACK } +#undef STEEP_SLOPE if (anglediff < 10) { @@ -1410,24 +1417,21 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) const fixed_t closeDist = distBase + (distAdjust * (9 - player->kartweight)); const fixed_t farDist = closeDist + (distAdjust * 2); + const tic_t futureSight = (TICRATE >> 1); + fixed_t distToFinish = K_DistanceOfLineFromPoint( finishBeamLine->v1->x, finishBeamLine->v1->y, finishBeamLine->v2->x, finishBeamLine->v2->y, player->mo->x, player->mo->y - ) - K_BotSpeedScaled(player, player->speed); + ) - (K_BotSpeedScaled(player, player->speed) * futureSight); // Don't run the spindash code at all until we're in the right place trySpindash = false; -#define QuickDebugPrint(input) \ - if (player - players == displayplayers[0]) \ - CONS_Printf(input); - if (distToFinish < closeDist) { // We're too close, we need to start backing up. turnamt = K_HandleBotReverse(player, cmd, predict, destangle); - QuickDebugPrint("Too close\n"); } else if (distToFinish < farDist) { @@ -1460,7 +1464,6 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) } } - QuickDebugPrint("No one to bully\n"); turnamt = K_HandleBotTrack(player, cmd, predict, destangle); cmd->buttons &= ~(BT_ACCELERATE|BT_BRAKE); cmd->forwardmove = 0; @@ -1468,7 +1471,6 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) } else { - QuickDebugPrint("Go get 'em\n"); turnamt = bullyTurn; trySpindash = false; @@ -1478,7 +1480,6 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) } else { - QuickDebugPrint("Too far\n"); // Too far away, we need to just drive up. if (predict == NULL) { From 1a25fcee4e1c5eadd2246d7ba6e2f6c01a6af11e Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 1 Apr 2022 16:05:49 -0400 Subject: [PATCH 09/16] Improve spindash conditions --- src/k_bot.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index dcacf0120..0aee5902e 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -309,12 +309,15 @@ boolean K_BotCanTakeCut(player_t *player) --------------------------------------------------*/ static fixed_t K_BotSpeedScaled(player_t *player, fixed_t speed) { - fixed_t moveFactor = player->mo->movefactor; fixed_t result = speed; -#if 0 // I still think this is a good idea, but it makes them move way slower on ice. Needs investigating. - if (moveFactor != FRACUNIT) + // I still think this is a good idea, but it makes them move way slower on ice / slopes. + // Needs investigating. +#if 0 + if (player->mo->movefactor != FRACUNIT) { + fixed_t moveFactor = player->mo->movefactor; + if (moveFactor == 0) { moveFactor = 1; @@ -330,7 +333,6 @@ static fixed_t K_BotSpeedScaled(player_t *player, fixed_t speed) result = FixedMul(result, moveFactor); } -#endif if (player->mo->standingslope != NULL) { @@ -352,6 +354,7 @@ static fixed_t K_BotSpeedScaled(player_t *player, fixed_t speed) result = FixedMul(result, (FRACUNIT*9/10) + (slopeMul/10)); } } +#endif return result; } @@ -1439,7 +1442,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) // We're in about the right place, let's do whatever we want to. - if (player->kartspeed > 3) + if (player->kartspeed >= 5) { // Faster characters want to spindash. // Slower characters will use their momentum. @@ -1473,9 +1476,17 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) { turnamt = bullyTurn; - trySpindash = false; - cmd->buttons |= BT_ACCELERATE; - cmd->forwardmove = MAXPLMOVE; + // If already spindashing, wait until we get a relatively OK charge first. + if (player->spindash > 0 && player->spindash <= TICRATE) + { + trySpindash = true; + } + else + { + trySpindash = false; + cmd->buttons |= BT_ACCELERATE; + cmd->forwardmove = MAXPLMOVE; + } } } else From 56875c59e94e8bbdf8f390db2ee254c489748b98 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 1 Apr 2022 16:39:24 -0400 Subject: [PATCH 10/16] Improve bot friction rubberbanding --- src/k_bot.c | 34 +++------------------------------- 1 file changed, 3 insertions(+), 31 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index 0aee5902e..33c1598a3 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -311,9 +311,6 @@ static fixed_t K_BotSpeedScaled(player_t *player, fixed_t speed) { fixed_t result = speed; - // I still think this is a good idea, but it makes them move way slower on ice / slopes. - // Needs investigating. -#if 0 if (player->mo->movefactor != FRACUNIT) { fixed_t moveFactor = player->mo->movefactor; @@ -354,7 +351,6 @@ static fixed_t K_BotSpeedScaled(player_t *player, fixed_t speed) result = FixedMul(result, (FRACUNIT*9/10) + (slopeMul/10)); } } -#endif return result; } @@ -648,8 +644,9 @@ fixed_t K_BotTopSpeedRubberband(player_t *player) --------------------------------------------------*/ fixed_t K_BotFrictionRubberband(player_t *player, fixed_t frict) { + const fixed_t value = 1024; fixed_t rubberband = K_BotRubberband(player) - FRACUNIT; - fixed_t origFrict, newFrict; + fixed_t newFrict = frict; if (rubberband <= 0) { @@ -657,32 +654,7 @@ fixed_t K_BotFrictionRubberband(player_t *player, fixed_t frict) return frict; } - if (player->tiregrease > 0) - { - // This isn't great -- it means rubberbanding will slow down when they hit a spring - // But it's better than the opposite where they accelerate into hyperspace :V - // (would appreciate an actual fix though ... could try being additive instead of multiplicative) - return frict; - } - - origFrict = FixedDiv(ORIG_FRICTION, FRACUNIT + (rubberband / 2)); - - if (frict == ORIG_FRICTION) - { - newFrict = origFrict; - } - else - { - // Do some mumbo jumbo to make our friction value - // relative to what it WOULD be for ORIG_FRICTION. - // (I hate multiplicative friction :/) - - fixed_t offset = ORIG_FRICTION - frict; - fixed_t ratio = FixedDiv(frict, ORIG_FRICTION); - - offset = FixedDiv(offset, ratio); - newFrict = frict + offset; - } + newFrict = frict - FixedMul(value, rubberband); if (newFrict < 0) newFrict = 0; From 18dc75e6ca7e831051a44f658a4319602ec4a1df Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 1 Apr 2022 17:23:27 -0400 Subject: [PATCH 11/16] Make more accurate to how it used to work --- 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 33c1598a3..37d5c825b 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -644,7 +644,7 @@ fixed_t K_BotTopSpeedRubberband(player_t *player) --------------------------------------------------*/ fixed_t K_BotFrictionRubberband(player_t *player, fixed_t frict) { - const fixed_t value = 1024; + const fixed_t value = 20776; fixed_t rubberband = K_BotRubberband(player) - FRACUNIT; fixed_t newFrict = frict; From 0eb5c29448e5751d592a0295d9818fb7edda1077 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 1 Apr 2022 17:33:41 -0400 Subject: [PATCH 12/16] Don't do for tire grease again, but for the opposite reason. --- src/k_bot.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/k_bot.c b/src/k_bot.c index 37d5c825b..9c633f422 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -654,6 +654,12 @@ fixed_t K_BotFrictionRubberband(player_t *player, fixed_t frict) return frict; } + if (player->tiregrease > 0) + { + // Bots will lose all of their momentum without this. + return frict; + } + newFrict = frict - FixedMul(value, rubberband); if (newFrict < 0) From 59cd9e6157c3846b044bbf4cc07b66325a0f52a2 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 1 Apr 2022 21:47:50 -0400 Subject: [PATCH 13/16] Harder rubberbanding The old one is too difficult, the new one is too easy ... let's get an inbetween. --- src/k_bot.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index 9c633f422..b3f73b8ac 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -547,19 +547,11 @@ fixed_t K_BotRubberband(player_t *player) } } -#if 0 // Lv. 1: x1.0 max - // Lv. 5: x1.5 max - // Lv. 9: x2.0 max - // Lv. MAX: x2.5 max - max = FRACUNIT + ((FRACUNIT * (player->botvars.difficulty - 1)) / (DIFFICULTBOT - 1)); -#else - // Lv. 1: x1.0 max - // Lv. 5: x1.333 max - // Lv. 9: x1.667 max - // Lv. MAX: x2.0 max - max = FRACUNIT + ((FRACUNIT * (player->botvars.difficulty - 1)) / (MAXBOTDIFFICULTY - 1)); -#endif + // Lv. 5: x1.4 max + // Lv. 9: x1.8 max + // Lv. MAX: x2.2 max + max = FRACUNIT + ((FRACUNIT * (player->botvars.difficulty - 1)) / 10); // Lv. 1: x0.75 min // Lv. 5: x0.875 min From 6bdd11533c78c9c7dc04305fa0072c9a7c98156c Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 1 Apr 2022 21:48:28 -0400 Subject: [PATCH 14/16] Don't replace bots on the final race. --- src/k_grandprix.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/k_grandprix.c b/src/k_grandprix.c index be29dbad3..332224479 100644 --- a/src/k_grandprix.c +++ b/src/k_grandprix.c @@ -528,6 +528,12 @@ void K_ReplaceBot(player_t *bot) UINT8 i; + if (grandprixinfo.gp == true && grandprixinfo.roundnum >= grandprixinfo.cup->numlevels) + { + // Was last map, no replacement. + return; + } + // init usable bot skins list for (i = 0; i < MAXSKINS; i++) { @@ -582,7 +588,7 @@ void K_ReplaceBot(player_t *bot) else { const UINT8 startingdifficulty = K_BotStartingDifficulty(grandprixinfo.gamespeed); - newDifficulty = startingdifficulty - 3 + (grandprixinfo.roundnum - 1); + newDifficulty = startingdifficulty - 4 + grandprixinfo.roundnum; } if (newDifficulty > MAXBOTDIFFICULTY) From 5ffba0a678f61bb2d02c1deca8598fd1215943a3 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 2 Apr 2022 14:43:38 -0400 Subject: [PATCH 15/16] review --- src/k_bot.c | 25 +++++++------ src/k_grandprix.c | 94 +++++++++++++++++++++++++++++------------------ src/k_grandprix.h | 14 ++----- src/y_inter.c | 13 ++----- 4 files changed, 78 insertions(+), 68 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index b3f73b8ac..aed20e3b5 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -487,7 +487,7 @@ static UINT32 K_BotRubberbandDistance(player_t *player) fixed_t K_BotRubberband(player_t *player) { fixed_t rubberband = FRACUNIT; - fixed_t max, min; + fixed_t rubbermax, rubbermin; player_t *firstplace = NULL; line_t *botController = NULL; UINT8 i; @@ -551,21 +551,21 @@ fixed_t K_BotRubberband(player_t *player) // Lv. 5: x1.4 max // Lv. 9: x1.8 max // Lv. MAX: x2.2 max - max = FRACUNIT + ((FRACUNIT * (player->botvars.difficulty - 1)) / 10); + rubbermax = FRACUNIT + ((FRACUNIT * (player->botvars.difficulty - 1)) / 10); // Lv. 1: x0.75 min // Lv. 5: x0.875 min // Lv. 9: x1.0 min // Lv. MAX: x1.0 min - min = FRACUNIT - (((FRACUNIT/4) * (DIFFICULTBOT - min(DIFFICULTBOT, player->botvars.difficulty))) / (DIFFICULTBOT - 1)); + rubbermin = FRACUNIT - (((FRACUNIT/4) * (DIFFICULTBOT - min(DIFFICULTBOT, player->botvars.difficulty))) / (DIFFICULTBOT - 1)); - if (rubberband > max) + if (rubberband > rubbermax) { - rubberband = max; + rubberband = rubbermax; } - else if (rubberband < min) + else if (rubberband < rubbermin) { - rubberband = min; + rubberband = rubbermin; } return rubberband; @@ -1119,6 +1119,11 @@ static INT32 K_HandleBotTrack(player_t *player, ticcmd_t *cmd, botprediction_t * predict->x, predict->y ); + if (realrad < player->mo->radius) + { + realrad = player->mo->radius; + } + if (anglediff > 0) { // Become more precise based on how hard you need to turn @@ -1447,11 +1452,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) turnamt = bullyTurn; // If already spindashing, wait until we get a relatively OK charge first. - if (player->spindash > 0 && player->spindash <= TICRATE) - { - trySpindash = true; - } - else + if (player->spindash == 0 || player->spindash > TICRATE) { trySpindash = false; cmd->buttons |= BT_ACCELERATE; diff --git a/src/k_grandprix.c b/src/k_grandprix.c index 332224479..bb14edac6 100644 --- a/src/k_grandprix.c +++ b/src/k_grandprix.c @@ -513,18 +513,16 @@ void K_IncreaseBotDifficulty(player_t *bot) } /*-------------------------------------------------- - void K_ReplaceBot(player_t *bot) + void K_RetireBots(void) See header file for description. --------------------------------------------------*/ -void K_ReplaceBot(player_t *bot) +void K_RetireBots(void) { const SINT8 defaultbotskin = K_BotDefaultSkin(); SINT8 newDifficulty; boolean skinusable[MAXSKINS]; - UINT8 skinnum; - UINT8 loops = 0; UINT8 i; @@ -555,32 +553,6 @@ void K_ReplaceBot(player_t *bot) } } - skinnum = P_RandomKey(numskins); - - while (!skinusable[skinnum]) - { - if (loops >= numskins) - { - // no more skins - break; - } - - skinnum++; - - if (skinnum >= numskins) - { - skinnum = 0; - } - - loops++; - } - - if (loops >= numskins) - { - // Use default skin - skinnum = defaultbotskin; - } - if (!grandprixinfo.gp) // Sure, let's let this happen all the time :) { newDifficulty = cv_kartbot.value; @@ -600,14 +572,64 @@ void K_ReplaceBot(player_t *bot) newDifficulty = 1; } - bot->botvars.difficulty = newDifficulty; - bot->botvars.diffincrease = 0; + for (i = 0; i < MAXPLAYERS; i++) + { + player_t *bot = NULL; - SetPlayerSkinByNum(bot - players, skinnum); - bot->skincolor = skins[skinnum].prefcolor; - sprintf(player_names[bot - players], "%s", skins[skinnum].realname); + if (!playeringame[i] || !players[i].bot) + { + continue; + } - bot->score = 0; + bot = players[i]; + + if (bot->spectator) + { + continue; + } + + if (bot->pflags & PF_NOCONTEST) + { + UINT8 skinnum = P_RandomKey(numskins); + UINT8 loops = 0; + + while (!skinusable[skinnum]) + { + if (loops >= numskins) + { + // no more skins + break; + } + + skinnum++; + + if (skinnum >= numskins) + { + skinnum = 0; + } + + loops++; + } + + if (loops >= numskins) + { + // Use default skin + skinnum = defaultbotskin; + } + + skinusable[skinnum] = false; + + bot->botvars.difficulty = newDifficulty; + bot->botvars.diffincrease = 0; + + SetPlayerSkinByNum(bot - players, skinnum); + bot->skincolor = skins[skinnum].prefcolor; + sprintf(player_names[bot - players], "%s", skins[skinnum].realname); + + bot->score = 0; + bot->pflags &= ~PF_NOCONTEST; + } + } } /*-------------------------------------------------- diff --git a/src/k_grandprix.h b/src/k_grandprix.h index f951860d8..552f75450 100644 --- a/src/k_grandprix.h +++ b/src/k_grandprix.h @@ -106,19 +106,13 @@ void K_IncreaseBotDifficulty(player_t *bot); /*-------------------------------------------------- - void K_ReplaceBot(player_t *bot); + void K_RetireBots(player_t *bot); - "Replaces" a bot, by refreshing their difficulty + Replaces PF_NOCONTEST bots, by refreshing their difficulty and changing their skin. - - Input Arguments:- - bot - Player to do this for. - - Return:- - None --------------------------------------------------*/ -void K_ReplaceBot(player_t *bot); +void K_RetireBots(player_t *bot); /*-------------------------------------------------- @@ -162,7 +156,7 @@ void K_PlayerLoseLife(player_t *player); None Return:- - None + true if can change important gameplay rules, otherwise false. --------------------------------------------------*/ boolean K_CanChangeRules(void); diff --git a/src/y_inter.c b/src/y_inter.c index 69cbf2660..7f48f0798 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -584,7 +584,7 @@ void Y_IntermissionDrawer(void) V_DrawScaledPatch(x+16, y-4, 0, W_CachePatchName(va("K_CHILI%d", cursorframe+1), PU_CACHE)); } - if (!data.rankingsmode && (players[data.num[i]].pflags & PF_NOCONTEST) && players[data.num[i]].bot) + if ((players[data.num[i]].pflags & PF_NOCONTEST) && players[data.num[i]].bot) { // RETIRED!! V_DrawScaledPatch(x+12, y-7, 0, W_CachePatchName("K_NOBLNS", PU_CACHE)); @@ -811,15 +811,7 @@ void Y_Ticker(void) { if (!data.rankingsmode && sorttic != -1 && (intertic >= sorttic + 8)) { - UINT8 i; - for (i = 0; i < MAXPLAYERS; i++) - { - if ((players[i].pflags & PF_NOCONTEST) && players[i].bot) - { - K_ReplaceBot(&players[i]); - } - } - + K_RetireBots(); Y_CalculateMatchData(1, Y_CompareRank); } @@ -1170,6 +1162,7 @@ void Y_StartIntermission(void) // void Y_EndIntermission(void) { + K_RetireBots(); Y_UnloadData(); endtic = -1; From 358cc9f215913be964cee1d9bc19b4debe3b4866 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 2 Apr 2022 14:54:23 -0400 Subject: [PATCH 16/16] Bad function declaration --- src/k_grandprix.c | 2 +- src/k_grandprix.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/k_grandprix.c b/src/k_grandprix.c index bb14edac6..740730c6f 100644 --- a/src/k_grandprix.c +++ b/src/k_grandprix.c @@ -581,7 +581,7 @@ void K_RetireBots(void) continue; } - bot = players[i]; + bot = &players[i]; if (bot->spectator) { diff --git a/src/k_grandprix.h b/src/k_grandprix.h index 552f75450..bd20d3894 100644 --- a/src/k_grandprix.h +++ b/src/k_grandprix.h @@ -106,13 +106,13 @@ void K_IncreaseBotDifficulty(player_t *bot); /*-------------------------------------------------- - void K_RetireBots(player_t *bot); + void K_RetireBots(void); Replaces PF_NOCONTEST bots, by refreshing their difficulty and changing their skin. --------------------------------------------------*/ -void K_RetireBots(player_t *bot); +void K_RetireBots(void); /*--------------------------------------------------