Merge branch 'straight-line-bots' into 'master'

Slow down bots on straightaways (reinventing the accel wheel)

See merge request kart-krew-dev/ring-racers-internal!2641
This commit is contained in:
Oni VelocitOni 2025-06-22 18:38:16 +00:00
commit 74f4474bf0
7 changed files with 60 additions and 13 deletions

View file

@ -430,6 +430,8 @@ struct botvars_t
tic_t rouletteTimeout; // If it takes too long to decide, try lowering priority until we find something valid. tic_t rouletteTimeout; // If it takes too long to decide, try lowering priority until we find something valid.
angle_t predictionError; // How bad is our momentum angle relative to where we're trying to go? angle_t predictionError; // How bad is our momentum angle relative to where we're trying to go?
angle_t recentDeflection; // How long have we been going straight? (See k_bot.h)
angle_t lastAngle;
}; };
// player_t struct for round-specific condition tracking // player_t struct for round-specific condition tracking

View file

@ -586,7 +586,7 @@ const botcontroller_t *K_GetBotController(const mobj_t *mobj)
fixed_t K_BotMapModifier(void) fixed_t K_BotMapModifier(void)
{ {
// fuck it we ball // fuck it we ball
// return 10*FRACUNIT/10; return 5*FRACUNIT/10;
constexpr INT32 complexity_scale = 10000; constexpr INT32 complexity_scale = 10000;
fixed_t modifier_max = (10 * FRACUNIT / 10) - FRACUNIT; fixed_t modifier_max = (10 * FRACUNIT / 10) - FRACUNIT;
@ -690,9 +690,9 @@ fixed_t K_BotRubberband(const player_t *player)
// Allow the status quo to assert itself a bit. Bots get most of their speed from their // Allow the status quo to assert itself a bit. Bots get most of their speed from their
// mechanics adjustments, not from items, so kill some bot speed if they've got bad EXP. // mechanics adjustments, not from items, so kill some bot speed if they've got bad EXP.
if (player->gradingfactor < FRACUNIT && !(player->botvars.rival)) if (player->gradingfactor < FRACUNIT && !(player->botvars.rival) && player->botvars.difficulty > 1)
{ {
UINT8 levelreduce = 3; // How much to drop the "effective level" of bots that are consistently behind UINT8 levelreduce = std::min<UINT8>(3, player->botvars.difficulty); // How much to drop the "effective level" of bots that are consistently behind
expreduce = Easing_Linear((K_EffectiveGradingFactor(player) - MINGRADINGFACTOR) * 2, levelreduce*FRACUNIT, 0); expreduce = Easing_Linear((K_EffectiveGradingFactor(player) - MINGRADINGFACTOR) * 2, levelreduce*FRACUNIT, 0);
} }
@ -823,12 +823,23 @@ fixed_t K_BotRubberband(const player_t *player)
fixed_t K_UpdateRubberband(player_t *player) fixed_t K_UpdateRubberband(player_t *player)
{ {
fixed_t dest = K_BotRubberband(player); fixed_t dest = K_BotRubberband(player);
fixed_t deflect = player->botvars.recentDeflection;
if (deflect > BOTMAXDEFLECTION)
deflect = BOTMAXDEFLECTION;
dest = FixedMul(dest, Easing_Linear(
FixedDiv(deflect, BOTMAXDEFLECTION),
BOTSTRAIGHTSPEED,
BOTTURNSPEED
));
fixed_t ret = player->botvars.rubberband; fixed_t ret = player->botvars.rubberband;
UINT8 ease_soften = 8; UINT8 ease_soften = (ret > dest) ? 3 : 8;
if (player->botvars.bumpslow && dest > ret) if (player->botvars.bumpslow && dest > ret)
ease_soften *= 10; ease_soften = 80;
// Ease into the new value. // Ease into the new value.
ret += (dest - player->botvars.rubberband) / ease_soften; ret += (dest - player->botvars.rubberband) / ease_soften;
@ -2123,6 +2134,21 @@ void K_UpdateBotGameplayVars(player_t *player)
} }
} }
angle_t mangle = K_MomentumAngleEx(player->mo, 5*mapobjectscale); // magic threshold
angle_t langle = player->botvars.lastAngle;
angle_t dangle = 0;
if (mangle >= langle)
dangle = mangle - langle;
else
dangle = langle - mangle;
// Writing this made me move my tongue around in my mouth
UINT32 smo = BOTANGLESAMPLES - 1;
player->botvars.recentDeflection = (smo * player->botvars.recentDeflection / BOTANGLESAMPLES) + (dangle / BOTANGLESAMPLES);
player->botvars.lastAngle = mangle;
const botcontroller_t *botController = K_GetBotController(player->mo); const botcontroller_t *botController = K_GetBotController(player->mo);
if (K_TryRingShooter(player, botController) == true) if (K_TryRingShooter(player, botController) == true)
{ {

View file

@ -35,7 +35,7 @@ extern "C" {
// How many tics in a row do you need to turn in this direction before we'll let you turn. // 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. // Made it as small as possible without making it look like the bots are twitching constantly.
#define BOTTURNCONFIRM 4 #define BOTTURNCONFIRM 1
// How many tics with only one spindash-viable condition before we'll let you spindash. // How many tics with only one spindash-viable condition before we'll let you spindash.
#define BOTSPINDASHCONFIRM (4*TICRATE) #define BOTSPINDASHCONFIRM (4*TICRATE)
@ -46,6 +46,11 @@ extern "C" {
// How long it takes for a Lv.1 bot to decide to pick an item. // How long it takes for a Lv.1 bot to decide to pick an item.
#define BOT_ITEM_DECISION_TIME (2*TICRATE) #define BOT_ITEM_DECISION_TIME (2*TICRATE)
#define BOTSTRAIGHTSPEED (80*FRACUNIT/100) // How fast we move when at 0 deflection.
#define BOTTURNSPEED (100*FRACUNIT/100) // How fast we move when at MAXDEFLECTION deflection.
#define BOTANGLESAMPLES (TICRATE) // Time period to average over. Higher values produce lower peaks that last longer.
#define BOTMAXDEFLECTION (ANG1*3) // Measured in "degrees per tic" here, use debugbots.
// Point for bots to aim for // Point for bots to aim for
struct botprediction_t struct botprediction_t
{ {

View file

@ -7095,7 +7095,8 @@ static void K_DrawBotDebugger(void)
V_DrawSmallString(8, 66, 0, va("Complexity: %d", K_GetTrackComplexity())); V_DrawSmallString(8, 66, 0, va("Complexity: %d", K_GetTrackComplexity()));
V_DrawSmallString(8, 70, 0, va("Bot modifier: %.2f", FixedToFloat(K_BotMapModifier()))); V_DrawSmallString(8, 70, 0, va("Bot modifier: %.2f", FixedToFloat(K_BotMapModifier())));
V_DrawSmallString(8, 76, 0, va("Prediction error: %d", bot->botvars.predictionError)); V_DrawSmallString(8, 76, 0, va("Prediction error: %.2fdeg", FIXED_TO_FLOAT(FixedDiv(bot->botvars.predictionError, ANG1))));
V_DrawSmallString(8, 80, 0, va("Recent deflection: %.2fdeg", FIXED_TO_FLOAT(FixedDiv(bot->botvars.recentDeflection, ANG1))));
} }
static void K_DrawGPRankDebugger(void) static void K_DrawGPRankDebugger(void)

View file

@ -1607,13 +1607,16 @@ static boolean K_TryDraft(player_t *player, mobj_t *dest, fixed_t minDist, fixed
// How much this increments every tic biases toward acceleration! (min speed gets 1.5% per tic, max speed gets 0.5% per tic) // How much this increments every tic biases toward acceleration! (min speed gets 1.5% per tic, max speed gets 0.5% per tic)
if (player->draftpower < FRACUNIT) if (player->draftpower < FRACUNIT)
{ {
fixed_t add = (FRACUNIT/200) + ((9 - player->kartspeed) * ((3*FRACUNIT)/1600));; fixed_t add = (FRACUNIT/200) + ((9 - player->kartspeed) * ((3*FRACUNIT)/1600));
player->draftpower += add; player->draftpower += add;
if (player->bot && (player->botvars.rival || cv_levelskull.value)) if (player->bot)
{ {
// Double speed for the rival! // Double speed for the rival!
player->draftpower += add; if (player->botvars.rival || cv_levelskull.value)
player->draftpower += add;
else if (dest->player->bot) // Reduce bot gluts.
player->draftpower -= 3*add/4;
} }
if (gametyperules & GTR_CLOSERPLAYERS) if (gametyperules & GTR_CLOSERPLAYERS)

View file

@ -756,7 +756,10 @@ static void P_NetArchivePlayers(savebuffer_t *save)
WRITEUINT32(save->p, players[i].botvars.spindashconfirm); WRITEUINT32(save->p, players[i].botvars.spindashconfirm);
WRITEUINT32(save->p, players[i].botvars.respawnconfirm); WRITEUINT32(save->p, players[i].botvars.respawnconfirm);
WRITEUINT8(save->p, players[i].botvars.roulettePriority); WRITEUINT8(save->p, players[i].botvars.roulettePriority);
WRITEUINT32(save->p, players[i].botvars.rouletteTimeout); WRITEINT32(save->p, players[i].botvars.rouletteTimeout);
WRITEUINT32(save->p, players[i].botvars.predictionError);
WRITEUINT32(save->p, players[i].botvars.recentDeflection);
WRITEUINT32(save->p, players[i].botvars.lastAngle);
// itemroulette_t // itemroulette_t
WRITEUINT8(save->p, players[i].itemRoulette.active); WRITEUINT8(save->p, players[i].itemRoulette.active);
@ -1409,6 +1412,9 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
players[i].botvars.respawnconfirm = READUINT32(save->p); players[i].botvars.respawnconfirm = READUINT32(save->p);
players[i].botvars.roulettePriority = READUINT8(save->p); players[i].botvars.roulettePriority = READUINT8(save->p);
players[i].botvars.rouletteTimeout = READUINT32(save->p); players[i].botvars.rouletteTimeout = READUINT32(save->p);
players[i].botvars.predictionError = READUINT32(save->p);
players[i].botvars.recentDeflection = READUINT32(save->p);
players[i].botvars.lastAngle = READUINT32(save->p);
// itemroulette_t // itemroulette_t
players[i].itemRoulette.active = (boolean)READUINT8(save->p); players[i].itemRoulette.active = (boolean)READUINT8(save->p);

View file

@ -1206,11 +1206,15 @@ void P_ButteredSlope(mobj_t *mo)
// Let's get the gravity strength for the object... // Let's get the gravity strength for the object...
thrust = FixedMul(thrust, abs(P_GetMobjGravity(mo))); thrust = FixedMul(thrust, abs(P_GetMobjGravity(mo)));
if (mo->friction != ORIG_FRICTION) fixed_t basefriction = ORIG_FRICTION;
if (mo->player)
basefriction = K_PlayerBaseFriction(mo->player, ORIG_FRICTION);
if (mo->friction != basefriction && basefriction != 0)
{ {
// ... and its friction against the ground for good measure. // ... and its friction against the ground for good measure.
// (divided by original friction to keep behaviour for normal slopes the same) // (divided by original friction to keep behaviour for normal slopes the same)
thrust = FixedMul(thrust, FixedDiv(mo->friction, ORIG_FRICTION)); thrust = FixedMul(thrust, FixedDiv(mo->friction, basefriction));
// Sal: Also consider movefactor of players. // Sal: Also consider movefactor of players.
// We want ice to make slopes *really* funnel you in a specific direction. // We want ice to make slopes *really* funnel you in a specific direction.