Add final position as a ranking requirement

1st is a large bonus, 2nd is a medium bonus, 3rd place is no bonus, and everything below is a penalty. This will help make Loser Valley grades never be above a C at most.
This commit is contained in:
Sally Coolatta 2023-03-04 16:16:37 -05:00
parent 3d1070c2f2
commit 3c07641938
6 changed files with 125 additions and 95 deletions

View file

@ -4128,6 +4128,7 @@ static void G_DoCompleted(void)
G_SetGamestate(GS_NULL); G_SetGamestate(GS_NULL);
wipegamestate = GS_NULL; wipegamestate = GS_NULL;
g_gpRank.position = MAXPLAYERS;
g_gpRank.difficulty = 0; g_gpRank.difficulty = 0;
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
@ -4160,6 +4161,10 @@ static void G_DoCompleted(void)
{ {
g_gpRank.difficulty = max(g_gpRank.difficulty, players[i].botvars.difficulty); g_gpRank.difficulty = max(g_gpRank.difficulty, players[i].botvars.difficulty);
} }
else
{
g_gpRank.position = min(g_gpRank.position, K_GetPodiumPosition(&players[i]));
}
} }
} }

View file

@ -4821,19 +4821,21 @@ static void K_DrawGPRankDebugger(void)
grade = K_CalculateGPGrade(&g_gpRank); grade = K_CalculateGPGrade(&g_gpRank);
V_DrawThinString(0, 0, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, V_DrawThinString(0, 0, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE,
va("PTS: %d / %d", g_gpRank.winPoints, g_gpRank.totalPoints)); va("POS: %d / %d", g_gpRank.position, RANK_NEUTRAL_POSITION));
V_DrawThinString(0, 10, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, V_DrawThinString(0, 10, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE,
va("LAPS: %d / %d", g_gpRank.laps, g_gpRank.totalLaps)); va("PTS: %d / %d", g_gpRank.winPoints, g_gpRank.totalPoints));
V_DrawThinString(0, 20, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, V_DrawThinString(0, 20, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE,
va("CONTINUES: %d", g_gpRank.continuesUsed)); va("LAPS: %d / %d", g_gpRank.laps, g_gpRank.totalLaps));
V_DrawThinString(0, 30, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, V_DrawThinString(0, 30, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE,
va("CAPSULES: %d / %d", g_gpRank.capsules, g_gpRank.totalCapsules)); va("CONTINUES: %d", g_gpRank.continuesUsed));
V_DrawThinString(0, 40, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, V_DrawThinString(0, 40, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE,
va("RINGS: %d / %d", g_gpRank.rings, g_gpRank.totalRings)); va("CAPSULES: %d / %d", g_gpRank.capsules, g_gpRank.totalCapsules));
V_DrawThinString(0, 50, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, V_DrawThinString(0, 50, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE,
va("DIFFICULTY: %d / %d", g_gpRank.difficulty, g_gpRank.difficultyTarget)); va("RINGS: %d / %d", g_gpRank.rings, g_gpRank.totalRings));
V_DrawThinString(0, 60, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE, V_DrawThinString(0, 60, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE,
va("SPECIAL: %s", (g_gpRank.specialWon == true) ? "YES" : "NO")); va("DIFFICULTY: %d / %d", g_gpRank.difficulty, g_gpRank.difficultyTarget));
V_DrawThinString(0, 70, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE,
va("EMERALD: %s", (g_gpRank.specialWon == true) ? "YES" : "NO"));
switch (grade) switch (grade)
{ {
@ -4846,7 +4848,7 @@ static void K_DrawGPRankDebugger(void)
default: { break; } default: { break; }
} }
V_DrawThinString(0, 80, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE|V_YELLOWMAP, V_DrawThinString(0, 90, V_SNAPTOTOP|V_SNAPTOLEFT|V_6WIDTHSPACE|V_ALLOWLOWERCASE|V_YELLOWMAP,
va(" ** FINAL GRADE: %c", gradeChar)); va(" ** FINAL GRADE: %c", gradeChar));
} }

View file

@ -9319,85 +9319,79 @@ void K_KartUpdatePosition(player_t *player)
return; return;
} }
for (i = 0; i < MAXPLAYERS; i++) if (K_PodiumSequence() == true)
{ {
if (!playeringame[i] || players[i].spectator || !players[i].mo) position = K_GetPodiumPosition(player);
continue;
realplayers++; for (i = 0; i < MAXPLAYERS; i++)
if (K_PodiumSequence() == true)
{ {
if (players[i].score > player->score) if (!playeringame[i] || players[i].spectator || !players[i].mo)
{ continue;
// Final score is the important part.
position++; realplayers++;
}
else if (players[i].score == player->score)
{
if (players[i].bot == false && player->bot == true)
{
// Bots are never as important as players.
position++;
}
else if (i < player - players)
{
// Port priority is the final tie breaker.
position++;
}
}
} }
else if (gametyperules & GTR_CIRCUIT) }
else
{
for (i = 0; i < MAXPLAYERS; i++)
{ {
if (player->exiting) // End of match standings if (!playeringame[i] || players[i].spectator || !players[i].mo)
{ continue;
// Only time matters
if (players[i].realtime < player->realtime)
position++;
}
else
{
// I'm a lap behind this player OR
// My distance to the finish line is higher, so I'm behind
if ((players[i].laps > player->laps)
|| (players[i].distancetofinish < player->distancetofinish))
{
position++;
}
}
}
else
{
if (player->exiting) // End of match standings
{
// Only score matters
if (players[i].roundscore > player->roundscore)
position++;
}
else
{
UINT8 myEmeralds = K_NumEmeralds(player);
UINT8 yourEmeralds = K_NumEmeralds(&players[i]);
// First compare all points realplayers++;
if (players[i].roundscore > player->roundscore)
if (gametyperules & GTR_CIRCUIT)
{
if (player->exiting) // End of match standings
{ {
position++; // Only time matters
if (players[i].realtime < player->realtime)
position++;
} }
else if (players[i].roundscore == player->roundscore) else
{ {
// Emeralds are a tie breaker // I'm a lap behind this player OR
if (yourEmeralds > myEmeralds) // My distance to the finish line is higher, so I'm behind
if ((players[i].laps > player->laps)
|| (players[i].distancetofinish < player->distancetofinish))
{ {
position++; position++;
} }
else if (yourEmeralds == myEmeralds) }
}
else
{
if (player->exiting) // End of match standings
{
// Only score matters
if (players[i].roundscore > player->roundscore)
position++;
}
else
{
UINT8 myEmeralds = K_NumEmeralds(player);
UINT8 yourEmeralds = K_NumEmeralds(&players[i]);
// First compare all points
if (players[i].roundscore > player->roundscore)
{ {
// Bumpers are the second tier tie breaker position++;
if (players[i].bumpers > player->bumpers) }
else if (players[i].roundscore == player->roundscore)
{
// Emeralds are a tie breaker
if (yourEmeralds > myEmeralds)
{ {
position++; position++;
} }
else if (yourEmeralds == myEmeralds)
{
// Bumpers are the second tier tie breaker
if (players[i].bumpers > player->bumpers)
{
position++;
}
}
} }
} }
} }

View file

@ -57,7 +57,7 @@ static struct podiumData_s
UINT8 fade; UINT8 fade;
} podiumData; } podiumData;
#define PODIUM_STATES (9) // TODO: enum #define PODIUM_STATES (10) // TODO: enum when this actually gets made
/*-------------------------------------------------- /*--------------------------------------------------
boolean K_PodiumSequence(void) boolean K_PodiumSequence(void)
@ -326,7 +326,7 @@ void K_CeremonyTicker(boolean run)
{ {
podiumData.delay++; podiumData.delay++;
if (podiumData.delay > TICRATE) if (podiumData.delay > TICRATE/2)
{ {
podiumData.state++; podiumData.state++;
podiumData.delay = 0; podiumData.delay = 0;
@ -429,60 +429,74 @@ void K_CeremonyDrawer(void)
{ {
case 1: case 1:
{ {
V_DrawString(x, y, V_SNAPTOTOP|V_SNAPTOLEFT|V_ALLOWLOWERCASE, V_DrawString(x, y, V_ALLOWLOWERCASE,
va("PTS: %d / %d", g_gpRank.winPoints, g_gpRank.totalPoints) va("POS: %d / %d", podiumData.rankData.position, RANK_NEUTRAL_POSITION)
); );
break; break;
} }
case 2: case 2:
{ {
V_DrawString(x, y, V_SNAPTOTOP|V_SNAPTOLEFT|V_ALLOWLOWERCASE, V_DrawString(x, y, V_ALLOWLOWERCASE,
va("LAPS: %d / %d", g_gpRank.laps, g_gpRank.totalLaps) va("PTS: %d / %d", podiumData.rankData.winPoints, podiumData.rankData.totalPoints)
); );
break; break;
} }
case 3: case 3:
{ {
V_DrawString(x, y, V_SNAPTOTOP|V_SNAPTOLEFT|V_ALLOWLOWERCASE, V_DrawString(x, y, V_ALLOWLOWERCASE,
va("CONTINUES: %d", g_gpRank.continuesUsed) va("LAPS: %d / %d", podiumData.rankData.laps, podiumData.rankData.totalLaps)
); );
break; break;
} }
case 4: case 4:
{ {
V_DrawString(x, y, V_SNAPTOTOP|V_SNAPTOLEFT|V_ALLOWLOWERCASE, V_DrawString(x, y, V_ALLOWLOWERCASE,
va("CAPSULES: %d / %d", g_gpRank.capsules, g_gpRank.totalCapsules) va("CONTINUES: %d", podiumData.rankData.continuesUsed)
); );
break; break;
} }
case 5: case 5:
{ {
V_DrawString(x, y, V_SNAPTOTOP|V_SNAPTOLEFT|V_ALLOWLOWERCASE, V_DrawString(x, y, V_ALLOWLOWERCASE,
va("RINGS: %d / %d", g_gpRank.rings, g_gpRank.totalRings) va("CAPSULES: %d / %d", podiumData.rankData.capsules, podiumData.rankData.totalCapsules)
); );
break; break;
} }
case 6: case 6:
{ {
V_DrawString(x, y, V_SNAPTOTOP|V_SNAPTOLEFT|V_ALLOWLOWERCASE, V_DrawString(x, y, V_ALLOWLOWERCASE,
va("DIFFICULTY: %d / %d", g_gpRank.difficulty, g_gpRank.difficultyTarget) va("RINGS: %d / %d", podiumData.rankData.rings, podiumData.rankData.totalRings)
); );
break; break;
} }
case 7: case 7:
{ {
V_DrawString(x, y, V_SNAPTOTOP|V_SNAPTOLEFT|V_ALLOWLOWERCASE, V_DrawString(x, y, V_ALLOWLOWERCASE,
va("SPECIAL: %s", (g_gpRank.specialWon == true) ? "YES" : "NO") va("DIFFICULTY: %d / %d", podiumData.rankData.difficulty, podiumData.rankData.difficultyTarget)
); );
break; break;
} }
case 8: case 8:
{ {
V_DrawString(x, y + 10, V_SNAPTOTOP|V_SNAPTOLEFT|V_ALLOWLOWERCASE, V_DrawString(x, y, V_ALLOWLOWERCASE,
va("EMERALD: %s", (podiumData.rankData.specialWon == true) ? "YES" : "NO")
);
break;
}
case 9:
{
V_DrawString(x, y + 10, V_YELLOWMAP|V_ALLOWLOWERCASE,
va(" ** FINAL GRADE: %c", gradeChar) va(" ** FINAL GRADE: %c", gradeChar)
); );
break; break;
} }
case 10:
{
V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, BASEVIDHEIGHT - 10, V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_6WIDTHSPACE|V_ALLOWLOWERCASE,
"Press some button type deal to continue"
);
break;
}
} }
y += 10; y += 10;

View file

@ -56,6 +56,9 @@ void K_InitGrandPrixRank(gpRank_t *rankData)
rankData->players = numHumans; rankData->players = numHumans;
rankData->totalPlayers = K_GetGPPlayerCount(numHumans); rankData->totalPlayers = K_GetGPPlayerCount(numHumans);
// Initialize to the neutral value.
rankData->position = RANK_NEUTRAL_POSITION;
// Calculate total of points // Calculate total of points
// (Should this account for all coop players?) // (Should this account for all coop players?)
for (i = 0; i < numHumans; i++) for (i = 0; i < numHumans; i++)
@ -111,17 +114,25 @@ gp_rank_e K_CalculateGPGrade(gpRank_t *rankData)
gp_rank_e retGrade = GRADE_E; gp_rank_e retGrade = GRADE_E;
const INT32 pointsWeight = 100; const INT32 positionWeight = 1500;
const INT32 lapsWeight = 100; const INT32 pointsWeight = 1000;
const INT32 capsulesWeight = 100; const INT32 lapsWeight = 1000;
const INT32 ringsWeight = 50; const INT32 capsulesWeight = 1000;
const INT32 difficultyWeight = 20; const INT32 ringsWeight = 500;
const INT32 total = pointsWeight + lapsWeight + capsulesWeight + ringsWeight + difficultyWeight; const INT32 difficultyWeight = 200;
const INT32 continuesPenalty = 20; const INT32 total = positionWeight + pointsWeight + lapsWeight + capsulesWeight + ringsWeight + difficultyWeight;
const INT32 continuesPenalty = 200;
INT32 ours = 0; INT32 ours = 0;
fixed_t percent = 0; fixed_t percent = 0;
if (rankData->position > 0)
{
const INT32 sc = (rankData->position - 1);
const INT32 loser = (RANK_NEUTRAL_POSITION - 1);
ours += ((loser - sc) * positionWeight) / loser;
}
if (rankData->totalPoints > 0) if (rankData->totalPoints > 0)
{ {
ours += (rankData->winPoints * pointsWeight) / rankData->totalPoints; ours += (rankData->winPoints * pointsWeight) / rankData->totalPoints;

View file

@ -25,6 +25,8 @@ struct gpRank_t
UINT8 players; UINT8 players;
UINT8 totalPlayers; UINT8 totalPlayers;
UINT8 position;
UINT32 winPoints; UINT32 winPoints;
UINT32 totalPoints; UINT32 totalPoints;
@ -57,6 +59,8 @@ typedef enum
GRADE_S GRADE_S
} gp_rank_e; } gp_rank_e;
// 3rd place is neutral, anything below is a penalty
#define RANK_NEUTRAL_POSITION (3)
/*-------------------------------------------------- /*--------------------------------------------------
void K_InitGrandPrixRank(gpRank_t *rankData); void K_InitGrandPrixRank(gpRank_t *rankData);