diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ad870cec4..04d1c6040 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -135,6 +135,7 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32 k_specialstage.c k_roulette.c k_podium.c + k_rank.c ) if(SRB2_CONFIG_ENABLE_WEBM_MOVIES) diff --git a/src/g_game.c b/src/g_game.c index 601834123..3eae4f03b 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -63,6 +63,7 @@ #include "doomstat.h" #include "k_director.h" #include "k_podium.h" +#include "k_rank.h" #ifdef HAVE_DISCORDRPC #include "discord.h" @@ -4127,7 +4128,7 @@ static void G_DoCompleted(void) G_SetGamestate(GS_NULL); wipegamestate = GS_NULL; - UINT8 bestDifficulty = 0; + g_gpRank.difficulty = 0; for (i = 0; i < MAXPLAYERS; i++) { @@ -4157,13 +4158,11 @@ static void G_DoCompleted(void) if (players[i].bot) { - bestDifficulty = max(bestDifficulty, players[i].botvars.difficulty); + g_gpRank.difficulty = max(g_gpRank.difficulty, 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) @@ -5500,7 +5499,7 @@ void G_SetRetryFlag(void) { if (retrying == false) { - gpRank.continuesUsed++; + g_gpRank.continuesUsed++; } retrying = true; diff --git a/src/k_battle.c b/src/k_battle.c index 8e5db972b..4c5c01b54 100644 --- a/src/k_battle.c +++ b/src/k_battle.c @@ -20,6 +20,7 @@ #include "k_boss.h" // bossinfo.valid #include "p_spec.h" #include "k_objects.h" +#include "k_rank.h" // Battle overtime info struct battleovertime battleovertime; @@ -780,7 +781,7 @@ void K_BattleInit(boolean singleplayercontext) P_SpawnMapThing(mt); } - gpRank.totalCapsules += maptargets; + g_gpRank.totalCapsules += maptargets; battlecapsules = true; } diff --git a/src/k_grandprix.c b/src/k_grandprix.c index c061d271f..bc4779386 100644 --- a/src/k_grandprix.c +++ b/src/k_grandprix.c @@ -21,7 +21,6 @@ #include "r_things.h" struct grandprixinfo grandprixinfo; -struct gpRank gpRank; /*-------------------------------------------------- UINT8 K_BotStartingDifficulty(SINT8 value) @@ -116,13 +115,11 @@ UINT8 K_BotDefaultSkin(void) } /*-------------------------------------------------- - static UINT8 K_GetGPPlayerCount(UINT8 humans) + UINT8 K_GetGPPlayerCount(UINT8 humans) - Counts the number of total players, - including humans and bots, to put into - a GP session. + See header file for description. --------------------------------------------------*/ -static UINT8 K_GetGPPlayerCount(UINT8 humans) +UINT8 K_GetGPPlayerCount(UINT8 humans) { UINT8 playerCount = 8; @@ -135,66 +132,6 @@ static UINT8 K_GetGPPlayerCount(UINT8 humans) 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) @@ -804,75 +741,3 @@ 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 9103b047f..2bd3be61c 100644 --- a/src/k_grandprix.h +++ b/src/k_grandprix.h @@ -37,39 +37,6 @@ 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); @@ -112,13 +79,23 @@ INT16 K_CalculateGPRankPoints(UINT8 position, UINT8 numplayers); UINT8 K_BotDefaultSkin(void); -/*-------------------------------------------------- - void K_InitGrandPrixRank(void); - Calculates rank requirements for a GP session. +/*-------------------------------------------------- + UINT8 K_GetGPPlayerCount(UINT8 humans) + + Counts the number of total players, + including humans and bots, to put into + a GP session. + + Input Arguments:- + humans - Number of human players. + + Return:- + Number of both human players and CPU. --------------------------------------------------*/ -void K_InitGrandPrixRank(void); +UINT8 K_GetGPPlayerCount(UINT8 humans); + /*-------------------------------------------------- void K_InitGrandPrixBots(void); @@ -209,20 +186,6 @@ 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" diff --git a/src/k_hud.c b/src/k_hud.c index 947032b41..27bed0e83 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -37,6 +37,8 @@ #include "r_fps.h" #include "m_random.h" #include "k_roulette.h" +#include "k_bot.h" +#include "k_rank.h" //{ Patch Definitions static patch_t *kp_nodraw; @@ -5070,23 +5072,23 @@ void K_drawKartHUD(void) if (grandprixinfo.gp == true) { - gp_rank_e grade = K_CalculateGPGrade(); + gp_rank_e grade = K_CalculateGPGrade(&g_gpRank); char gradeChar = '?'; V_DrawThinString(0, 0, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, - va("PTS: %d / %d", gpRank.winPoints, gpRank.totalPoints)); + va("PTS: %d / %d", g_gpRank.winPoints, g_gpRank.totalPoints)); V_DrawThinString(0, 10, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, - va("LAPS: %d / %d", gpRank.laps, gpRank.totalLaps)); + va("LAPS: %d / %d", g_gpRank.laps, g_gpRank.totalLaps)); V_DrawThinString(0, 20, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, - va("CONTINUES: %d", gpRank.continuesUsed)); + va("CONTINUES: %d", g_gpRank.continuesUsed)); V_DrawThinString(0, 30, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, - va("CAPSULES: %d / %d", gpRank.capsules, gpRank.totalCapsules)); + va("CAPSULES: %d / %d", g_gpRank.capsules, g_gpRank.totalCapsules)); V_DrawThinString(0, 40, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, - va("RINGS: %d / %d", gpRank.rings, gpRank.totalRings)); + va("RINGS: %d / %d", g_gpRank.rings, g_gpRank.totalRings)); V_DrawThinString(0, 50, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, - va("SPECIAL: %d", gpRank.specialWon)); + va("DIFFICULTY: %d / %d", g_gpRank.difficulty, MAXBOTDIFFICULTY)); V_DrawThinString(0, 60, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, - va("DIFFICULTY: %d", gpRank.difficulty)); + va("SPECIAL: %s", (g_gpRank.specialWon == true) ? "YES" : "NO")); switch (grade) { diff --git a/src/k_rank.c b/src/k_rank.c new file mode 100644 index 000000000..629bf1640 --- /dev/null +++ b/src/k_rank.c @@ -0,0 +1,156 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) by Sally "TehRealSalt" Cochenour +// Copyright (C) by Kart Krew +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file k_rank.c +/// \brief Grand Prix mode ranking + +#include "k_rank.h" +#include "k_grandprix.h" +#include "k_specialstage.h" +#include "doomdef.h" +#include "d_player.h" +#include "g_game.h" +#include "k_bot.h" +#include "k_kart.h" +#include "m_random.h" +#include "r_things.h" + +gpRank_t g_gpRank = {0}; + +/*-------------------------------------------------- + void K_InitGrandPrixRank(gpRank_t *rankData) + + See header file for description. +--------------------------------------------------*/ +void K_InitGrandPrixRank(gpRank_t *rankData) +{ + UINT8 numHumans = 0; + INT32 i; + + memset(rankData, 0, sizeof(gpRank_t)); + + if (grandprixinfo.cup == NULL) + { + return; + } + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i]) + { + if (numHumans < MAXSPLITSCREENPLAYERS && players[i].spectator == false) + { + numHumans++; + } + } + } + + // Calculate players + rankData->players = numHumans; + rankData->totalPlayers = K_GetGPPlayerCount(numHumans); + + // Calculate total of points + // (Should this account for all coop players?) + for (i = 0; i < numHumans; i++) + { + rankData->totalPoints += grandprixinfo.cup->numlevels * K_CalculateGPRankPoints(i + 1, rankData->totalPlayers); + } + + rankData->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++) + { + rankData->totalLaps += laps; + } + + // Total capsules will need to be calculated as you enter the bonus stages... +} + +/*-------------------------------------------------- + gp_rank_e K_CalculateGPGrade(gpRank_t *rankData) + + See header file for description. +--------------------------------------------------*/ +gp_rank_e K_CalculateGPGrade(gpRank_t *rankData) +{ + 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: Balance requirements + 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 (rankData->totalPoints > 0) + { + ours += (rankData->winPoints * pointsWeight) / rankData->totalPoints; + } + + if (rankData->totalLaps > 0) + { + ours += (rankData->laps * lapsWeight) / rankData->totalLaps; + } + + if (rankData->totalCapsules > 0) + { + ours += (rankData->capsules * capsulesWeight) / rankData->totalCapsules; + } + + if (rankData->totalRings > 0) + { + ours += (rankData->rings * ringsWeight) / rankData->totalRings; + } + + ours += (rankData->difficulty * difficultyWeight) / MAXBOTDIFFICULTY; + + ours -= rankData->continuesUsed * continuesPenalty; + + percent = FixedDiv(ours, total); + + for (retGrade = 0; retGrade < GRADE_A; retGrade++) + { + if (percent < gradePercents[retGrade]) + { + break; + } + } + + if (rankData->specialWon == true) + { + // Winning the Special Stage gives you + // a free grade increase. + retGrade++; + } + + return retGrade; +} diff --git a/src/k_rank.h b/src/k_rank.h new file mode 100644 index 000000000..6f417ce82 --- /dev/null +++ b/src/k_rank.h @@ -0,0 +1,95 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) by Sally "TehRealSalt" Cochenour +// Copyright (C) by Kart Krew +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file k_rank.h +/// \brief Grand Prix mode ranking + +#ifndef __K_RANK__ +#define __K_RANK__ + +#include "doomdef.h" +#include "doomstat.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct gpRank_t +{ + 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; +}; + +extern gpRank_t g_gpRank; + +typedef enum +{ + GRADE_E, + GRADE_D, + GRADE_C, + GRADE_B, + GRADE_A, + GRADE_S +} gp_rank_e; + + +/*-------------------------------------------------- + void K_InitGrandPrixRank(gpRank_t *rankData); + + Calculates rank requirements for a GP session. + + Input Arguments:- + rankData - Pointer to struct that contains all + of the information required to calculate GP rank. + + Return:- + N/A +--------------------------------------------------*/ + +void K_InitGrandPrixRank(gpRank_t *rankData); + + +/*-------------------------------------------------- + gp_rank_e K_CalculateGPGrade(gpRank_t *rankData); + + Calculates the player's grade using the + variables from gpRank. + + Input Arguments:- + rankData - struct containing existing rank data. + + Return:- + gp_rank_e representing the total grade. +--------------------------------------------------*/ + +gp_rank_e K_CalculateGPGrade(gpRank_t *rankData); + + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif diff --git a/src/p_inter.c b/src/p_inter.c index be0de4620..3d69eec25 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1595,8 +1595,6 @@ 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_setup.c b/src/p_setup.c index e8043d56e..fe769e1e5 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -102,6 +102,7 @@ #include "acs/interface.h" #include "doomstat.h" // MAXMUSNAMES #include "k_podium.h" +#include "k_rank.h" // Replay names have time #if !defined (UNDER_CE) @@ -7388,7 +7389,7 @@ static void P_InitGametype(void) { if (grandprixinfo.initalize == true) { - K_InitGrandPrixRank(); + K_InitGrandPrixRank(&g_gpRank); K_InitGrandPrixBots(); grandprixinfo.initalize = false; } diff --git a/src/p_spec.c b/src/p_spec.c index e81620426..94506b429 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -2000,34 +2000,6 @@ static void K_HandleLapIncrement(player_t *player) } } - // finished race exit setup - if (player->laps > numlaps) - { - if (specialstageinfo.valid == true) - { - // Don't permit a win just by sneaking ahead of the UFO/emerald. - if (!(specialstageinfo.ufo == NULL || P_MobjWasRemoved(specialstageinfo.ufo))) - { - player->pflags |= PF_NOCONTEST; - } - } - - P_DoPlayerExit(player); - - if (!(player->pflags & PF_NOCONTEST)) - P_SetupSignExit(player); - } - else - { - UINT32 skinflags = (demo.playback) - ? demo.skinlist[demo.currentskinid[(player-players)]].flags - : skins[player->skin].flags; - if (skinflags & SF_IRONMAN) - { - SetRandomFakePlayerSkin(player, true); - } - } - if (player->laps > player->latestlap) { if (player->laps > 1) @@ -2062,8 +2034,35 @@ static void K_HandleLapIncrement(player_t *player) player->latestlap = player->laps; } - thwompsactive = true; // Lap 2 effects + // finished race exit setup + if (player->laps > numlaps) + { + if (specialstageinfo.valid == true) + { + // Don't permit a win just by sneaking ahead of the UFO/emerald. + if (!(specialstageinfo.ufo == NULL || P_MobjWasRemoved(specialstageinfo.ufo))) + { + player->pflags |= PF_NOCONTEST; + } + } + P_DoPlayerExit(player); + + if (!(player->pflags & PF_NOCONTEST)) + P_SetupSignExit(player); + } + else + { + UINT32 skinflags = (demo.playback) + ? demo.skinlist[demo.currentskinid[(player-players)]].flags + : skins[player->skin].flags; + if (skinflags & SF_IRONMAN) + { + SetRandomFakePlayerSkin(player, true); + } + } + + thwompsactive = true; // Lap 2 effects lowestLap = P_FindLowestLap(); for (i = 0; i < numlines; i++) diff --git a/src/p_user.c b/src/p_user.c index 140e2c7de..7c0a9f13a 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -58,6 +58,8 @@ #include "k_terrain.h" // K_SpawnSplashForMobj #include "k_color.h" #include "k_follower.h" +#include "k_battle.h" +#include "k_rank.h" #ifdef HW3SOUND #include "hardware/hw3sound.h" @@ -1353,7 +1355,7 @@ void P_DoPlayerExit(player_t *player) if (RINGTOTAL(player) > 0) { player->totalring += RINGTOTAL(player); - gpRank.rings += player->totalring; + g_gpRank.rings += RINGTOTAL(player); extra = player->totalring / lifethreshold; @@ -1367,13 +1369,15 @@ void P_DoPlayerExit(player_t *player) if (grandprixinfo.eventmode == GPEVENT_NONE) { - gpRank.winPoints += K_CalculateGPRankPoints(player->position, gpRank.totalPlayers); - gpRank.laps += player->lapPoints; + g_gpRank.winPoints += K_CalculateGPRankPoints(player->position, g_gpRank.totalPlayers); + g_gpRank.laps += player->lapPoints; } else if (grandprixinfo.eventmode == GPEVENT_SPECIAL) { - gpRank.specialWon = true; + g_gpRank.specialWon = true; } + + g_gpRank.capsules += numtargets; } } } diff --git a/src/typedef.h b/src/typedef.h index 0b745eb30..ae61dea9a 100644 --- a/src/typedef.h +++ b/src/typedef.h @@ -198,6 +198,9 @@ TYPEDEF (t_floor_t); // k_waypoint.h TYPEDEF (waypoint_t); +// k_rank.h +TYPEDEF (gpRank_t); + // lua_hudlib_drawlist.h typedef struct huddrawlist_s *huddrawlist_h;