Merge branch 'bubble-speed-cap' into 'master'

Bubble refinements

See merge request kart-krew-dev/ring-racers-internal!2639
This commit is contained in:
Oni VelocitOni 2025-06-21 05:19:17 +00:00
commit 53129a6b73
10 changed files with 98 additions and 18 deletions

View file

@ -1046,6 +1046,7 @@ struct player_t
UINT8 lastsafecheatcheck; UINT8 lastsafecheatcheck;
UINT8 ignoreAirtimeLeniency; // We bubblebounced or otherwise did an airtime thing with control, powerup timers should still count down UINT8 ignoreAirtimeLeniency; // We bubblebounced or otherwise did an airtime thing with control, powerup timers should still count down
boolean bubbledrag; // Just bubblebounced, slow down!
fixed_t topAccel; // Reduced on straight wall collisions to give players extra recovery time fixed_t topAccel; // Reduced on straight wall collisions to give players extra recovery time
fixed_t vortexBoost; fixed_t vortexBoost;

View file

@ -2047,6 +2047,7 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi
"S_BUBC2", "S_BUBC2",
"S_BUBD1", "S_BUBD1",
"S_BUBE1", "S_BUBE1",
"S_BUBG1", // F used by Nova Shore
// Flame Shield // Flame Shield
"S_FLAMESHIELD1", "S_FLAMESHIELD1",

View file

@ -390,6 +390,7 @@ char sprnames[NUMSPRITES + 1][5] =
"BUBC", // Bubble Shield Bottom Wave "BUBC", // Bubble Shield Bottom Wave
"BUBD", // Bubble Shield Reflection "BUBD", // Bubble Shield Reflection
"BUBE", // Bubble Shield Underline "BUBE", // Bubble Shield Underline
"BUBG", // Bubble Shield drag
"BWVE", // Bubble Shield waves "BWVE", // Bubble Shield waves
"FLMS", // Flame Shield "FLMS", // Flame Shield
"FLMA", // Flame Shield Top Layer "FLMA", // Flame Shield Top Layer
@ -2604,6 +2605,7 @@ state_t states[NUMSTATES] =
{SPR_NULL, 0, 5, {NULL}, 0, 0, S_BUBC1}, // S_BUBC2 {SPR_NULL, 0, 5, {NULL}, 0, 0, S_BUBC1}, // S_BUBC2
{SPR_BUBD, FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_BUBD1}, // S_BUBD1 {SPR_BUBD, FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_BUBD1}, // S_BUBD1
{SPR_BUBE, FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_BUBE1}, // S_BUBE1 {SPR_BUBE, FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_BUBE1}, // S_BUBE1
{SPR_BUBG, FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_BUBG1}, // S_BUBG1
{SPR_FLMS, FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_FLAMESHIELD2}, // S_FLAMESHIELD1 {SPR_FLMS, FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_FLAMESHIELD2}, // S_FLAMESHIELD1
{SPR_FLMS, FF_FULLBRIGHT|9, 2, {NULL}, 0, 0, S_FLAMESHIELD3}, // S_FLAMESHIELD2 {SPR_FLMS, FF_FULLBRIGHT|9, 2, {NULL}, 0, 0, S_FLAMESHIELD3}, // S_FLAMESHIELD2

View file

@ -931,6 +931,7 @@ typedef enum sprite
SPR_BUBC, // Bubble Shield Bottom Wave SPR_BUBC, // Bubble Shield Bottom Wave
SPR_BUBD, // Bubble Shield Reflection SPR_BUBD, // Bubble Shield Reflection
SPR_BUBE, // Bubble Shield Underline SPR_BUBE, // Bubble Shield Underline
SPR_BUBG, // Bubble Shield drag
SPR_BWVE, // Bubble Shield waves SPR_BWVE, // Bubble Shield waves
SPR_FLMS, // Flame Shield SPR_FLMS, // Flame Shield
SPR_FLMA, // Flame Shield Top Layer SPR_FLMA, // Flame Shield Top Layer
@ -3101,6 +3102,7 @@ typedef enum state
S_BUBC2, S_BUBC2,
S_BUBD1, S_BUBD1,
S_BUBE1, S_BUBE1,
S_BUBG1,
// Flame Shield // Flame Shield
S_FLAMESHIELD1, S_FLAMESHIELD1,

View file

@ -72,6 +72,15 @@
// comeback is Battle Mode's karma comeback, also bool // comeback is Battle Mode's karma comeback, also bool
// mapreset is set when enough players fill an empty server // mapreset is set when enough players fill an empty server
static void K_PopBubbleShield(player_t *player)
{
S_StartSound(player->mo, sfx_kc31);
K_StripItems(player);
K_AddHitLag(player->mo, 4, false);
vector3_t offset = { 0, 0, 0 };
K_SpawnSingleHitLagSpark(player->mo, &offset, player->mo->scale*2, 4, 0, player->skincolor);
}
boolean K_ThunderDome(void) boolean K_ThunderDome(void)
{ {
if (K_CanChangeRules(true)) if (K_CanChangeRules(true))
@ -479,17 +488,24 @@ boolean K_IsPlayerLosing(player_t *player)
} }
// Some behavior should change if the player approaches the frontrunner unusually fast. // Some behavior should change if the player approaches the frontrunner unusually fast.
boolean K_IsPlayerScamming(player_t *player) fixed_t K_PlayerScamPercentage(player_t *player, UINT8 mult)
{ {
if (!M_NotFreePlay()) if (!M_NotFreePlay())
return false; return 0;
if (!(gametyperules & GTR_CIRCUIT)) if (!(gametyperules & GTR_CIRCUIT))
return false; return 0;
// "Why 8?" Consistency // "Why 8?" Consistency
// "Why 2000?" Vibes // "Why 2000?" Vibes
return (K_GetItemRouletteDistance(player, 8) < SCAMDIST);
UINT32 distance = K_GetItemRouletteDistance(player, 8);
UINT32 scamdistance = mult * SCAMDIST;
if (distance >= scamdistance)
return 0;
return Easing_Linear((scamdistance - distance) * FRACUNIT / scamdistance, 0, FRACUNIT);
} }
fixed_t K_GetKartGameSpeedScalar(SINT8 value) fixed_t K_GetKartGameSpeedScalar(SINT8 value)
@ -9951,7 +9967,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
if (player->invincibilitytimer && (player->ignoreAirtimeLeniency > 0 || onground == true || K_PowerUpRemaining(player, POWERUP_SMONITOR))) if (player->invincibilitytimer && (player->ignoreAirtimeLeniency > 0 || onground == true || K_PowerUpRemaining(player, POWERUP_SMONITOR)))
{ {
player->invincibilitytimer--; player->invincibilitytimer--;
if (player->invincibilitytimer && K_IsPlayerScamming(player)) if (player->invincibilitytimer && K_PlayerScamPercentage(player, 1))
player->invincibilitytimer--; player->invincibilitytimer--;
// Extra tripwire leniency for the end of invincibility // Extra tripwire leniency for the end of invincibility
@ -10029,7 +10045,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
if (player->growshrinktimer > 0 && (onground == true || player->ignoreAirtimeLeniency > 0)) if (player->growshrinktimer > 0 && (onground == true || player->ignoreAirtimeLeniency > 0))
{ {
player->growshrinktimer--; player->growshrinktimer--;
if (player->growshrinktimer && K_IsPlayerScamming(player)) if (player->growshrinktimer && K_PlayerScamPercentage(player, 1))
player->growshrinktimer--; player->growshrinktimer--;
} }
@ -10060,6 +10076,34 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
if (player->ignoreAirtimeLeniency) if (player->ignoreAirtimeLeniency)
player->ignoreAirtimeLeniency--; player->ignoreAirtimeLeniency--;
if (player->bubbledrag)
{
if (onground)
{
player->bubbledrag = false;
}
else
{
fixed_t scam = K_PlayerScamPercentage(player, BUBBLESCAM);
fixed_t speed = R_PointToDist2(0, 0, player->mo->momx, player->mo->momy);
fixed_t basespeed = K_GetKartSpeed(player, false, false);
fixed_t targetspeed = 4 * basespeed;
targetspeed -= FixedMul(scam, targetspeed);
// CONS_Printf("spd %d tspd %d psp %d\n", speed, targetspeed, scam);
fixed_t div = 12*FRACUNIT; // bigger number slower drag
if (speed > targetspeed)
{
fixed_t newspeed = speed - FixedDiv((speed - targetspeed), div);
player->mo->momx = FixedMul(FixedDiv(player->mo->momx, speed), newspeed);
player->mo->momy = FixedMul(FixedDiv(player->mo->momy, speed), newspeed);
}
}
}
if (player->freeRingShooterCooldown && !player->mo->hitlag) if (player->freeRingShooterCooldown && !player->mo->hitlag)
player->freeRingShooterCooldown--; player->freeRingShooterCooldown--;
@ -13219,6 +13263,7 @@ boolean K_FastFallBounce(player_t *player)
P_InstaThrust(player->mo, player->mo->angle, 11*max(minspeed, fallspeed)/10); P_InstaThrust(player->mo, player->mo->angle, 11*max(minspeed, fallspeed)/10);
player->ignoreAirtimeLeniency = max(player->ignoreAirtimeLeniency, TICRATE); player->ignoreAirtimeLeniency = max(player->ignoreAirtimeLeniency, TICRATE);
player->bubbledrag = true;
bounce += 3 * mapobjectscale; bounce += 3 * mapobjectscale;
@ -13237,14 +13282,12 @@ boolean K_FastFallBounce(player_t *player)
numplayers = 1; // solo behavior numplayers = 1; // solo behavior
} }
/*
if (player->position == 1 && player->positiondelay <= 0 && numplayers != 1) if (player->position == 1 && player->positiondelay <= 0 && numplayers != 1)
{ {
S_StartSound(player->mo, sfx_kc31); K_PopBubbleShield(player);
K_StripItems(player);
K_AddHitLag(player->mo, 4, false);
vector3_t offset = { 0, 0, 0 };
K_SpawnSingleHitLagSpark(player->mo, &offset, player->mo->scale*2, 4, 0, player->skincolor);
} }
*/
if (player->tripwireReboundDelay) if (player->tripwireReboundDelay)
bounce /= 2; bounce /= 2;
@ -14513,14 +14556,20 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
if (player->bubbleblowup > bubbletime*2) if (player->bubbleblowup > bubbletime*2)
{ {
player->itemamount--; player->itemamount--;
K_ThrowKartItem(player, (player->throwdir > 0), MT_BUBBLESHIELDTRAP, -1, 0, 0);
if (player->throwdir == -1) if (player->throwdir == -1)
{ {
P_InstaThrust(player->mo, player->mo->angle, player->speed + (80 * mapobjectscale)); P_InstaThrust(player->mo, player->mo->angle, player->speed + (80 * mapobjectscale));
player->wavedashboost += TICRATE; player->wavedashboost += TICRATE;
player->wavedashpower = FRACUNIT; player->wavedashpower = FRACUNIT;
player->fakeBoost += TICRATE/2; player->fakeBoost += TICRATE/2;
K_PopBubbleShield(player);
} }
else
{
K_ThrowKartItem(player, (player->throwdir > 0), MT_BUBBLESHIELDTRAP, -1, 0, 0);
}
K_PlayAttackTaunt(player->mo); K_PlayAttackTaunt(player->mo);
player->bubbleblowup = 0; player->bubbleblowup = 0;
player->bubblecool = 0; player->bubblecool = 0;

View file

@ -91,6 +91,8 @@ Make sure this matches the actual number of states
#define MAXTOPACCEL (12*FRACUNIT) #define MAXTOPACCEL (12*FRACUNIT)
#define TOPACCELREGEN (FRACUNIT/16) #define TOPACCELREGEN (FRACUNIT/16)
#define BUBBLESCAM (4)
// Handling boosts and sliptide conditions got weird. // Handling boosts and sliptide conditions got weird.
// You must be under a handling boost of at least SLIPTIDEHANDLING to sliptide. // You must be under a handling boost of at least SLIPTIDEHANDLING to sliptide.
// HANDLESCALING is used to adjust all handling boosts simultaneously (weight factors in the future?) // HANDLESCALING is used to adjust all handling boosts simultaneously (weight factors in the future?)
@ -124,7 +126,7 @@ UINT32 K_GetPlayerDontDrawFlag(player_t *player);
void K_ReduceVFXForEveryone(mobj_t *mo); void K_ReduceVFXForEveryone(mobj_t *mo);
boolean K_IsPlayerLosing(player_t *player); boolean K_IsPlayerLosing(player_t *player);
boolean K_IsPlayerScamming(player_t *player); fixed_t K_PlayerScamPercentage(player_t *player, UINT8 mult);
fixed_t K_GetKartGameSpeedScalar(SINT8 value); fixed_t K_GetKartGameSpeedScalar(SINT8 value);
INT32 K_GetShieldFromItem(INT32 item); INT32 K_GetShieldFromItem(INT32 item);

View file

@ -412,6 +412,8 @@ static int player_get(lua_State *L)
lua_pushinteger(L, plr->lastsafecheatcheck); lua_pushinteger(L, plr->lastsafecheatcheck);
else if (fastcmp(field,"ignoreairtimeleniency")) else if (fastcmp(field,"ignoreairtimeleniency"))
lua_pushinteger(L, plr->ignoreAirtimeLeniency); lua_pushinteger(L, plr->ignoreAirtimeLeniency);
else if (fastcmp(field,"bubbledrag"))
lua_pushinteger(L, plr->bubbledrag);
else if (fastcmp(field,"topaccel")) else if (fastcmp(field,"topaccel"))
lua_pushinteger(L, plr->topAccel); lua_pushinteger(L, plr->topAccel);
else if (fastcmp(field,"vortexboost")) else if (fastcmp(field,"vortexboost"))
@ -1042,6 +1044,8 @@ static int player_set(lua_State *L)
plr->lastsafecheatcheck = luaL_checkinteger(L, 3); plr->lastsafecheatcheck = luaL_checkinteger(L, 3);
else if (fastcmp(field,"ignoreairtimeleniency")) else if (fastcmp(field,"ignoreairtimeleniency"))
plr->ignoreAirtimeLeniency = luaL_checkinteger(L, 3); plr->ignoreAirtimeLeniency = luaL_checkinteger(L, 3);
else if (fastcmp(field,"bubbledrag"))
plr->bubbledrag = luaL_checkinteger(L, 3);
else if (fastcmp(field,"topaccel")) else if (fastcmp(field,"topaccel"))
plr->topAccel = luaL_checkinteger(L, 3); plr->topAccel = luaL_checkinteger(L, 3);
else if (fastcmp(field,"vortexboost")) else if (fastcmp(field,"vortexboost"))

View file

@ -15,6 +15,7 @@
#include "../m_easing.h" #include "../m_easing.h"
#include "../m_fixed.h" #include "../m_fixed.h"
#include "../tables.h" #include "../tables.h"
#include "../k_hud.h" // transflag
using namespace srb2::objects; using namespace srb2::objects;
@ -89,6 +90,7 @@ struct Visual : Mobj
if (sprite != SPR_BUBB && if (sprite != SPR_BUBB &&
sprite != SPR_BUBC && sprite != SPR_BUBC &&
sprite != SPR_BUBG &&
bubble()->player()->bubblecool && bubble()->player()->bubblecool &&
f == 0) // base size f == 0) // base size
renderflags |= RF_DONTDRAW; renderflags |= RF_DONTDRAW;
@ -113,6 +115,19 @@ struct Visual : Mobj
spritescale({ FRACUNIT, FRACUNIT }); spritescale({ FRACUNIT, FRACUNIT });
} }
if (sprite == SPR_BUBG)
{
renderflags &= ~(RF_TRANSMASK|RF_DONTDRAW);
renderflags |= RF_ADD;
fixed_t transpercent = K_PlayerScamPercentage(bubble()->follow()->player, BUBBLESCAM);
UINT8 transfactor = (transpercent * NUMTRANSMAPS) / FRACUNIT;
if (transfactor < 10)
renderflags |= ((10-transfactor) << RF_TRANSSHIFT);
// CONS_Printf("tp %d rf %d\n", transpercent, renderflags);
}
prev_scale(scale()); prev_scale(scale());
return true; return true;
@ -123,11 +138,12 @@ struct Visual : Mobj
void Obj_SpawnBubbleShieldVisuals(mobj_t *bubble) void Obj_SpawnBubbleShieldVisuals(mobj_t *bubble)
{ {
Visual::spawn(static_cast<Bubble*>(bubble), S_BUBA1, 1, 2); Visual::spawn(static_cast<Bubble*>(bubble), S_BUBA1, 1, 3); //Top shine/outline
Visual::spawn(static_cast<Bubble*>(bubble), S_BUBB1, 0, 1); Visual::spawn(static_cast<Bubble*>(bubble), S_BUBB1, 0, 2); //Top wave
Visual::spawn(static_cast<Bubble*>(bubble), S_BUBC1, 1, -1); Visual::spawn(static_cast<Bubble*>(bubble), S_BUBG1, 0, 1); //Fog mechanic
Visual::spawn(static_cast<Bubble*>(bubble), S_BUBD1, 0, -2); Visual::spawn(static_cast<Bubble*>(bubble), S_BUBC1, 1, -1); //Back Wave
Visual::spawn(static_cast<Bubble*>(bubble), S_BUBE1, 1, -3); Visual::spawn(static_cast<Bubble*>(bubble), S_BUBD1, 0, -2); //Bottom Reflection
Visual::spawn(static_cast<Bubble*>(bubble), S_BUBE1, 1, -3); //Backlit outline
} }
boolean Obj_TickBubbleShieldVisual(mobj_t *mobj) boolean Obj_TickBubbleShieldVisual(mobj_t *mobj)

View file

@ -641,6 +641,7 @@ static void P_NetArchivePlayers(savebuffer_t *save)
WRITEUINT8(save->p, players[i].lastsafecheatcheck); WRITEUINT8(save->p, players[i].lastsafecheatcheck);
WRITEUINT8(save->p, players[i].ignoreAirtimeLeniency); WRITEUINT8(save->p, players[i].ignoreAirtimeLeniency);
WRITEUINT8(save->p, players[i].bubbledrag);
WRITEFIXED(save->p, players[i].topAccel); WRITEFIXED(save->p, players[i].topAccel);
WRITEFIXED(save->p, players[i].vortexBoost); WRITEFIXED(save->p, players[i].vortexBoost);
@ -1292,6 +1293,7 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
players[i].lastsafecheatcheck = READUINT8(save->p); players[i].lastsafecheatcheck = READUINT8(save->p);
players[i].ignoreAirtimeLeniency = READUINT8(save->p); players[i].ignoreAirtimeLeniency = READUINT8(save->p);
players[i].bubbledrag = READUINT8(save->p);
players[i].topAccel = READFIXED(save->p); players[i].topAccel = READFIXED(save->p);
players[i].vortexBoost = READFIXED(save->p); players[i].vortexBoost = READFIXED(save->p);

View file

@ -428,6 +428,7 @@ void P_ResetPlayer(player_t *player)
player->glanceDir = 0; player->glanceDir = 0;
player->fastfall = 0; player->fastfall = 0;
player->turbine = 0; player->turbine = 0;
player->bubbledrag = false;
Obj_EndBungee(player); Obj_EndBungee(player);
if (player->mo != NULL && P_MobjWasRemoved(player->mo) == false) if (player->mo != NULL && P_MobjWasRemoved(player->mo) == false)