From aab374e9ef9a94bf53944a7b6653b2898269d97c Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Thu, 21 Aug 2025 23:56:04 -0400 Subject: [PATCH 01/11] WIP: Speed Assist --- src/d_player.h | 2 + src/k_kart.c | 113 ++++++++++++++++++++++++++++++++++++++++++++ src/k_roulette.c | 42 ++++++++-------- src/k_roulette.h | 15 ++++++ src/lua_playerlib.c | 20 ++++---- src/p_saveg.cpp | 4 ++ 6 files changed, 167 insertions(+), 29 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index 553c2a422..132ec37d8 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -874,6 +874,8 @@ struct player_t UINT16 invincibilitytimer; // Invincibility timer UINT16 invincibilityextensions; // Used to control invinc time gains when it's already been extended. + fixed_t loneliness; // How long has a player been too far to interact? Do they need speed assist? + UINT8 eggmanexplode; // Fake item recieved, explode in a few seconds SINT8 eggmanblame; // (-1 to 15) - Fake item recieved, who set this fake diff --git a/src/k_kart.c b/src/k_kart.c index 4fdee3183..c638aa84a 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3950,6 +3950,25 @@ fixed_t K_GetKartSpeedFromStat(UINT8 kartspeed) return finalspeed; } +// Speed Assist pt.2: If we need assistance, how much? +static fixed_t K_GetKartSpeedAssist(const player_t *player) +{ + if (modeattacking) + return FRACUNIT; + if (gametype && GTR_BUMPERS) + return FRACUNIT; + if (specialstageinfo.valid) + return FRACUNIT; + if (K_PlayerUsesBotMovement(player)) + return FRACUNIT; + if (player->loneliness < 0) + return FRACUNIT; + + fixed_t MAX_SPEED_ASSIST = FRACUNIT; + + return FRACUNIT + FixedMul(player->loneliness, MAX_SPEED_ASSIST); +} + fixed_t K_GetKartSpeed(const player_t *player, boolean doboostpower, boolean dorubberband) { const boolean mobjValid = (player->mo != NULL && P_MobjWasRemoved(player->mo) == false); @@ -4016,6 +4035,8 @@ fixed_t K_GetKartSpeed(const player_t *player, boolean doboostpower, boolean dor finalspeed += FixedMul(player->outrun, physicsScale); } + finalspeed = FixedMul(finalspeed, K_GetKartSpeedAssist(player)); + return finalspeed; } @@ -9956,6 +9977,98 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) } } + if (!K_PlayerUsesBotMovement(player)) + { + UINT32 toDefender = 0; + UINT32 toFirst = 0; + + for (UINT8 i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] == false || players[i].spectator == true || players[i].exiting) + continue; + + if (players[i].position == player->position - 1) + toDefender = K_UndoMapScaling(player->distancetofinish - players[i].distancetofinish); + + if (players[i].position == 1) + toFirst = K_UndoMapScaling(player->distancetofinish - players[i].distancetofinish); + } + + UINT32 average = 0; + UINT8 counted = 0; + UINT32 firstRaw = 0; + + for (UINT8 i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] == false || players[i].spectator == true || players[i].exiting) + continue; + + if (players[i].position != 1) + { + counted++; + average += K_UndoMapScaling(players[i].distancetofinish); + } + else + { + firstRaw = K_UndoMapScaling(players[i].distancetofinish); + } + } + + average /= max(counted, 1); + + if (D_NumPlayersInRace() == 2) + { + average = firstRaw; + } + + UINT32 REALLY_FAR = average + 3000; // This far back, get max gain + UINT32 TOO_CLOSE = average + 1000; // Start gaining here, lose if closer + UINT32 WAY_TOO_CLOSE = average; // Lose at max rate here + + fixed_t MAX_GAIN_PER_SEC = FRACUNIT/10; // % assist to gain per sec when REALLY_FAR + fixed_t MAX_LOSS_PER_SEC = FRACUNIT/10; // % assist to lose per sec when WAY_TOO_CLOSE + + UINT32 gaingap = REALLY_FAR - TOO_CLOSE; + UINT32 lossgap = TOO_CLOSE - WAY_TOO_CLOSE; + + CONS_Printf("Mine %d - %d / %d / %d\n", player->distancetofinish, WAY_TOO_CLOSE, TOO_CLOSE, REALLY_FAR); + + UINT32 mydist = K_UndoMapScaling(player->distancetofinish); + + if (mydist >= TOO_CLOSE) + { + fixed_t gain = MAX_GAIN_PER_SEC / TICRATE; + fixed_t gainrate = FRACUNIT * (mydist - TOO_CLOSE) / gaingap; + gainrate = clamp(gainrate, 0, FRACUNIT); + gainrate = Easing_InCubic(gainrate, 0, FRACUNIT); + + gain = FixedMul(gain, gainrate); + + player->loneliness += gain; + + CONS_Printf("gaining @ %d - %d\n", gainrate, player->loneliness); + } + else + { + fixed_t loss = MAX_LOSS_PER_SEC / TICRATE; + fixed_t lossrate = FRACUNIT * (mydist - WAY_TOO_CLOSE) / lossgap; + lossrate = FRACUNIT - clamp(lossrate, 0, FRACUNIT); + lossrate = Easing_InCubic(lossrate, 0, FRACUNIT); + + loss = FixedMul(loss, lossrate); + + player->loneliness -= loss; + CONS_Printf("LOSING @ %d - %d\n", lossrate, player->loneliness); + } + + player->loneliness = clamp(player->loneliness, 0, FRACUNIT); + } + else + { + player->loneliness = 0; + } + + if (player->spheres > 40) player->spheres = 40; // where's the < 0 check? see below the following block! diff --git a/src/k_roulette.c b/src/k_roulette.c index 158f463ba..8a734688a 100644 --- a/src/k_roulette.c +++ b/src/k_roulette.c @@ -77,7 +77,7 @@ #define ROULETTE_SPEED_TIMEATTACK (9) #define ROULETTE_SPEED_VERSUS_SLOWEST (12) -static UINT32 K_DynamicItemOddsRace[NUMKARTRESULTS-1][2] = +static UINT32 K_DynamicItemOddsRace[NUMKARTRESULTS-1][2] = { // distance, duplication tolerance {20, 10}, // sneaker @@ -104,7 +104,7 @@ static UINT32 K_DynamicItemOddsRace[NUMKARTRESULTS-1][2] = {43, 5}, // gardentop {0, 0}, // gachabom {1, 2}, // stoneshoe - {1, 2}, // toxomister + {1, 2}, // toxomister {45, 6}, // dualsneaker {55, 8}, // triplesneaker {25, 2}, // triplebanana @@ -114,7 +114,7 @@ static UINT32 K_DynamicItemOddsRace[NUMKARTRESULTS-1][2] = {0, 0}, // triplegachabom }; -static UINT32 K_DynamicItemOddsBattle[NUMKARTRESULTS-1][2] = +static UINT32 K_DynamicItemOddsBattle[NUMKARTRESULTS-1][2] = { // distance, duplication tolerance {20, 1}, // sneaker @@ -141,7 +141,7 @@ static UINT32 K_DynamicItemOddsBattle[NUMKARTRESULTS-1][2] = {0, 0}, // gardentop {10, 5}, // gachabom {0, 0}, // stoneshoe - {0, 0}, // toxomister + {0, 0}, // toxomister {0, 0}, // dualsneaker {20, 1}, // triplesneaker {0, 0}, // triplebanana @@ -151,7 +151,7 @@ static UINT32 K_DynamicItemOddsBattle[NUMKARTRESULTS-1][2] = {10, 2}, // triplegachabom }; -static UINT32 K_DynamicItemOddsSpecial[NUMKARTRESULTS-1][2] = +static UINT32 K_DynamicItemOddsSpecial[NUMKARTRESULTS-1][2] = { // distance, duplication tolerance {15, 2}, // sneaker @@ -178,7 +178,7 @@ static UINT32 K_DynamicItemOddsSpecial[NUMKARTRESULTS-1][2] = {0, 0}, // gardentop {0, 0}, // gachabom {0, 0}, // stoneshoe - {0, 0}, // toxomister + {0, 0}, // toxomister {35, 2}, // dualsneaker {0, 0}, // triplesneaker {0, 0}, // triplebanana @@ -277,7 +277,7 @@ static kartslotmachine_t K_KartItemReelRingBox[] = KSM__MAX }; -static sfxenum_t ringboxsound[] = +static sfxenum_t ringboxsound[] = { sfx_slot00, sfx_slot01, @@ -436,7 +436,7 @@ fixed_t K_ItemOddsScale(UINT8 playerCount) } /*-------------------------------------------------- - static UINT32 K_UndoMapScaling(UINT32 distance) + UINT32 K_UndoMapScaling(UINT32 distance) Takes a raw map distance and adjusts it to be in x1 scale. @@ -447,7 +447,7 @@ fixed_t K_ItemOddsScale(UINT8 playerCount) Return:- Distance unscaled by mapobjectscale. --------------------------------------------------*/ -static UINT32 K_UndoMapScaling(UINT32 distance) +UINT32 K_UndoMapScaling(UINT32 distance) { if (mapobjectscale != FRACUNIT) { @@ -1115,7 +1115,7 @@ static boolean K_TimingPermitsItem(kartitems_t item, const itemroulette_t *roule boolean notNearEnd = false; boolean cooldownOnStart = false; - + switch (item) { case KITEM_BANANA: @@ -1154,7 +1154,7 @@ static boolean K_TimingPermitsItem(kartitems_t item, const itemroulette_t *roule { // In Race, we reintroduce and reenable this item to counter breakaway frontruns. // No need to roll it if that's not the case. - return false; + return false; break; } @@ -1210,7 +1210,7 @@ void K_FillItemRoulette(player_t *const player, itemroulette_t *const roulette, if (player != NULL) { roulette->baseDist = K_UndoMapScaling(player->distancetofinish); - + if (player->pflags & PF_AUTOROULETTE) roulette->autoroulette = true; @@ -1219,7 +1219,7 @@ void K_FillItemRoulette(player_t *const player, itemroulette_t *const roulette, // Lua may want to intercept reelbuilder entirely. LUA_HookPreFillItemRoulette(player, roulette, ringbox); - + // If prehook did something, no need to continue. if (roulette->itemList.len != 0) { return; @@ -1229,7 +1229,7 @@ void K_FillItemRoulette(player_t *const player, itemroulette_t *const roulette, // Lua can modify the final result. LUA_HookFillItemRoulette(player, roulette, ringbox); - + // If somehow there's no items, add sad. if (roulette->itemList.len == 0) { if (roulette->ringbox) @@ -1259,7 +1259,7 @@ void K_FillItemRouletteData(player_t *player, itemroulette_t *const roulette, bo { K_InitRoulette(roulette); } - + if (ringbox == true) { // If this is being invoked by a Ring Box, it should literally never produce items. @@ -1504,7 +1504,7 @@ void K_FillItemRouletteData(player_t *player, itemroulette_t *const roulette, bo { permit[i] = false; } - } + } // == REEL CANDIDATE PREP // Dynamic Roulette works by comparing an item's "ideal" distance to our current distance from 1st. @@ -1548,7 +1548,7 @@ void K_FillItemRouletteData(player_t *player, itemroulette_t *const roulette, bo if (player->position > 1) // Loneliness is expected when frontrunnning, don't influence their item table. { if ((gametyperules & GTR_CIRCUIT) && specialstageinfo.valid == false) - { + { for (i = 0; i < MAXPLAYERS; i++) { if (playeringame[i] == false || players[i].spectator == true || players[i].exiting) @@ -1583,7 +1583,7 @@ void K_FillItemRouletteData(player_t *player, itemroulette_t *const roulette, bo // incredibly forceful; there's a truly forced special case above. fixed_t spb_odds = K_PercentSPBOdds(roulette, player->position); - if ((gametyperules & GTR_CIRCUIT) + if ((gametyperules & GTR_CIRCUIT) && specialstageinfo.valid == false && (spb_odds > 0) & (spbplace == -1) && (roulette->preexpdist >= powers[KITEM_SPB]) // SPECIAL CASE: Check raw distance instead of EXP-influenced target distance. @@ -1689,7 +1689,7 @@ void K_FillItemRouletteData(player_t *player, itemroulette_t *const roulette, bo // This fills the spawnChance array with a rolling count of items, // so that we can loop upward through it until we hit our random index. for (i = 1; i < NUMKARTRESULTS; i++) - { + { // If an item is far too week for this reel, reject it. // This can happen in regions of the odds with a lot of items that // don't really like to be duplicated. Favor the player; high-rolling @@ -1736,7 +1736,7 @@ void K_FillItemRouletteData(player_t *player, itemroulette_t *const roulette, bo // == FINALLY ADD THIS SHIT TO THE REEL // Super simple: generate a random index, - // count up until we hit that index, + // count up until we hit that index, // insert that item and decrement everything after. while (totalSpawnChance > 0) { @@ -2041,7 +2041,7 @@ void K_KartItemRoulette(player_t *const player, ticcmd_t *const cmd) S_StartSound(NULL, sfx_s240); else S_StartSound(NULL, sfx_itrol1 + roulette->sound); - + if (roulette->index == 0 && roulette->itemList.len > 1) { S_StartSound(NULL, sfx_kc50); diff --git a/src/k_roulette.h b/src/k_roulette.h index e45dd7809..6473d43a1 100644 --- a/src/k_roulette.h +++ b/src/k_roulette.h @@ -108,6 +108,21 @@ fixed_t K_ItemOddsScale(UINT8 playerCount); UINT32 K_ScaleItemDistance(INT32 distance, UINT8 numPlayers); +/*-------------------------------------------------- + UINT32 K_UndoMapScaling(UINT32 distance) + + Takes a raw map distance and adjusts it to + be in x1 scale. + + Input Arguments:- + distance - Original distance. + + Return:- + Distance unscaled by mapobjectscale. +--------------------------------------------------*/ + +UINT32 K_UndoMapScaling(UINT32 distance); + /*-------------------------------------------------- void K_PushToRouletteItemList(itemroulette_t *const roulette, INT32 item) diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index bb2fa0817..203f2c117 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -102,16 +102,16 @@ static int lib_iterateDisplayplayers(lua_State *L) INT32 i = lua_tonumber(L, lua_upvalueindex(1)); if (lua_gettop(L) < 2) - { + { lua_pushcclosure(L, lib_iterateDisplayplayers, 1); return 1; } - + if (i <= r_splitscreen) { if (!playeringame[displayplayers[i]]) return 0; - + // Return player and splitscreen index. LUA_PushUserdata(L, &players[displayplayers[i]], META_PLAYER); lua_pushnumber(L, i); @@ -515,6 +515,8 @@ static int player_get(lua_State *L) lua_pushinteger(L, plr->invincibilitytimer); else if (fastcmp(field,"invincibilityextensions")) lua_pushinteger(L, plr->invincibilityextensions); + else if (fastcmp(field,"loneliness")) + lua_pushinteger(L, plr->loneliness); else if (fastcmp(field,"eggmanexplode")) lua_pushinteger(L, plr->eggmanexplode); else if (fastcmp(field,"eggmanblame")) @@ -748,7 +750,7 @@ static int player_get(lua_State *L) else if (fastcmp(field,"griefstrikes")) lua_pushinteger(L, plr->griefStrikes); else if (fastcmp(field,"griefwarned")) - lua_pushinteger(L, plr->griefWarned); + lua_pushinteger(L, plr->griefWarned); else if (fastcmp(field,"stairjank")) lua_pushinteger(L, plr->stairjank); else if (fastcmp(field,"splitscreenindex")) @@ -947,9 +949,9 @@ static int player_set(lua_State *L) else if (fastcmp(field,"amps")) plr->amps = luaL_checkinteger(L, 3); else if (fastcmp(field,"amppickup")) - plr->amppickup = luaL_checkinteger(L, 3); + plr->amppickup = luaL_checkinteger(L, 3); else if (fastcmp(field,"ampspending")) - plr->ampspending = luaL_checkinteger(L, 3); + plr->ampspending = luaL_checkinteger(L, 3); else if (fastcmp(field,"itemflags")) plr->itemflags = luaL_checkinteger(L, 3); else if (fastcmp(field,"outrun")) @@ -1166,6 +1168,8 @@ static int player_set(lua_State *L) plr->invincibilitytimer = luaL_checkinteger(L, 3); else if (fastcmp(field,"invincibilityextensions")) plr->invincibilityextensions = luaL_checkinteger(L, 3); + else if (fastcmp(field,"loneliness")) + plr->loneliness = luaL_checkinteger(L, 3); else if (fastcmp(field,"eggmanexplode")) plr->eggmanexplode = luaL_checkinteger(L, 3); else if (fastcmp(field,"eggmanblame")) @@ -1813,12 +1817,12 @@ int LUA_PlayerLib(lua_State *L) lua_pushcfunction(L, ticcmd_set); lua_setfield(L, -2, "__newindex"); lua_pop(L,1); - + luaL_newmetatable(L, META_SONICLOOPVARS); lua_pushcfunction(L, sonicloopvars_get); lua_setfield(L, -2, "__index"); lua_pop(L,1); - + luaL_newmetatable(L, META_SONICLOOPCAMVARS); lua_pushcfunction(L, sonicloopcamvars_get); lua_setfield(L, -2, "__index"); diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index aee0a3fa4..0141f5f76 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -585,6 +585,8 @@ static void P_NetArchivePlayers(savebuffer_t *save) WRITEUINT16(save->p, players[i].invincibilitytimer); WRITEUINT16(save->p, players[i].invincibilityextensions); + WRITEFIXED(save->p, players[i].loneliness); + WRITEUINT8(save->p, players[i].eggmanexplode); WRITESINT8(save->p, players[i].eggmanblame); @@ -1256,6 +1258,8 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) players[i].invincibilitytimer = READUINT16(save->p); players[i].invincibilityextensions = READUINT16(save->p); + players[i].loneliness = READFIXED(save->p); + players[i].eggmanexplode = READUINT8(save->p); players[i].eggmanblame = READSINT8(save->p); From 265c8d969e5f125613494b3046675f2470f0d83e Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Fri, 22 Aug 2025 03:16:21 -0400 Subject: [PATCH 02/11] Somewhat real constants, debug UI --- src/k_hud.cpp | 11 +++++++---- src/k_kart.c | 17 +++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/k_hud.cpp b/src/k_hud.cpp index e14d31342..d7a388077 100644 --- a/src/k_hud.cpp +++ b/src/k_hud.cpp @@ -4574,17 +4574,17 @@ static void K_drawKartAccessibilityIcons(boolean gametypeinfoshown, INT32 fx) mirror = true; } } - + // Adjust for Lua disabling things underneath or to the left of the speedometer. if (!LUA_HudEnabled(hud_rings)) - { + { if (r_splitscreen < 2) { fy += 14; } // For 4P race, only check if it's a race. // For 4P battle/capsules, check if it's either prisons or battle, AND check if that element isn't disabled. - else if ((gametyperules & GTR_CIRCUIT) == GTR_CIRCUIT || + else if ((gametyperules & GTR_CIRCUIT) == GTR_CIRCUIT || ((battleprisons || (gametyperules & GTR_BUMPERS) == GTR_BUMPERS) && !LUA_HudEnabled(hud_gametypeinfo))) { fx -= 44; @@ -4716,7 +4716,7 @@ static void K_drawKartSpeedometer(boolean gametypeinfoshown) { fy += 9; } - + // Adjust for Lua disabling things underneath the speedometer. if (!LUA_HudEnabled(hud_rings)) { @@ -4730,6 +4730,9 @@ static void K_drawKartSpeedometer(boolean gametypeinfoshown) V_DrawScaledPatch(LAPS_X+19, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[numbers[2]]); V_DrawScaledPatch(LAPS_X+29, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_speedometerlabel[labeln]); + // debug + V_DrawThinString(LAPS_X+7, fy-10, V_HUDTRANS|V_SLIDEIN|splitflags, va("%d\%", stplyr->loneliness*100/FRACUNIT)); + K_drawKartAccessibilityIcons(gametypeinfoshown, 56); } diff --git a/src/k_kart.c b/src/k_kart.c index c638aa84a..3957f84a1 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3964,7 +3964,7 @@ static fixed_t K_GetKartSpeedAssist(const player_t *player) if (player->loneliness < 0) return FRACUNIT; - fixed_t MAX_SPEED_ASSIST = FRACUNIT; + fixed_t MAX_SPEED_ASSIST = FRACUNIT/3; return FRACUNIT + FixedMul(player->loneliness, MAX_SPEED_ASSIST); } @@ -10021,18 +10021,16 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) average = firstRaw; } - UINT32 REALLY_FAR = average + 3000; // This far back, get max gain - UINT32 TOO_CLOSE = average + 1000; // Start gaining here, lose if closer + UINT32 REALLY_FAR = average + 6000; // This far back, get max gain + UINT32 TOO_CLOSE = average + 3000; // Start gaining here, lose if closer UINT32 WAY_TOO_CLOSE = average; // Lose at max rate here - fixed_t MAX_GAIN_PER_SEC = FRACUNIT/10; // % assist to gain per sec when REALLY_FAR - fixed_t MAX_LOSS_PER_SEC = FRACUNIT/10; // % assist to lose per sec when WAY_TOO_CLOSE + fixed_t MAX_GAIN_PER_SEC = FRACUNIT/40; // % assist to gain per sec when REALLY_FAR + fixed_t MAX_LOSS_PER_SEC = FRACUNIT/20; // % assist to lose per sec when WAY_TOO_CLOSE UINT32 gaingap = REALLY_FAR - TOO_CLOSE; UINT32 lossgap = TOO_CLOSE - WAY_TOO_CLOSE; - CONS_Printf("Mine %d - %d / %d / %d\n", player->distancetofinish, WAY_TOO_CLOSE, TOO_CLOSE, REALLY_FAR); - UINT32 mydist = K_UndoMapScaling(player->distancetofinish); if (mydist >= TOO_CLOSE) @@ -10045,12 +10043,12 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) gain = FixedMul(gain, gainrate); player->loneliness += gain; - - CONS_Printf("gaining @ %d - %d\n", gainrate, player->loneliness); } else { fixed_t loss = MAX_LOSS_PER_SEC / TICRATE; + if (mydist < WAY_TOO_CLOSE) + mydist = WAY_TOO_CLOSE; fixed_t lossrate = FRACUNIT * (mydist - WAY_TOO_CLOSE) / lossgap; lossrate = FRACUNIT - clamp(lossrate, 0, FRACUNIT); lossrate = Easing_InCubic(lossrate, 0, FRACUNIT); @@ -10058,7 +10056,6 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) loss = FixedMul(loss, lossrate); player->loneliness -= loss; - CONS_Printf("LOSING @ %d - %d\n", lossrate, player->loneliness); } player->loneliness = clamp(player->loneliness, 0, FRACUNIT); From dcaa3873a1ee3e708b8f1052663248e4cb55798c Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Fri, 22 Aug 2025 04:05:08 -0400 Subject: [PATCH 03/11] Speed Assist tweaks --- src/k_kart.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 3957f84a1..70493f751 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3964,7 +3964,7 @@ static fixed_t K_GetKartSpeedAssist(const player_t *player) if (player->loneliness < 0) return FRACUNIT; - fixed_t MAX_SPEED_ASSIST = FRACUNIT/3; + fixed_t MAX_SPEED_ASSIST = FRACUNIT/4; return FRACUNIT + FixedMul(player->loneliness, MAX_SPEED_ASSIST); } @@ -10026,7 +10026,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) UINT32 WAY_TOO_CLOSE = average; // Lose at max rate here fixed_t MAX_GAIN_PER_SEC = FRACUNIT/40; // % assist to gain per sec when REALLY_FAR - fixed_t MAX_LOSS_PER_SEC = FRACUNIT/20; // % assist to lose per sec when WAY_TOO_CLOSE + fixed_t MAX_LOSS_PER_SEC = FRACUNIT/5; // % assist to lose per sec when WAY_TOO_CLOSE UINT32 gaingap = REALLY_FAR - TOO_CLOSE; UINT32 lossgap = TOO_CLOSE - WAY_TOO_CLOSE; From 0ee5e7689e471c0ca916ee03528f724cf60eaebb Mon Sep 17 00:00:00 2001 From: VelocitOni Date: Fri, 22 Aug 2025 04:43:09 -0400 Subject: [PATCH 04/11] Moved the points all back Moved where it all happens back by +3000 each, halved gain-rate. --- src/k_kart.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 70493f751..0f1a90abe 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -10021,11 +10021,11 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) average = firstRaw; } - UINT32 REALLY_FAR = average + 6000; // This far back, get max gain - UINT32 TOO_CLOSE = average + 3000; // Start gaining here, lose if closer - UINT32 WAY_TOO_CLOSE = average; // Lose at max rate here + UINT32 REALLY_FAR = average + 9000; // This far back, get max gain + UINT32 TOO_CLOSE = average + 6000; // Start gaining here, lose if closer + UINT32 WAY_TOO_CLOSE = average + 3000; // Lose at max rate here - fixed_t MAX_GAIN_PER_SEC = FRACUNIT/40; // % assist to gain per sec when REALLY_FAR + fixed_t MAX_GAIN_PER_SEC = FRACUNIT/20; // % assist to gain per sec when REALLY_FAR fixed_t MAX_LOSS_PER_SEC = FRACUNIT/5; // % assist to lose per sec when WAY_TOO_CLOSE UINT32 gaingap = REALLY_FAR - TOO_CLOSE; From 76ef610a734b66f7123d49f46de9965ac3bf1753 Mon Sep 17 00:00:00 2001 From: VelocitOni Date: Fri, 22 Aug 2025 05:08:06 -0400 Subject: [PATCH 05/11] Max is /5 /4 -> /5 max speed assist gain --- src/k_kart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_kart.c b/src/k_kart.c index 0f1a90abe..f7d104920 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3964,7 +3964,7 @@ static fixed_t K_GetKartSpeedAssist(const player_t *player) if (player->loneliness < 0) return FRACUNIT; - fixed_t MAX_SPEED_ASSIST = FRACUNIT/4; + fixed_t MAX_SPEED_ASSIST = FRACUNIT/5; return FRACUNIT + FixedMul(player->loneliness, MAX_SPEED_ASSIST); } From c6ddce7fa7f405cafad9e2c8b73438ea8f2c6cc2 Mon Sep 17 00:00:00 2001 From: VelocitOni Date: Fri, 22 Aug 2025 06:13:14 -0400 Subject: [PATCH 06/11] More nerfs +10% speed max, builds up slower again (my mistake), WAY further away. Even stricter on being "too close" --- src/k_kart.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index f7d104920..8d611507c 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3964,7 +3964,7 @@ static fixed_t K_GetKartSpeedAssist(const player_t *player) if (player->loneliness < 0) return FRACUNIT; - fixed_t MAX_SPEED_ASSIST = FRACUNIT/5; + fixed_t MAX_SPEED_ASSIST = FRACUNIT/10; return FRACUNIT + FixedMul(player->loneliness, MAX_SPEED_ASSIST); } @@ -10021,11 +10021,11 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) average = firstRaw; } - UINT32 REALLY_FAR = average + 9000; // This far back, get max gain - UINT32 TOO_CLOSE = average + 6000; // Start gaining here, lose if closer - UINT32 WAY_TOO_CLOSE = average + 3000; // Lose at max rate here + UINT32 REALLY_FAR = average + 10000; // This far back, get max gain + UINT32 TOO_CLOSE = average + 7000; // Start gaining here, lose if closer + UINT32 WAY_TOO_CLOSE = average + 6000; // Lose at max rate here - fixed_t MAX_GAIN_PER_SEC = FRACUNIT/20; // % assist to gain per sec when REALLY_FAR + fixed_t MAX_GAIN_PER_SEC = FRACUNIT/40; // % assist to gain per sec when REALLY_FAR fixed_t MAX_LOSS_PER_SEC = FRACUNIT/5; // % assist to lose per sec when WAY_TOO_CLOSE UINT32 gaingap = REALLY_FAR - TOO_CLOSE; From 1dfa72da485d3f351da7d4efddcb7097bb6493db Mon Sep 17 00:00:00 2001 From: VelocitOni Date: Fri, 22 Aug 2025 06:36:26 -0400 Subject: [PATCH 07/11] Middleman /7 speed assist max, builds up slightly faster --- src/k_kart.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 8d611507c..c3a9abd5d 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3964,7 +3964,7 @@ static fixed_t K_GetKartSpeedAssist(const player_t *player) if (player->loneliness < 0) return FRACUNIT; - fixed_t MAX_SPEED_ASSIST = FRACUNIT/10; + fixed_t MAX_SPEED_ASSIST = FRACUNIT/7; return FRACUNIT + FixedMul(player->loneliness, MAX_SPEED_ASSIST); } @@ -10025,7 +10025,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) UINT32 TOO_CLOSE = average + 7000; // Start gaining here, lose if closer UINT32 WAY_TOO_CLOSE = average + 6000; // Lose at max rate here - fixed_t MAX_GAIN_PER_SEC = FRACUNIT/40; // % assist to gain per sec when REALLY_FAR + fixed_t MAX_GAIN_PER_SEC = FRACUNIT/30; // % assist to gain per sec when REALLY_FAR fixed_t MAX_LOSS_PER_SEC = FRACUNIT/5; // % assist to lose per sec when WAY_TOO_CLOSE UINT32 gaingap = REALLY_FAR - TOO_CLOSE; From ddb034f0564c04d3d12c413384224064425b57e7 Mon Sep 17 00:00:00 2001 From: VelocitOni Date: Fri, 22 Aug 2025 18:36:59 -0400 Subject: [PATCH 08/11] Point to the back quarter of racers Instead of everyone behind 1st being considered for average, only consider the last 25% of players. Speed assist is stronger again to make up for this (+25% again) --- src/k_kart.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index c3a9abd5d..f88eee6bf 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3964,7 +3964,7 @@ static fixed_t K_GetKartSpeedAssist(const player_t *player) if (player->loneliness < 0) return FRACUNIT; - fixed_t MAX_SPEED_ASSIST = FRACUNIT/7; + fixed_t MAX_SPEED_ASSIST = FRACUNIT/4; return FRACUNIT + FixedMul(player->loneliness, MAX_SPEED_ASSIST); } @@ -10003,7 +10003,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) if (playeringame[i] == false || players[i].spectator == true || players[i].exiting) continue; - if (players[i].position != 1) + if (players[i].position != 1 && players[i].position >= (D_NumPlayersInRace()*3)/4) //Not in 1st, but also back quarter of the average { counted++; average += K_UndoMapScaling(players[i].distancetofinish); @@ -10025,7 +10025,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) UINT32 TOO_CLOSE = average + 7000; // Start gaining here, lose if closer UINT32 WAY_TOO_CLOSE = average + 6000; // Lose at max rate here - fixed_t MAX_GAIN_PER_SEC = FRACUNIT/30; // % assist to gain per sec when REALLY_FAR + fixed_t MAX_GAIN_PER_SEC = FRACUNIT/20; // % assist to gain per sec when REALLY_FAR fixed_t MAX_LOSS_PER_SEC = FRACUNIT/5; // % assist to lose per sec when WAY_TOO_CLOSE UINT32 gaingap = REALLY_FAR - TOO_CLOSE; From 563f76638acbf224843ef692a7b141dfa4b9b4ef Mon Sep 17 00:00:00 2001 From: VelocitOni Date: Fri, 22 Aug 2025 19:57:06 -0400 Subject: [PATCH 09/11] " - 1" on positioned players average Consider just 1 more guy in the average, maybe helps really really small games. --- src/k_kart.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index f88eee6bf..6a8676266 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -10003,7 +10003,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) if (playeringame[i] == false || players[i].spectator == true || players[i].exiting) continue; - if (players[i].position != 1 && players[i].position >= (D_NumPlayersInRace()*3)/4) //Not in 1st, but also back quarter of the average + if (players[i].position != 1 && players[i].position >= ((D_NumPlayersInRace()*3)/4) - 1) // Not in 1st, but also back quarter of the average (-1 guy, for leeway) { counted++; average += K_UndoMapScaling(players[i].distancetofinish); @@ -10020,7 +10020,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) { average = firstRaw; } - + UINT32 REALLY_FAR = average + 10000; // This far back, get max gain UINT32 TOO_CLOSE = average + 7000; // Start gaining here, lose if closer UINT32 WAY_TOO_CLOSE = average + 6000; // Lose at max rate here From be6d51247c8dbcc750082fd92ef34f9f77a6401c Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Sat, 23 Aug 2025 03:00:21 -0400 Subject: [PATCH 10/11] Use less speed assist when applying boosts --- src/k_kart.c | 49 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 6a8676266..a5c86f128 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3954,19 +3954,19 @@ fixed_t K_GetKartSpeedFromStat(UINT8 kartspeed) static fixed_t K_GetKartSpeedAssist(const player_t *player) { if (modeattacking) - return FRACUNIT; + return 0; if (gametype && GTR_BUMPERS) - return FRACUNIT; + return 0; if (specialstageinfo.valid) - return FRACUNIT; + return 0; if (K_PlayerUsesBotMovement(player)) - return FRACUNIT; + return 0; if (player->loneliness < 0) - return FRACUNIT; + return 0; fixed_t MAX_SPEED_ASSIST = FRACUNIT/4; - return FRACUNIT + FixedMul(player->loneliness, MAX_SPEED_ASSIST); + return FixedMul(player->loneliness, MAX_SPEED_ASSIST); } fixed_t K_GetKartSpeed(const player_t *player, boolean doboostpower, boolean dorubberband) @@ -4035,7 +4035,39 @@ fixed_t K_GetKartSpeed(const player_t *player, boolean doboostpower, boolean dor finalspeed += FixedMul(player->outrun, physicsScale); } - finalspeed = FixedMul(finalspeed, K_GetKartSpeedAssist(player)); + // Speed Assist pt.3: How much of our potential assist do we apply? + if (doboostpower && K_GetKartSpeedAssist(player)) + { + fixed_t assist = K_GetKartSpeedAssist(player); + + // Don't use all of our speed assist if we're already under boost effect. + fixed_t START_ASSIST_ROLLOFF = 3*FRACUNIT/2; // Don't roll off at below this speed + fixed_t END_ASSIST_ROLLOFF = 5*FRACUNIT/2; // Use minimum assist power at above this speed + fixed_t MIN_ASSIST_POWER = 0; // % assist to apply while going fast (FRACUNIT=full, 0=none) + + fixed_t speedgap = END_ASSIST_ROLLOFF - START_ASSIST_ROLLOFF; + + fixed_t bonusspeed = FixedDiv(player->speed, K_GetKartSpeed(player, false, false)); + + if (doboostpower) + { + if (bonusspeed < START_ASSIST_ROLLOFF) + { + // :) + } + else if (bonusspeed > END_ASSIST_ROLLOFF) + { + assist = FixedMul(assist, MIN_ASSIST_POWER); + } + else + { + fixed_t normalizer = FixedDiv((bonusspeed - START_ASSIST_ROLLOFF), speedgap); + assist = Easing_Linear(normalizer, assist, FixedMul(assist, MIN_ASSIST_POWER)); + } + } + + finalspeed = FixedMul(finalspeed, FRACUNIT + assist); + } return finalspeed; } @@ -9977,6 +10009,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) } } + // Speed Assist pt.1 if (!K_PlayerUsesBotMovement(player)) { UINT32 toDefender = 0; @@ -10020,7 +10053,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) { average = firstRaw; } - + UINT32 REALLY_FAR = average + 10000; // This far back, get max gain UINT32 TOO_CLOSE = average + 7000; // Start gaining here, lose if closer UINT32 WAY_TOO_CLOSE = average + 6000; // Lose at max rate here From 5d4326a2d0ebb2e1102a95ad9ab281844e102b95 Mon Sep 17 00:00:00 2001 From: VelocitOni Date: Sun, 24 Aug 2025 03:40:16 -0400 Subject: [PATCH 11/11] Doesn't apply to tutorial or free play anymore Doesn't apply to free play anymore, battle mode either (correction) --- src/k_hud.cpp | 4 +++- src/k_kart.c | 6 +++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/k_hud.cpp b/src/k_hud.cpp index d7a388077..555887961 100644 --- a/src/k_hud.cpp +++ b/src/k_hud.cpp @@ -4730,8 +4730,10 @@ static void K_drawKartSpeedometer(boolean gametypeinfoshown) V_DrawScaledPatch(LAPS_X+19, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[numbers[2]]); V_DrawScaledPatch(LAPS_X+29, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_speedometerlabel[labeln]); - // debug + /* + // debug for Speed Assist V_DrawThinString(LAPS_X+7, fy-10, V_HUDTRANS|V_SLIDEIN|splitflags, va("%d\%", stplyr->loneliness*100/FRACUNIT)); + */ K_drawKartAccessibilityIcons(gametypeinfoshown, 56); } diff --git a/src/k_kart.c b/src/k_kart.c index a5c86f128..9f01871d7 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3953,9 +3953,13 @@ fixed_t K_GetKartSpeedFromStat(UINT8 kartspeed) // Speed Assist pt.2: If we need assistance, how much? static fixed_t K_GetKartSpeedAssist(const player_t *player) { + if (!M_NotFreePlay()) + return 0; if (modeattacking) return 0; - if (gametype && GTR_BUMPERS) + if (gametyperules & GTR_BUMPERS) + return 0; + if (gametype == GT_TUTORIAL) return 0; if (specialstageinfo.valid) return 0;