From 55de982fa380dc74329ee4a72c6682500981141a Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 22 Feb 2023 04:31:15 -0500 Subject: [PATCH] GP ranking Needs balancing + intermission, but mostly functional --- src/d_player.h | 1 + src/g_game.c | 18 ++++++ src/k_battle.c | 1 + src/k_grandprix.c | 160 ++++++++++++++++++++++++++++++++++++++++++++-- src/k_grandprix.h | 54 ++++++++++++++++ src/k_hud.c | 35 ++++++++++ src/p_inter.c | 2 + src/p_saveg.c | 2 + src/p_setup.c | 1 + src/p_spec.c | 13 +++- src/p_user.c | 11 ++++ 11 files changed, 291 insertions(+), 7 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index f38076f6f..40bfcc95a 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -634,6 +634,7 @@ struct player_t tic_t realtime; // integer replacement for leveltime UINT8 laps; // Number of laps (optional) UINT8 latestlap; + UINT32 lapPoints; // Points given from laps INT32 starpostnum; // The number of the last starpost you hit UINT8 ctfteam; // 0 == Spectator, 1 == Red, 2 == Blue diff --git a/src/g_game.c b/src/g_game.c index 5468cf505..601834123 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2388,6 +2388,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) INT16 totalring; UINT8 laps; UINT8 latestlap; + UINT32 lapPoints; UINT16 skincolor; INT32 skin; UINT8 availabilities[MAXAVAILABILITY]; @@ -2497,6 +2498,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) nocontrol = 0; laps = 0; latestlap = 0; + lapPoints = 0; roundscore = 0; exiting = 0; khudfinish = 0; @@ -2532,6 +2534,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) laps = players[player].laps; latestlap = players[player].latestlap; + lapPoints = players[player].lapPoints; roundscore = players[player].roundscore; @@ -2604,6 +2607,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) p->laps = laps; p->latestlap = latestlap; + p->lapPoints = lapPoints; p->totalring = totalring; p->bot = bot; @@ -4123,6 +4127,8 @@ static void G_DoCompleted(void) G_SetGamestate(GS_NULL); wipegamestate = GS_NULL; + UINT8 bestDifficulty = 0; + for (i = 0; i < MAXPLAYERS; i++) { if (playeringame[i]) @@ -4148,9 +4154,16 @@ static void G_DoCompleted(void) } G_PlayerFinishLevel(i); // take away cards and stuff + + if (players[i].bot) + { + bestDifficulty = max(bestDifficulty, players[i].botvars.difficulty); + } } } + gpRank.difficulty = bestDifficulty; + // See Y_StartIntermission timer handling if ((gametyperules & GTR_CIRCUIT) && ((multiplayer && demo.playback) || j == r_splitscreen+1) && (!K_CanChangeRules(false) || cv_inttime.value > 0)) // play some generic music if there's no win/cool/lose music going on (for exitlevel commands) @@ -5485,6 +5498,11 @@ boolean G_GetExitGameFlag(void) // Same deal with retrying. void G_SetRetryFlag(void) { + if (retrying == false) + { + gpRank.continuesUsed++; + } + retrying = true; } diff --git a/src/k_battle.c b/src/k_battle.c index b34c7a151..8e5db972b 100644 --- a/src/k_battle.c +++ b/src/k_battle.c @@ -780,6 +780,7 @@ void K_BattleInit(boolean singleplayercontext) P_SpawnMapThing(mt); } + gpRank.totalCapsules += maptargets; battlecapsules = true; } diff --git a/src/k_grandprix.c b/src/k_grandprix.c index 6d879054b..c061d271f 100644 --- a/src/k_grandprix.c +++ b/src/k_grandprix.c @@ -21,6 +21,7 @@ #include "r_things.h" struct grandprixinfo grandprixinfo; +struct gpRank gpRank; /*-------------------------------------------------- UINT8 K_BotStartingDifficulty(SINT8 value) @@ -114,6 +115,86 @@ UINT8 K_BotDefaultSkin(void) return (UINT8)defaultbotskin; } +/*-------------------------------------------------- + static UINT8 K_GetGPPlayerCount(UINT8 humans) + + Counts the number of total players, + including humans and bots, to put into + a GP session. +--------------------------------------------------*/ +static UINT8 K_GetGPPlayerCount(UINT8 humans) +{ + UINT8 playerCount = 8; + + if (humans > 2) + { + // Add 3 bots per player beyond 2P + playerCount += (humans - 2) * 3; + } + + return playerCount; +} + +/*-------------------------------------------------- + void K_InitGrandPrixRank(void) + + See header file for description. +--------------------------------------------------*/ +void K_InitGrandPrixRank(void) +{ + UINT8 numHumans = 0; + INT32 i; + + memset(&gpRank, 0, sizeof(gpRank)); + + if (grandprixinfo.cup == NULL) + { + return; + } + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i]) + { + if (numHumans < MAXSPLITSCREENPLAYERS && players[i].spectator == false) + { + numHumans++; + } + } + } + + // Calculate players + gpRank.players = numHumans; + gpRank.totalPlayers = K_GetGPPlayerCount(numHumans); + + // Calculate total of points + // (Should this account for all coop players?) + for (i = 0; i < numHumans; i++) + { + gpRank.totalPoints += grandprixinfo.cup->numlevels * K_CalculateGPRankPoints(i + 1, gpRank.totalPlayers); + } + + gpRank.totalRings = grandprixinfo.cup->numlevels * numHumans * 20; + + UINT32 laps = 0; + for (i = 0; i < grandprixinfo.cup->numlevels; i++) + { + const INT32 cupLevelNum = grandprixinfo.cup->cachedlevels[i]; + if (cupLevelNum < nummapheaders && mapheaderinfo[cupLevelNum] != NULL) + { + laps += mapheaderinfo[cupLevelNum]->numlaps; + } + } + + // +1, since 1st place laps are worth 2 pts. + for (i = 0; i < numHumans+1; i++) + { + gpRank.totalLaps += laps; + } + + // Total capsules will need to be calculated as you enter the bonus stages... +} + /*-------------------------------------------------- void K_InitGrandPrixBots(void) @@ -198,12 +279,7 @@ void K_InitGrandPrixBots(void) } } - if (numplayers > 2) - { - // Add 3 bots per player beyond 2P - playercount += (numplayers-2) * 3; - } - + playercount = K_GetGPPlayerCount(numplayers); wantedbots = playercount - numplayers; // Create rival list @@ -728,3 +804,75 @@ boolean K_CanChangeRules(boolean allowdemos) return true; } + +/*-------------------------------------------------- + gp_rank_e K_CalculateGPGrade(void) + + See header file for description. +--------------------------------------------------*/ +gp_rank_e K_CalculateGPGrade(void) +{ + static const fixed_t gradePercents[GRADE_A] = { + 9*FRACUNIT/20, // GRADE_E -> GRADE_D + 12*FRACUNIT/20, // GRADE_D -> GRADE_C + 15*FRACUNIT/20, // GRADE_C -> GRADE_B + 18*FRACUNIT/20 // GRADE_B -> GRADE_A + }; + + gp_rank_e retGrade = GRADE_E; + + // TODO: + const INT32 pointsWeight = 100; + const INT32 lapsWeight = 100; + const INT32 capsulesWeight = 100; + const INT32 ringsWeight = 50; + const INT32 difficultyWeight = 20; + const INT32 total = pointsWeight + lapsWeight + capsulesWeight + ringsWeight + difficultyWeight; + const INT32 continuesPenalty = 20; + + INT32 ours = 0; + fixed_t percent = 0; + + if (gpRank.totalPoints > 0) + { + ours += (gpRank.winPoints * pointsWeight) / gpRank.totalPoints; + } + + if (gpRank.totalLaps > 0) + { + ours += (gpRank.laps * lapsWeight) / gpRank.totalLaps; + } + + if (gpRank.totalCapsules > 0) + { + ours += (gpRank.capsules * capsulesWeight) / gpRank.totalCapsules; + } + + if (gpRank.totalRings > 0) + { + ours += (gpRank.rings * ringsWeight) / gpRank.totalRings; + } + + ours += (gpRank.difficulty * difficultyWeight) / MAXBOTDIFFICULTY; + + ours -= gpRank.continuesUsed * continuesPenalty; + + percent = FixedDiv(ours, total); + + for (retGrade = 0; retGrade < GRADE_A; retGrade++) + { + if (percent < gradePercents[retGrade]) + { + break; + } + } + + if (gpRank.specialWon == true) + { + // Winning the Special Stage gives you + // a free grade increase. + retGrade++; + } + + return retGrade; +} diff --git a/src/k_grandprix.h b/src/k_grandprix.h index a862245c7..9103b047f 100644 --- a/src/k_grandprix.h +++ b/src/k_grandprix.h @@ -37,6 +37,38 @@ extern struct grandprixinfo UINT8 eventmode; ///< See GPEVENT_ constants } grandprixinfo; +extern struct gpRank +{ + UINT8 players; + UINT8 totalPlayers; + + UINT32 winPoints; + UINT32 totalPoints; + + UINT32 laps; + UINT32 totalLaps; + + UINT32 continuesUsed; + + UINT32 capsules; + UINT32 totalCapsules; + + UINT32 rings; + UINT32 totalRings; + + boolean specialWon; + UINT8 difficulty; +} gpRank; + +typedef enum +{ + GRADE_E, + GRADE_D, + GRADE_C, + GRADE_B, + GRADE_A, + GRADE_S +} gp_rank_e; /*-------------------------------------------------- UINT8 K_BotStartingDifficulty(SINT8 value); @@ -80,6 +112,13 @@ INT16 K_CalculateGPRankPoints(UINT8 position, UINT8 numplayers); UINT8 K_BotDefaultSkin(void); +/*-------------------------------------------------- + void K_InitGrandPrixRank(void); + + Calculates rank requirements for a GP session. +--------------------------------------------------*/ + +void K_InitGrandPrixRank(void); /*-------------------------------------------------- void K_InitGrandPrixBots(void); @@ -170,6 +209,21 @@ void K_PlayerLoseLife(player_t *player); boolean K_CanChangeRules(boolean allowdemos); +/*-------------------------------------------------- + gp_rank_e K_CalculateGPGrade(void); + + Calculates the player's grade using the + variables from gpRank. + + Input Arguments:- + N/A + + Return:- + gp_rank_e representing the total grade. +--------------------------------------------------*/ + +gp_rank_e K_CalculateGPGrade(void); + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/k_hud.c b/src/k_hud.c index bbc008520..947032b41 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -5067,4 +5067,39 @@ void K_drawKartHUD(void) K_DrawWaypointDebugger(); K_DrawDirectorDebugger(); + + if (grandprixinfo.gp == true) + { + gp_rank_e grade = K_CalculateGPGrade(); + char gradeChar = '?'; + + V_DrawThinString(0, 0, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, + va("PTS: %d / %d", gpRank.winPoints, gpRank.totalPoints)); + V_DrawThinString(0, 10, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, + va("LAPS: %d / %d", gpRank.laps, gpRank.totalLaps)); + V_DrawThinString(0, 20, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, + va("CONTINUES: %d", gpRank.continuesUsed)); + V_DrawThinString(0, 30, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, + va("CAPSULES: %d / %d", gpRank.capsules, gpRank.totalCapsules)); + V_DrawThinString(0, 40, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, + va("RINGS: %d / %d", gpRank.rings, gpRank.totalRings)); + V_DrawThinString(0, 50, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, + va("SPECIAL: %d", gpRank.specialWon)); + V_DrawThinString(0, 60, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, + va("DIFFICULTY: %d", gpRank.difficulty)); + + switch (grade) + { + case GRADE_E: { gradeChar = 'E'; break; } + case GRADE_D: { gradeChar = 'D'; break; } + case GRADE_C: { gradeChar = 'C'; break; } + case GRADE_B: { gradeChar = 'B'; break; } + case GRADE_A: { gradeChar = 'A'; break; } + case GRADE_S: { gradeChar = 'S'; break; } + default: { break; } + } + + V_DrawThinString(0, 80, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE|V_YELLOWMAP, + va(" ** FINAL GRADE: %c", gradeChar)); + } } diff --git a/src/p_inter.c b/src/p_inter.c index 3d69eec25..be0de4620 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1595,6 +1595,8 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget K_SpawnBattlePoints(source->player, NULL, 1); } + gpRank.capsules++; + // All targets busted! if (++numtargets >= maptargets) { diff --git a/src/p_saveg.c b/src/p_saveg.c index 4d252c64b..8e6a4dd15 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -178,6 +178,7 @@ static void P_NetArchivePlayers(savebuffer_t *save) WRITEUINT32(save->p, players[i].realtime); WRITEUINT8(save->p, players[i].laps); WRITEUINT8(save->p, players[i].latestlap); + WRITEUINT32(save->p, players[i].lapPoints); WRITEINT32(save->p, players[i].starpostnum); WRITEUINT8(save->p, players[i].ctfteam); @@ -567,6 +568,7 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) players[i].realtime = READUINT32(save->p); // integer replacement for leveltime players[i].laps = READUINT8(save->p); // Number of laps (optional) players[i].latestlap = READUINT8(save->p); + players[i].lapPoints = READUINT32(save->p); players[i].starpostnum = READINT32(save->p); players[i].ctfteam = READUINT8(save->p); // 1 == Red, 2 == Blue diff --git a/src/p_setup.c b/src/p_setup.c index 77261176e..e8043d56e 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -7388,6 +7388,7 @@ static void P_InitGametype(void) { if (grandprixinfo.initalize == true) { + K_InitGrandPrixRank(); K_InitGrandPrixBots(); grandprixinfo.initalize = false; } diff --git a/src/p_spec.c b/src/p_spec.c index 0d844cb27..e81620426 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -2027,7 +2027,6 @@ static void K_HandleLapIncrement(player_t *player) SetRandomFakePlayerSkin(player, true); } } - if (player->laps > player->latestlap) { @@ -2046,6 +2045,18 @@ static void K_HandleLapIncrement(player_t *player) // Update power levels for this lap. K_UpdatePowerLevels(player, player->laps, false); + + if (nump > 1 && K_IsPlayerLosing(player) == false) + { + if (nump > 2 && player->position == 1) // 1st place in 1v1 uses thumbs up + { + player->lapPoints += 2; + } + else + { + player->lapPoints++; + } + } } player->latestlap = player->laps; diff --git a/src/p_user.c b/src/p_user.c index f5cb57df2..140e2c7de 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -1353,6 +1353,7 @@ void P_DoPlayerExit(player_t *player) if (RINGTOTAL(player) > 0) { player->totalring += RINGTOTAL(player); + gpRank.rings += player->totalring; extra = player->totalring / lifethreshold; @@ -1363,6 +1364,16 @@ void P_DoPlayerExit(player_t *player) player->xtralife = extra; } } + + if (grandprixinfo.eventmode == GPEVENT_NONE) + { + gpRank.winPoints += K_CalculateGPRankPoints(player->position, gpRank.totalPlayers); + gpRank.laps += player->lapPoints; + } + else if (grandprixinfo.eventmode == GPEVENT_SPECIAL) + { + gpRank.specialWon = true; + } } } }