Merge branch 'gp-tweaks-420' into 'master'

GP refinements

Closes #1527

See merge request kart-krew-dev/ring-racers-internal!2620
This commit is contained in:
AJ Martinez 2025-06-19 06:44:28 +00:00
commit 9f506f93dd
14 changed files with 199 additions and 71 deletions

View file

@ -410,7 +410,7 @@ struct botvars_t
botStyle_e style; // Training mode-style CPU mode
UINT8 difficulty; // Bot's difficulty setting
UINT8 diffincrease; // In GP: bot difficulty will increase this much next round
INT16 diffincrease; // In GP: bot difficulty will increase this much next round
boolean rival; // If true, they're the GP rival
// All entries above persist between rounds and must be recorded in demos

View file

@ -745,9 +745,11 @@ extern int
#define MAXAMPSCALINGDIST 18000
// Exp
#define EXP_STABLERATE 3*FRACUNIT/10 // how low is your placement before losing XP? 4*FRACUNIT/10 = top 40% of race will gain
#define EXP_POWER 3*FRACUNIT/100 // adjust to change overall xp volatility
#define MINEXP 25 // The min value target
#define TARGETEXP 100 // The target value needed for A rank
#define MAXEXP 125 // The max value displayed by the hud and in the tally screen and GP results screen
#define TARGETEXP 120 // Used for grading ...
#define MAXEXP 120 // The max value displayed by the hud and in the tally screen and GP results screen
#ifdef __cplusplus
} // extern "C"

View file

@ -319,7 +319,7 @@ void G_ReadDemoExtraData(void)
if (players[p].bot)
{
players[p].botvars.difficulty = READUINT8(demobuf.p);
players[p].botvars.diffincrease = READUINT8(demobuf.p); // needed to avoid having to duplicate logic
players[p].botvars.diffincrease = READINT16(demobuf.p); // needed to avoid having to duplicate logic
players[p].botvars.rival = (boolean)READUINT8(demobuf.p);
}
}
@ -495,7 +495,7 @@ void G_WriteDemoExtraData(void)
if (players[i].bot)
{
WRITEUINT8(demobuf.p, players[i].botvars.difficulty);
WRITEUINT8(demobuf.p, players[i].botvars.diffincrease); // needed to avoid having to duplicate logic
WRITEINT16(demobuf.p, players[i].botvars.diffincrease); // needed to avoid having to duplicate logic
WRITEUINT8(demobuf.p, (UINT8)players[i].botvars.rival);
}
}
@ -2109,7 +2109,7 @@ void G_BeginRecording(void)
if (i & DEMO_BOT)
{
WRITEUINT8(demobuf.p, player->botvars.difficulty);
WRITEUINT8(demobuf.p, player->botvars.diffincrease); // needed to avoid having to duplicate logic
WRITEINT16(demobuf.p, player->botvars.diffincrease); // needed to avoid having to duplicate logic
WRITEUINT8(demobuf.p, (UINT8)player->botvars.rival);
}
@ -3220,7 +3220,7 @@ void G_DoPlayDemoEx(const char *defdemoname, lumpnum_t deflumpnum)
if ((players[p].bot = bot) == true)
{
players[p].botvars.difficulty = READUINT8(demobuf.p);
players[p].botvars.diffincrease = READUINT8(demobuf.p); // needed to avoid having to duplicate logic
players[p].botvars.diffincrease = READINT16(demobuf.p); // needed to avoid having to duplicate logic
players[p].botvars.rival = (boolean)READUINT8(demobuf.p);
}

View file

@ -1908,17 +1908,22 @@ void G_Ticker(boolean run)
&& grandprixinfo.gp == true
&& grandprixinfo.masterbots == false)
{
UINT8 bot_level_decrease = 3;
UINT8 bot_level_decrease = 2;
UINT8 min_lvl = 5;
if (grandprixinfo.gamespeed == KARTSPEED_EASY)
{
bot_level_decrease++;
min_lvl = 1;
}
else if (grandprixinfo.gamespeed == KARTSPEED_HARD)
{
bot_level_decrease--;
min_lvl = 9;
}
boolean already_min_lvl = (players[i].botvars.difficulty >= min_lvl);
if (players[i].botvars.difficulty <= bot_level_decrease)
{
players[i].botvars.difficulty = 1;
@ -1927,6 +1932,9 @@ void G_Ticker(boolean run)
{
players[i].botvars.difficulty -= bot_level_decrease;
}
if (already_min_lvl)
players[i].botvars.difficulty = max(players[i].botvars.difficulty, min_lvl);
}
else
{
@ -2260,7 +2268,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
INT16 steering;
angle_t playerangleturn;
UINT8 botdiffincrease;
INT16 botdiffincrease;
boolean botrival;
boolean cangrabitems;
@ -5855,6 +5863,7 @@ void G_SetRetryFlag(void)
if (retrying == false && grandprixinfo.gp)
{
grandprixinfo.rank.continuesUsed++;
grandprixinfo.rank.levels[grandprixinfo.rank.numLevels].continues++;
}
retrying = true;

View file

@ -585,10 +585,12 @@ const botcontroller_t *K_GetBotController(const mobj_t *mobj)
--------------------------------------------------*/
fixed_t K_BotMapModifier(void)
{
// fuck it we ball
// return 10*FRACUNIT/10;
constexpr INT32 complexity_scale = 10000;
fixed_t modifier_max = FRACUNIT * 2;
fixed_t modifier_min = 3 * FRACUNIT / 10;
modifier_min -= FRACUNIT;
fixed_t modifier_max = (10 * FRACUNIT / 10) - FRACUNIT;
fixed_t modifier_min = (5 * FRACUNIT / 10) - FRACUNIT;
const fixed_t complexity_value = std::clamp<fixed_t>(
FixedDiv(K_GetTrackComplexity(), complexity_scale),
@ -691,8 +693,7 @@ fixed_t K_BotRubberband(const player_t *player)
if (player->gradingfactor < FRACUNIT && !(player->botvars.rival))
{
UINT8 levelreduce = 3; // How much to drop the "effective level" of bots that are consistently behind
fixed_t effgradingfactor = std::max(FRACUNIT/2, player->gradingfactor);
expreduce = Easing_Linear((effgradingfactor - FRACUNIT/2) * 2, levelreduce*FRACUNIT, 0);
expreduce = Easing_Linear((K_EffectiveGradingFactor(player) - MINGRADINGFACTOR) * 2, levelreduce*FRACUNIT, 0);
}
fixed_t difficultyEase = (((player->botvars.difficulty - 1) * FRACUNIT) - expreduce) / (MAXBOTDIFFICULTY - 1);

View file

@ -90,6 +90,14 @@ UINT8 K_GetGPPlayerCount(UINT8 humans)
return std::clamp<UINT8>(humans * 4, 8, MAXPLAYERS);
}
// Kind of hate unsigned types
static UINT8 K_GetOffsetStartingDifficulty(const UINT8 startingdifficulty, UINT8 offset)
{
if (offset >= startingdifficulty)
return 1;
return startingdifficulty - offset;
}
/*--------------------------------------------------
void K_InitGrandPrixBots(void)
@ -139,22 +147,22 @@ void K_InitGrandPrixBots(void)
else
{
// init difficulty levels list
difficultylevels[ 0] = std::max<UINT8>(1, startingdifficulty);
difficultylevels[ 1] = std::max<UINT8>(1, startingdifficulty-1);
difficultylevels[ 2] = std::max<UINT8>(1, startingdifficulty-2);
difficultylevels[ 3] = std::max<UINT8>(1, startingdifficulty-3);
difficultylevels[ 4] = std::max<UINT8>(1, startingdifficulty-3);
difficultylevels[ 5] = std::max<UINT8>(1, startingdifficulty-4);
difficultylevels[ 6] = std::max<UINT8>(1, startingdifficulty-4);
difficultylevels[ 7] = std::max<UINT8>(1, startingdifficulty-4);
difficultylevels[ 8] = std::max<UINT8>(1, startingdifficulty-5);
difficultylevels[ 9] = std::max<UINT8>(1, startingdifficulty-5);
difficultylevels[10] = std::max<UINT8>(1, startingdifficulty-5);
difficultylevels[11] = std::max<UINT8>(1, startingdifficulty-6);
difficultylevels[12] = std::max<UINT8>(1, startingdifficulty-6);
difficultylevels[13] = std::max<UINT8>(1, startingdifficulty-7);
difficultylevels[14] = std::max<UINT8>(1, startingdifficulty-7);
difficultylevels[15] = std::max<UINT8>(1, startingdifficulty-8);
difficultylevels[ 0] = startingdifficulty;
difficultylevels[ 1] = K_GetOffsetStartingDifficulty(startingdifficulty, 1);
difficultylevels[ 2] = K_GetOffsetStartingDifficulty(startingdifficulty, 2);
difficultylevels[ 3] = K_GetOffsetStartingDifficulty(startingdifficulty, 3);
difficultylevels[ 4] = K_GetOffsetStartingDifficulty(startingdifficulty, 3);
difficultylevels[ 5] = K_GetOffsetStartingDifficulty(startingdifficulty, 4);
difficultylevels[ 6] = K_GetOffsetStartingDifficulty(startingdifficulty, 4);
difficultylevels[ 7] = K_GetOffsetStartingDifficulty(startingdifficulty, 4);
difficultylevels[ 8] = K_GetOffsetStartingDifficulty(startingdifficulty, 5);
difficultylevels[ 9] = K_GetOffsetStartingDifficulty(startingdifficulty, 5);
difficultylevels[10] = K_GetOffsetStartingDifficulty(startingdifficulty, 5);
difficultylevels[11] = K_GetOffsetStartingDifficulty(startingdifficulty, 6);
difficultylevels[12] = K_GetOffsetStartingDifficulty(startingdifficulty, 6);
difficultylevels[13] = K_GetOffsetStartingDifficulty(startingdifficulty, 7);
difficultylevels[14] = K_GetOffsetStartingDifficulty(startingdifficulty, 7);
difficultylevels[15] = K_GetOffsetStartingDifficulty(startingdifficulty, 8);
}
for (i = 0; i < MAXPLAYERS; i++)
@ -381,7 +389,10 @@ void K_UpdateGrandPrixBots(void)
if (players[i].botvars.diffincrease)
{
players[i].botvars.difficulty += players[i].botvars.diffincrease;
if (players[i].botvars.diffincrease < 0)
players[i].botvars.difficulty = std::max(1, players[i].botvars.difficulty - players[i].botvars.diffincrease);
else
players[i].botvars.difficulty += players[i].botvars.diffincrease;
if (players[i].botvars.difficulty > MAXBOTDIFFICULTY)
{
@ -611,12 +622,10 @@ void K_IncreaseBotDifficulty(player_t *bot)
switch(averageRank)
{
case GRADE_E:
rankNudge = -2;
break;
case GRADE_D:
case GRADE_C:
rankNudge = -1;
break;
case GRADE_C:
case GRADE_B:
rankNudge = 0;
break;

View file

@ -4046,8 +4046,8 @@ static boolean K_drawKartLaps(void)
// WHAT IS THIS?
// WHAT ARE YOU FUCKING TALKING ABOUT?
V_DrawMappedPatch(fr, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_exp[1], R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_MUSTARD, GTC_CACHE));
auto transflag = K_GetTransFlagFromFixed(stplyr->gradingfactor);
skincolornum_t overlaycolor = stplyr->gradingfactor < FRACUNIT ? SKINCOLOR_RUBY : SKINCOLOR_ULTRAMARINE ;
auto transflag = K_GetTransFlagFromFixed(K_EffectiveGradingFactor(stplyr));
skincolornum_t overlaycolor = K_EffectiveGradingFactor(stplyr) < FRACUNIT ? SKINCOLOR_RUBY : SKINCOLOR_ULTRAMARINE ;
auto colormap = R_GetTranslationColormap(TC_RAINBOW, overlaycolor, GTC_CACHE);
V_DrawMappedPatch(fr, fy, transflag|V_SLIDEIN|splitflags, kp_exp[1], colormap);
@ -4069,8 +4069,8 @@ static boolean K_drawKartLaps(void)
V_DrawMappedPatch(LAPS_X+bump, LAPS_Y, V_HUDTRANS|V_SLIDEIN|splitflags, kp_exp[0], R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_MUSTARD, GTC_CACHE));
auto transflag = K_GetTransFlagFromFixed(stplyr->gradingfactor);
skincolornum_t overlaycolor = stplyr->gradingfactor < FRACUNIT ? SKINCOLOR_RUBY : SKINCOLOR_ULTRAMARINE ;
auto transflag = K_GetTransFlagFromFixed(K_EffectiveGradingFactor(stplyr));
skincolornum_t overlaycolor = K_EffectiveGradingFactor(stplyr) < FRACUNIT ? SKINCOLOR_RUBY : SKINCOLOR_ULTRAMARINE ;
auto colormap = R_GetTranslationColormap(TC_RAINBOW, overlaycolor, GTC_CACHE);
V_DrawMappedPatch(LAPS_X+bump, LAPS_Y, transflag|V_SLIDEIN|splitflags, kp_exp[0], colormap);

View file

@ -130,6 +130,13 @@ boolean K_InRaceDuel(void)
);
}
fixed_t K_EffectiveGradingFactor(const player_t *player)
{
if (grandprixinfo.gp && grandprixinfo.masterbots && !K_PlayerUsesBotMovement(player))
return MINGRADINGFACTOR;
return max(MINGRADINGFACTOR, player->gradingfactor);
}
player_t *K_DuelOpponent(player_t *player)
{
if (!K_InRaceDuel())
@ -749,6 +756,14 @@ static fixed_t K_PlayerWeight(mobj_t *mobj, mobj_t *against)
fixed_t spd = K_GetKartSpeed(mobj->player, false, true);
fixed_t unmodifiedspd = K_GetKartSpeed(mobj->player, false, false);
fixed_t bumpfactor = FRACUNIT;
if (K_PlayerUsesBotMovement(mobj->player))
{
// Bot bumps are just a hard problem: lots going on.
// Treat bots as moving slower than they really are.
bumpfactor = max(bumpfactor, FixedDiv(spd, unmodifiedspd) * 2);
}
fixed_t speedfactor = 8 * mapobjectscale;
weight = (mobj->player->kartweight) * FRACUNIT;
@ -766,7 +781,7 @@ static fixed_t K_PlayerWeight(mobj_t *mobj, mobj_t *against)
if (mobj->player->speed > spd)
weight += FixedDiv(
FixedDiv((mobj->player->speed - spd), speedfactor),
FixedDiv(spd, unmodifiedspd)
bumpfactor
);
}
@ -3897,9 +3912,9 @@ fixed_t K_GetKartSpeed(const player_t *player, boolean doboostpower, boolean dor
if (K_PlayerUsesBotMovement(player))
{
// Increase bot speed by 0-10% depending on difficulty
// Increase bot speed by 0-20% depending on difficulty
const fixed_t modifier = K_BotMapModifier();
fixed_t add = ((player->botvars.difficulty-1) * FixedMul(FRACUNIT / 10, modifier)) / (DIFFICULTBOT-1);
fixed_t add = ((player->botvars.difficulty-1) * FixedMul(FRACUNIT / 5, modifier)) / (DIFFICULTBOT-1);
finalspeed = FixedMul(finalspeed, FRACUNIT + add);
if (player->bot && (player->botvars.rival || cv_levelskull.value))
@ -4103,10 +4118,17 @@ fixed_t K_GetNewSpeed(const player_t *player)
p_accel = FixedDiv(p_accel, player->botvars.rubberband);
}
// WEIRD! Adjust speed cap when base friction is grippy (bots only), to make sure
// they don't drive really, really fast when we try to give them extra grip.
fixed_t frictiondelta = FRACUNIT + K_PlayerBaseFriction(player, ORIG_FRICTION) - ORIG_FRICTION;
fixed_t p_speed_cap = p_speed;
if (frictiondelta < FRACUNIT)
p_speed_cap = FixedMul(frictiondelta, p_speed);
oldspeed = R_PointToDist2(0, 0, player->rmomx, player->rmomy);
// Don't calculate the acceleration as ever being above top speed
if (oldspeed > p_speed)
oldspeed = p_speed;
if (oldspeed > p_speed_cap)
oldspeed = p_speed_cap;
newspeed = FixedDiv(FixedDiv(FixedMul(oldspeed, accelmax - p_accel) + FixedMul(p_speed, p_accel), accelmax), K_PlayerBaseFriction(player, ORIG_FRICTION));
finalspeed = newspeed - oldspeed;
@ -13318,7 +13340,17 @@ fixed_t K_PlayerBaseFriction(const player_t *player, fixed_t original)
// If bots are moving in the wrong direction relative to where they want to look, add some extra grip.
angle_t MAXERROR = ANGLE_45;
fixed_t errorfrict = Easing_Linear(min(FRACUNIT, FixedDiv(player->botvars.predictionError, MAXERROR)), 0, FRACUNIT>>2);
angle_t MINERROR = 0;
angle_t BLINDSPOT = ANGLE_135;
fixed_t MAXERRORFRICTION = FixedMul(FRACUNIT >> 3, factor);
fixed_t errorfrict = Easing_InCubic(min(FRACUNIT, FixedDiv(player->botvars.predictionError, MAXERROR)), 0, MAXERRORFRICTION);
const botcontroller_t *botController = K_GetBotController(player->mo);
if (botController != NULL && (botController->flags & TMBOT_NORUBBERBAND) == TMBOT_NORUBBERBAND)
MAXERRORFRICTION = 0; // Don't grip to setpieces...
if (player->botvars.predictionError > BLINDSPOT)
MAXERRORFRICTION = 0; // ...or "tar pit" narrow waypoints.
if (player->currentwaypoint && player->currentwaypoint->mobj)
{
@ -13329,11 +13361,23 @@ fixed_t K_PlayerBaseFriction(const player_t *player, fixed_t original)
errorfrict *= 2;
}
errorfrict = min(errorfrict, frict/4);
frict -= errorfrict;
/*
if (player->mo && !P_MobjWasRemoved(player->mo) && player->mo->movefactor < FRACUNIT)
{
// Reduce error friction on low-friction surfaces
errorfrict = FixedMul(errorfrict, player->mo->movefactor);
}
*/
if (player->botvars.predictionError >= MINERROR)
{
// CONS_Printf("%d: friction was %d, is ", leveltime, frict);
frict -= min(errorfrict, MAXERRORFRICTION);
// CONS_Printf("%d\n", frict);
}
// Bots gain more traction as they rubberband.
const fixed_t traction_value = FixedMul(player->botvars.rubberband, max(FRACUNIT, K_BotMapModifier()));
const fixed_t traction_value = FixedMul(player->botvars.rubberband, K_BotMapModifier());
if (traction_value > FRACUNIT)
{
const fixed_t traction_mul = traction_value - FRACUNIT;
@ -13413,6 +13457,11 @@ void K_AdjustPlayerFriction(player_t *player)
player->mo->friction = FRACUNIT;
}
/*
if (player->cmd.buttons & BT_ATTACK)
player->mo->friction -= FRACUNIT/2;
*/
// Cap between intended values
if (player->mo->friction > FRACUNIT)
player->mo->friction = FRACUNIT;
@ -13647,7 +13696,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
else
{
UINT32 behind = K_GetItemRouletteDistance(player, player->itemRoulette.playing);
behind = FixedMul(behind, max(player->gradingfactor, FRACUNIT/2));
behind = FixedMul(behind, K_EffectiveGradingFactor(player));
UINT32 behindMulti = behind / 500;
behindMulti = min(behindMulti, 60);
award = award * (behindMulti + 10) / 10;
@ -15850,8 +15899,8 @@ boolean K_PlayerCanUseItem(player_t *player)
fixed_t K_GetGradingFactorAdjustment(player_t *player)
{
fixed_t power = 3*FRACUNIT/100; // adjust to change overall xp volatility
fixed_t stablerate = 3*FRACUNIT/10; // how low is your placement before losing XP? 4*FRACUNIT/10 = top 40% of race will gain
fixed_t power = EXP_POWER; // adjust to change overall xp volatility
const fixed_t stablerate = EXP_STABLERATE; // how low is your placement before losing XP? 4*FRACUNIT/10 = top 40% of race will gain
fixed_t result = 0;
if (g_teamplay)
@ -15968,7 +16017,8 @@ void K_BotHitPenalty(player_t *player)
{
if (K_PlayerUsesBotMovement(player))
{
player->botvars.rubberband = max(player->botvars.rubberband/2, FRACUNIT/2);
if (!player->botvars.bumpslow)
player->botvars.rubberband = max(3*player->botvars.rubberband/4, FRACUNIT/2);
player->botvars.bumpslow = TICRATE*2;
}
}

View file

@ -114,6 +114,9 @@ boolean K_DuelItemAlwaysSpawns(mapthing_t *mt);
boolean K_InRaceDuel(void);
player_t *K_DuelOpponent(player_t *player);
fixed_t K_EffectiveGradingFactor(const player_t *player);
#define MINGRADINGFACTOR (FRACUNIT/2)
void K_TimerReset(void);
void K_TimerInit(void);

View file

@ -188,6 +188,10 @@ void podiumData_s::Init(void)
lvl->time = M_RandomRange(50*TICRATE, 210*TICRATE);
lvl->continues = 0;
if (!M_RandomRange(0, 2))
lvl->continues = M_RandomRange(1, 3);
for (INT32 j = 0; j < rank.numPlayers; j++)
{
gpRank_level_perplayer_t *const dta = &lvl->perPlayer[j];
@ -633,12 +637,15 @@ void podiumData_s::Draw(void)
if (lvl->event != GPEVENT_SPECIAL && dta->grade != GRADE_INVALID)
{
drawer_rank
.xy(0, -1)
.colormap( static_cast<skincolornum_t>(K_GetGradeColor(dta->grade)) )
.patch(va("R_CUPRN%c", K_GetGradeChar(dta->grade)));
drawer_rank
.xy(0, -1).flags(lvl->continues ? V_TRANSLUCENT : 0)
.colormap( static_cast<skincolornum_t>(K_GetGradeColor(dta->grade)) )
.patch(va("R_CUPRN%c", K_GetGradeChar(dta->grade)));
}
if (lvl->continues)
drawer_rank.xy(7, 1).align(srb2::Draw::Align::kCenter).font(srb2::Draw::Font::kPing).colorize(SKINCOLOR_RED).text(va("-%d", lvl->continues));
// Do not draw any stats for GAME OVERed player
if (dta->grade != GRADE_INVALID || lvl->event == GPEVENT_SPECIAL)
{
@ -716,10 +723,28 @@ void podiumData_s::Draw(void)
.xy(0, 1)
.colorize(static_cast<skincolornum_t>(SKINCOLOR_MUSTARD))
.patch("K_SPTEXP");
// Colorize the crystal, just like we do for hud
fixed_t factor = FixedDiv(dta->exp*FRACUNIT, lvl->totalExp*FRACUNIT);
skincolornum_t overlaycolor = factor < FRACUNIT ? SKINCOLOR_RUBY : SKINCOLOR_ULTRAMARINE;
if (factor >= FRACUNIT) {factor += factor-FRACUNIT;} // exaggerate the positive side, since reverse engineering the factor like this results in half the translucency range
skincolornum_t overlaycolor = SKINCOLOR_MUSTARD;
fixed_t stablerateinverse = FRACUNIT - EXP_STABLERATE;
INT16 exp_range = MAXEXP-MINEXP;
INT16 exp_offset = dta->exp-MINEXP;
fixed_t factor = (exp_offset*FRACUNIT) / exp_range; // 0.0 to 1.0 in fixed
// amount of blue is how much factor is above EXP_STABLERATE, and amount of red is how much factor is below
// assume that EXP_STABLERATE is within 0.0 to 1.0 in fixed
if (factor <= stablerateinverse)
{
overlaycolor = SKINCOLOR_RUBY;
factor = FixedDiv(factor, stablerateinverse);
}
else
{
overlaycolor = SKINCOLOR_ULTRAMARINE;
fixed_t bluemaxoffset = EXP_STABLERATE;
factor = factor - stablerateinverse;
factor = FRACUNIT - FixedDiv(factor, bluemaxoffset);
}
auto transflag = K_GetTransFlagFromFixed(factor);
drawer_gametype
.xy(0, 1)
@ -865,12 +890,32 @@ void podiumData_s::Draw(void)
drawer_totals_right
.colorize(static_cast<skincolornum_t>(SKINCOLOR_MUSTARD))
.patch("K_STEXP");
// Colorize the crystal for the totals, just like we do for in race hud
fixed_t factor = FixedDiv((rank.exp+(35*rank.numPlayers-1))*FRACUNIT, rank.totalExp*FRACUNIT); // bump the calc a bit, because its probably not possible for every human to get 125 on every race
skincolornum_t overlaycolor = factor < FRACUNIT ? SKINCOLOR_RUBY : SKINCOLOR_ULTRAMARINE;
if (factor >= FRACUNIT) {factor += factor-FRACUNIT;} // exaggerate the positive side, since reverse engineering the factor like this results in half the translucency range
fixed_t extraexpfactor = (MAXEXP*FRACUNIT) / TARGETEXP;
INT16 totalExpMax = FixedMul(rank.totalExp*FRACUNIT, extraexpfactor) / FRACUNIT; // im just going to calculate it from target lol
INT16 totalExpMin = rank.numPlayers*MINEXP;
skincolornum_t overlaycolor = SKINCOLOR_MUSTARD;
fixed_t stablerateinverse = FRACUNIT - EXP_STABLERATE;
INT16 exp_range = totalExpMax-totalExpMin;
INT16 exp_offset = rank.exp-totalExpMin;
fixed_t factor = (exp_offset*FRACUNIT) / exp_range; // 0.0 to 1.0 in fixed
// amount of blue is how much factor is above EXP_STABLERATE, and amount of red is how much factor is below
// assume that EXP_STABLERATE is within 0.0 to 1.0 in fixed
if (factor <= stablerateinverse)
{
overlaycolor = SKINCOLOR_RUBY;
factor = FixedDiv(factor, stablerateinverse);
}
else
{
overlaycolor = SKINCOLOR_ULTRAMARINE;
fixed_t bluemaxoffset = EXP_STABLERATE;
factor = factor - stablerateinverse;
factor = FRACUNIT - FixedDiv(factor, bluemaxoffset);
}
auto transflag = K_GetTransFlagFromFixed(factor);
drawer_totals_right
.colorize(static_cast<skincolornum_t>(overlaycolor))
.flags(transflag)

View file

@ -34,6 +34,7 @@ struct gpRank_level_t
UINT32 time;
UINT16 totalExp;
UINT16 totalPrisons;
UINT16 continues;
gpRank_level_perplayer_t perPlayer[MAXSPLITSCREENPLAYERS];
};
@ -95,8 +96,8 @@ extern "C" {
#define RANK_WEIGHT_PRISONS (100)
#define RANK_WEIGHT_RINGS (50)
#define RANK_CONTINUE_PENALTY_DIV (20) // 5% of the total grade
#define RANK_CONTINUE_PENALTY_START (2)
#define RANK_CONTINUE_PENALTY_DIV (10) // 10% of the total grade
#define RANK_CONTINUE_PENALTY_START (0)
/*--------------------------------------------------
void K_InitGrandPrixRank(gpRank_t *rankData);

View file

@ -1138,7 +1138,7 @@ static boolean K_ShouldPlayerAllowItem(kartitems_t item, const player_t *player)
return false;
// GIGA power items reserved only for players who were doing great and died.
if (player->gradingfactor < K_RequiredXPForItem(item))
if (K_EffectiveGradingFactor(player) < K_RequiredXPForItem(item))
return false;
return !K_IsItemFirstOnly(item);
@ -1405,7 +1405,7 @@ void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulet
if ((gametyperules & GTR_CIRCUIT) && !K_Cooperative())
{
roulette->dist = FixedMul(roulette->preexpdist, max(player->gradingfactor, FRACUNIT/2));
roulette->dist = FixedMul(roulette->preexpdist, K_EffectiveGradingFactor(player));
}
// ===============================================================================

View file

@ -188,7 +188,7 @@ INT32 level_tally_t::CalculateGrade(void)
7*FRACUNIT/20, // D: 35% or higher
10*FRACUNIT/20, // C: 50% or higher
14*FRACUNIT/20, // B: 70% or higher
17*FRACUNIT/20 // A: 85% or higher
18*FRACUNIT/20 // A: 90% or higher
};
INT32 retGrade = GRADE_E; // gp_rank_e
@ -204,14 +204,14 @@ INT32 level_tally_t::CalculateGrade(void)
}
case TALLY_BONUS_SCORE:
{
bonusWeights[i] = ((pointLimit != 0) ? 100 : 0);
bonusWeights[i] = ((pointLimit != 0) ? 200 : 0);
break;
}
case TALLY_BONUS_EXP:
case TALLY_BONUS_PRISON:
case TALLY_BONUS_POWERSTONES:
{
bonusWeights[i] = 150;
bonusWeights[i] = 300;
break;
}
default:
@ -222,7 +222,7 @@ INT32 level_tally_t::CalculateGrade(void)
}
}
const INT32 positionWeight = (position > 0 && numPlayers > 2) ? 20 : 0;
const INT32 positionWeight = (position > 0 && numPlayers > 2) ? 50 : 0;
const INT32 total = positionWeight + bonusWeights[0] + bonusWeights[1];
INT32 ours = 0;
@ -246,7 +246,7 @@ INT32 level_tally_t::CalculateGrade(void)
}
case TALLY_BONUS_EXP:
{
const fixed_t frac = std::min(FRACUNIT, ((exp-15) * FRACUNIT) / std::max(1, static_cast<int>(totalExp)));
const fixed_t frac = std::min(FRACUNIT, ((exp) * FRACUNIT) / std::max(1, static_cast<int>(totalExp)));
ours += Easing_Linear(frac, 0, bonusWeights[i]);
break;
}

View file

@ -6422,6 +6422,7 @@ static inline void P_ArchiveMisc(savebuffer_t *save)
WRITEUINT32(save->p, lvl->time);
WRITEUINT16(save->p, lvl->totalExp);
WRITEUINT16(save->p, lvl->totalPrisons);
WRITEUINT16(save->p, lvl->continues);
UINT8 j;
for (j = 0; j < rank->numPlayers; j++)
@ -6436,6 +6437,9 @@ static inline void P_ArchiveMisc(savebuffer_t *save)
WRITESINT8(save->p, (SINT8)plr->grade);
}
}
const gpRank_level_t *lvl = &rank->levels[rank->numLevels];
WRITEUINT16(save->p, lvl->continues + 1);
}
// Marathon information
@ -6710,6 +6714,7 @@ static boolean P_UnArchiveSPGame(savebuffer_t *save)
lvl->time = READUINT32(save->p);
lvl->totalExp = READUINT16(save->p);
lvl->totalPrisons = READUINT16(save->p);
lvl->continues = READUINT16(save->p);
for (j = 0; j < rank->numPlayers; j++)
{
@ -6723,6 +6728,9 @@ static boolean P_UnArchiveSPGame(savebuffer_t *save)
plr->grade = (gp_rank_e)READSINT8(save->p);
}
}
gpRank_level_t *const lvl = &rank->levels[rank->numLevels];
lvl->continues = READUINT16(save->p);
}
// Marathon information