GP rank cleanup

- Move ranking to its own file.
- gpRank (the variable) -> g_gpRank
- gpRank (the struct) -> gpRank_t
- Functions that worked on the global directly now take a pointer to a struct
- Fixed total ring increment
- Fixed final lap's lapPoints being discarded
- Capsules are now added when exiting with the rest of the stuff
This commit is contained in:
Sally Coolatta 2023-02-23 12:05:01 -05:00
parent 55de982fa3
commit c61e017c38
13 changed files with 326 additions and 239 deletions

View file

@ -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)

View file

@ -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;

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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"

View file

@ -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)
{

156
src/k_rank.c Normal file
View file

@ -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;
}

95
src/k_rank.h Normal file
View file

@ -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

View file

@ -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)
{

View file

@ -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;
}

View file

@ -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++)

View file

@ -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;
}
}
}

View file

@ -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;