GP ranking

Needs balancing + intermission, but mostly functional
This commit is contained in:
Sally Coolatta 2023-02-22 04:31:15 -05:00
parent 20dedb6602
commit 55de982fa3
11 changed files with 291 additions and 7 deletions

View file

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

View file

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

View file

@ -780,6 +780,7 @@ void K_BattleInit(boolean singleplayercontext)
P_SpawnMapThing(mt);
}
gpRank.totalCapsules += maptargets;
battlecapsules = true;
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -7388,6 +7388,7 @@ static void P_InitGametype(void)
{
if (grandprixinfo.initalize == true)
{
K_InitGrandPrixRank();
K_InitGrandPrixBots();
grandprixinfo.initalize = false;
}

View file

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

View file

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