Merge branch 'master' of https://git.do.srb2.org/KartKrew/Kart into will-it-blend

# Conflicts:
#	src/k_podium.c
This commit is contained in:
toaster 2023-10-29 12:36:30 +00:00
commit 388b14e956
21 changed files with 1622 additions and 865 deletions

View file

@ -142,8 +142,8 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32
k_profiles.c
k_specialstage.c
k_roulette.c
k_podium.c
k_rank.c
k_podium.cpp
k_rank.cpp
k_vote.c
k_serverstats.c
k_zvote.c

View file

@ -802,7 +802,7 @@ consvar_t cv_kartdebugwaypoints = OnlineCheat("debugwaypoints", "Off").values({{
extern CV_PossibleValue_t numlaps_cons_t[];
void NumLaps_OnChange(void);
consvar_t cv_numlaps = OnlineCheat("numlaps", "Map default").values(numlaps_cons_t).onchange(NumLaps_OnChange).save().description("Race maps always have the same number of laps");
consvar_t cv_numlaps = OnlineCheat("numlaps", "Map default").values(numlaps_cons_t).onchange(NumLaps_OnChange).description("Race maps always have the same number of laps");
consvar_t cv_restrictskinchange = OnlineCheat("restrictskinchange", "Yes").yes_no().description("Don't let players change their skin in the middle of gameplay");
consvar_t cv_spbtest = OnlineCheat("spbtest", "Off").on_off().description("SPB can never target a player");

View file

@ -4199,7 +4199,10 @@ static void G_DoCompleted(void)
if (intertype == int_none)
{
G_UpdateVisited();
K_UpdateGPRank();
if (grandprixinfo.gp == true)
{
K_UpdateGPRank(&grandprixinfo.rank);
}
G_AfterIntermission();
}
else
@ -5857,7 +5860,13 @@ char *G_BuildMapTitle(INT32 mapnum)
char *title = NULL;
if (!mapnum || mapnum > nummapheaders || !mapheaderinfo[mapnum-1])
{
#ifdef PARANOIA
I_Error("G_BuildMapTitle: Internal map ID %d not found (nummapheaders = %d)", mapnum-1, nummapheaders);
#else
return NULL;
#endif
}
if (strcmp(mapheaderinfo[mapnum-1]->lvlttl, ""))
{

View file

@ -40,7 +40,23 @@ UINT8 numtargets = 0; // Capsules busted
INT32 K_StartingBumperCount(void)
{
if (battleprisons)
return 0; // always 1 hit in Prison Break
{
if (grandprixinfo.gp)
{
switch (grandprixinfo.gamespeed)
{
case KARTSPEED_HARD:
return (grandprixinfo.masterbots == true) ? 0 : 1;
case KARTSPEED_NORMAL:
return 2;
case KARTSPEED_EASY:
return 3;
}
}
return 2; // Normal
}
return cv_kartbumpers.value;
}

View file

@ -1976,9 +1976,8 @@ void K_drawKartTimestamp(tic_t drawtime, INT32 TX, INT32 TY, INT32 splitflags, U
}
}
static fixed_t K_DrawKartPositionNumPatch(UINT8 num, UINT8 *color, fixed_t x, fixed_t y, fixed_t scale, INT32 flags)
static fixed_t K_DrawKartPositionNumPatch(UINT8 num, UINT8 splitIndex, UINT8 *color, fixed_t x, fixed_t y, fixed_t scale, INT32 flags)
{
const UINT8 splitIndex = (r_splitscreen > 0) ? 1 : 0;
fixed_t w = FRACUNIT;
fixed_t h = FRACUNIT;
INT32 overlayFlags[2];
@ -2023,14 +2022,76 @@ static fixed_t K_DrawKartPositionNumPatch(UINT8 num, UINT8 *color, fixed_t x, fi
return x;
}
void K_DrawKartPositionNumXY(
UINT8 num,
UINT8 splitIndex,
fixed_t fx, fixed_t fy, fixed_t scale, INT32 fflags,
tic_t counter, boolean subtract,
boolean exit, boolean lastLap, boolean losing
)
{
counter /= 3; // Alternate colors every three frames
UINT8 *color = NULL;
if (exit && num == 1)
{
// 1st place winner? You get rainbows!!
color = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_POSNUM_BEST1 + (counter % 6), GTC_CACHE);
}
else if (exit || lastLap)
{
// On the final lap, or already won.
boolean useRedNums = losing;
if (subtract)
{
// Subtracting RED will look BLUE, and vice versa.
useRedNums = !useRedNums;
}
if (useRedNums == true)
{
color = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_POSNUM_LOSE1 + (counter % 3), GTC_CACHE);
}
else
{
color = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_POSNUM_WIN1 + (counter % 3), GTC_CACHE);
}
}
else
{
color = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_POSNUM, GTC_CACHE);
}
if ((fflags & V_SNAPTORIGHT) == 0)
{
UINT8 adjustNum = num;
do
{
fixed_t w = SHORT(kp_positionnum[adjustNum % 10][0][splitIndex]->width) * scale;
fx += w;
adjustNum /= 10;
} while (adjustNum);
}
// Draw the number
do
{
fx = K_DrawKartPositionNumPatch(
(num % 10), splitIndex, color,
fx, fy, scale, V_SPLITSCREEN|fflags
);
num /= 10;
} while (num);
}
static void K_DrawKartPositionNum(UINT8 num)
{
const tic_t counter = (leveltime / 3); // Alternate colors every three frames
UINT8 splitIndex = (r_splitscreen > 0) ? 1 : 0;
fixed_t scale = FRACUNIT;
fixed_t fx = 0, fy = 0;
transnum_t trans = 0;
INT32 fflags = 0;
UINT8 *color = NULL;
if (stplyr->lives <= 0 && stplyr->playerstate == PST_DEAD)
{
@ -2110,57 +2171,16 @@ static void K_DrawKartPositionNum(UINT8 num)
fflags |= (trans << V_ALPHASHIFT);
}
if (stplyr->exiting && num == 1)
{
// 1st place winner? You get rainbows!!
color = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_POSNUM_BEST1 + (counter % 6), GTC_CACHE);
}
else if (stplyr->laps >= numlaps || stplyr->exiting)
{
// On the final lap, or already won.
boolean useRedNums = K_IsPlayerLosing(stplyr);
if ((mapheaderinfo[gamemap - 1]->levelflags & LF_SUBTRACTNUM) == LF_SUBTRACTNUM)
{
// Subtracting RED will look BLUE, and vice versa.
useRedNums = !useRedNums;
}
if (useRedNums == true)
{
color = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_POSNUM_LOSE1 + (counter % 3), GTC_CACHE);
}
else
{
color = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_POSNUM_WIN1 + (counter % 3), GTC_CACHE);
}
}
else
{
color = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_POSNUM, GTC_CACHE);
}
if ((fflags & V_SNAPTORIGHT) == 0)
{
const UINT8 splitIndex = (r_splitscreen > 0) ? 1 : 0;
UINT8 adjustNum = num;
do
{
fixed_t w = SHORT(kp_positionnum[adjustNum % 10][0][splitIndex]->width) * scale;
fx += w;
adjustNum /= 10;
} while (adjustNum);
}
// Draw the number
do
{
fx = K_DrawKartPositionNumPatch(
(num % 10), color,
fx, fy, scale, V_SPLITSCREEN|fflags
);
num /= 10;
} while (num);
K_DrawKartPositionNumXY(
num,
splitIndex,
fx, fy, scale, fflags,
leveltime,
((mapheaderinfo[gamemap - 1]->levelflags & LF_SUBTRACTNUM) == LF_SUBTRACTNUM),
stplyr->exiting,
(stplyr->laps >= numlaps),
K_IsPlayerLosing(stplyr)
);
}
static boolean K_drawKartPositionFaces(void)

View file

@ -54,6 +54,14 @@ void K_drawButton(fixed_t x, fixed_t y, INT32 flags, patch_t *button[2], boolean
void K_drawButtonAnim(INT32 x, INT32 y, INT32 flags, patch_t *button[2], tic_t animtic);
void K_DrawSticker(INT32 x, INT32 y, INT32 width, INT32 flags, boolean isSmall);
void K_DrawKartPositionNumXY(
UINT8 num,
UINT8 splitIndex,
fixed_t fx, fixed_t fy, fixed_t scale, INT32 fflags,
tic_t counter, boolean subtract,
boolean exit, boolean lastLap, boolean losing
);
extern patch_t *kp_capsuletarget_arrow[2][2];
extern patch_t *kp_capsuletarget_icon[2];
extern patch_t *kp_capsuletarget_far[2][2];

View file

@ -3128,7 +3128,7 @@ mobj_t *K_GetGardenTop(player_t *player)
static fixed_t K_FlameShieldDashVar(INT32 val)
{
// 1 second = 75% + 50% top speed
return (3*FRACUNIT/4) + (((val * FRACUNIT) / TICRATE) / 2);
return (3*FRACUNIT/4) + (((val * FRACUNIT) / TICRATE));
}
INT16 K_GetSpindashChargeTime(player_t *player)
@ -4232,8 +4232,8 @@ void K_UpdateSliptideZipIndicator(player_t *player)
mobj->angle = momentumAngle + ANGLE_90;
P_SetScale(mobj, 3 * player->mo->scale / 2);
// No stored boost
if (player->sliptideZip == 0)
// No stored boost (or negligible enough that it might be a mistake)
if (player->sliptideZip <= HIDEWAVEDASHCHARGE)
{
mobj->renderflags |= RF_DONTDRAW;
mobj->frame = 7;
@ -9892,7 +9892,7 @@ static void K_KartDrift(player_t *player, boolean onground)
{
if (!keepsliptide && K_IsLosingSliptideZip(player) && player->sliptideZip > 0)
{
if (!S_SoundPlaying(player->mo, sfx_waved2))
if (player->sliptideZip > HIDEWAVEDASHCHARGE && !S_SoundPlaying(player->mo, sfx_waved2))
S_StartSoundAtVolume(player->mo, sfx_waved2, 255); // Losing combo time, going to boost
S_StopSoundByID(player->mo, sfx_waved1);
S_StopSoundByID(player->mo, sfx_waved4);
@ -9954,7 +9954,7 @@ static void K_KartDrift(player_t *player, boolean onground)
player->sliptideZipDelay = 0;
S_StopSoundByID(player->mo, sfx_waved2);
S_StopSoundByID(player->mo, sfx_waved4);
if (!S_SoundPlaying(player->mo, sfx_waved1))
if (player->sliptideZip > HIDEWAVEDASHCHARGE && !S_SoundPlaying(player->mo, sfx_waved1))
S_StartSoundAtVolume(player->mo, sfx_waved1, 255); // Charging
}
@ -10193,7 +10193,7 @@ static INT32 K_FlameShieldMax(player_t *player)
UINT32 distv = 1024; // Pre no-scams: 2048
distv = distv * 16 / FLAMESHIELD_MAX; // Old distv was based on a 16-segment bar
UINT8 numplayers = 0;
UINT32 scamradius = 2000; // How close is close enough that we shouldn't be allowed to scam 1st?
UINT32 scamradius = 1500; // How close is close enough that we shouldn't be allowed to scam 1st?
UINT8 i;
if (gametyperules & GTR_CIRCUIT)
@ -11709,29 +11709,31 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
if (player->flamelength < destlen)
player->flamelength = min(destlen, player->flamelength + 7); // Allows gauge to grow quickly when first acquired. 120/16 = ~7
flamemax = player->flamelength;
if (flamemax > 0)
flamemax += TICRATE; // leniency period
flamemax = player->flamelength + TICRATE; // TICRATE leniency period, but we block most effects at flamelength 0 down below
if ((cmd->buttons & BT_ATTACK) && (player->pflags & PF_HOLDREADY))
{
const INT32 incr = (gametyperules & GTR_CLOSERPLAYERS) ? 4 : 2;
if (player->flamedash == 0)
{
S_StartSound(player->mo, sfx_s3k43);
K_PlayBoostTaunt(player->mo);
}
player->flamedash += incr;
player->flamemeter += incr;
if (!onground)
if (player->flamelength)
{
P_Thrust(
player->mo, K_MomentumAngle(player->mo),
FixedMul(player->mo->scale, K_GetKartGameSpeedScalar(gamespeed))
);
if (player->flamedash == 0)
{
S_StartSound(player->mo, sfx_s3k43);
K_PlayBoostTaunt(player->mo);
}
player->flamedash += incr;
if (!onground)
{
P_Thrust(
player->mo, K_MomentumAngle(player->mo),
FixedMul(player->mo->scale, K_GetKartGameSpeedScalar(gamespeed))
);
}
}
if (player->flamemeter > flamemax)
@ -12445,6 +12447,13 @@ tic_t K_TimeLimitForGametype(void)
{
if (battleprisons)
{
if (grandprixinfo.gp)
{
if (grandprixinfo.masterbots)
return 15*TICRATE;
else if (grandprixinfo.gamespeed == KARTSPEED_EASY)
return 30*TICRATE;
}
return 20*TICRATE;
}

View file

@ -54,6 +54,10 @@ Make sure this matches the actual number of states
#define RINGVOLUMEUSEPENALTY 15
#define RINGVOLUMEREGEN 3
// Mispredicted turns can generate phantom sliptide inputs for a few tics.
// Delay the wavedash visuals until we're reasonably sure that it's a deliberate turn.
#define HIDEWAVEDASHCHARGE (60)
angle_t K_ReflectAngle(angle_t angle, angle_t against, fixed_t maxspeed, fixed_t yourspeed);
boolean K_IsDuelItem(mobjtype_t type);

View file

@ -1158,6 +1158,7 @@ void M_DrawImageDef(void);
void M_DrawCharacterSelect(void);
boolean M_DrawCharacterSprite(INT16 x, INT16 y, INT16 skin, UINT8 spr2, UINT8 rotation, UINT32 frame, INT32 addflags, UINT8 *colormap);
void M_DrawCup(cupheader_t *cup, fixed_t x, fixed_t y, INT32 lockedTic, boolean isTrophy, UINT8 placement);
void M_DrawCupSelect(void);
void M_DrawLevelSelect(void);
void M_DrawTimeAttack(void);

View file

@ -2601,11 +2601,98 @@ fixed_t M_DrawCupWinData(INT32 rankx, INT32 ranky, cupheader_t *cup, UINT8 diffi
return rankw;
}
void M_DrawCup(cupheader_t *cup, fixed_t x, fixed_t y, INT32 lockedTic, boolean isTrophy, UINT8 placement)
{
patch_t *patch = NULL;
UINT8 *colormap = NULL;
INT16 icony = 7;
char status = 'A';
char monitor = '0';
if (isTrophy)
{
UINT16 col = SKINCOLOR_NONE;
switch (placement)
{
case 0:
break;
case 1:
col = SKINCOLOR_GOLD;
status = 'B';
break;
case 2:
col = SKINCOLOR_SILVER;
status = 'B';
break;
case 3:
col = SKINCOLOR_BRONZE;
status = 'B';
break;
default:
col = SKINCOLOR_BEIGE;
break;
}
if (col != SKINCOLOR_NONE)
colormap = R_GetTranslationColormap(TC_RAINBOW, col, GTC_MENUCACHE);
else
colormap = NULL;
}
if (cup == &dummy_lostandfound)
{
// No cup? Lost and found!
monitor = '0';
}
else
{
if (cup->monitor < 10)
{
monitor = '0' + cup->monitor;
if (monitor == '2')
{
icony = 5; // by default already 7px down, so this is really 2px further up
}
else if (monitor == '3')
{
icony = 6;
}
}
else
{
monitor = 'A' + (cup->monitor - 10);
if (monitor == 'X')
{
icony = 11;
}
}
}
patch = W_CachePatchName(va("CUPMON%c%c", monitor, status), PU_CACHE);
V_DrawFixedPatch(x, y, FRACUNIT, 0, patch, colormap);
if (cup == &dummy_lostandfound)
{
; // Only ever placed on the list if valid
}
else if (lockedTic != 0)
{
patch_t *st = W_CachePatchName(va("ICONST0%d", (lockedTic % 4) + 1), PU_CACHE);
V_DrawFixedPatch(x + (8 * FRACUNIT), y + (icony * FRACUNIT), FRACUNIT, 0, st, NULL);
}
else
{
V_DrawFixedPatch(x + (8 * FRACUNIT), y + (icony * FRACUNIT), FRACUNIT, 0, W_CachePatchName(cup->icon, PU_CACHE), NULL);
V_DrawFixedPatch(x + (8 * FRACUNIT), y + (icony * FRACUNIT), FRACUNIT, 0, W_CachePatchName("CUPBOX", PU_CACHE), NULL);
}
}
void M_DrawCupSelect(void)
{
UINT8 i, j, temp = 0;
INT16 x, y;
UINT8 *colormap = NULL;
cupwindata_t *windata = NULL;
levelsearch_t templevelsearch = levellist.levelsearch; // full copy
@ -2614,117 +2701,46 @@ void M_DrawCupSelect(void)
for (j = 0; j < CUPMENU_ROWS; j++)
{
size_t id = (i + (j * CUPMENU_COLUMNS)) + (cupgrid.pageno * (CUPMENU_COLUMNS * CUPMENU_ROWS));
patch_t *patch = NULL;
INT16 icony = 7;
char status = 'A';
char monitor;
if (!cupgrid.builtgrid[id])
break;
templevelsearch.cup = cupgrid.builtgrid[id];
if (cupgrid.grandprix
&& (cv_dummygpdifficulty.value >= 0 && cv_dummygpdifficulty.value < KARTGP_MAX))
{
UINT16 col = SKINCOLOR_NONE;
windata = &templevelsearch.cup->windata[cv_dummygpdifficulty.value];
switch (windata->best_placement)
{
case 0:
break;
case 1:
col = SKINCOLOR_GOLD;
status = 'B';
break;
case 2:
col = SKINCOLOR_SILVER;
status = 'B';
break;
case 3:
col = SKINCOLOR_BRONZE;
status = 'B';
break;
default:
col = SKINCOLOR_BEIGE;
break;
}
if (col != SKINCOLOR_NONE)
colormap = R_GetTranslationColormap(TC_RAINBOW, col, GTC_MENUCACHE);
else
colormap = NULL;
}
if (templevelsearch.cup == &dummy_lostandfound)
{
// No cup? Lost and found!
monitor = '0';
}
else
{
if (templevelsearch.cup->monitor < 10)
{
monitor = '0' + templevelsearch.cup->monitor;
if (monitor == '2')
{
icony = 5; // by default already 7px down, so this is really 2px further up
}
else if (monitor == '3')
{
icony = 6;
}
}
else
{
monitor = 'A' + (templevelsearch.cup->monitor - 10);
if (monitor == 'X')
{
icony = 11;
}
}
}
patch = W_CachePatchName(va("CUPMON%c%c", monitor, status), PU_CACHE);
x = 14 + (i*42);
y = 20 + (j*44) - (30*menutransition.tics);
V_DrawFixedPatch((x)*FRACUNIT, (y)<<FRACBITS, FRACUNIT, 0, patch, colormap);
if (templevelsearch.cup == &dummy_lostandfound)
; // Only ever placed on the list if valid
else if (M_GetFirstLevelInList(&temp, &templevelsearch) == NEXTMAP_INVALID)
const boolean isGP = (cupgrid.grandprix && (cv_dummygpdifficulty.value >= 0 && cv_dummygpdifficulty.value < KARTGP_MAX));
if (isGP)
{
patch_t *st = W_CachePatchName(va("ICONST0%d", (cupgrid.previewanim % 4) + 1), PU_CACHE);
V_DrawScaledPatch(x + 8, y + icony, 0, st);
windata = &templevelsearch.cup->windata[cv_dummygpdifficulty.value];
}
else
{
V_DrawScaledPatch(x + 8, y + icony, 0, W_CachePatchName(templevelsearch.cup->icon, PU_CACHE));
V_DrawScaledPatch(x + 8, y + icony, 0, W_CachePatchName("CUPBOX", PU_CACHE));
if (cupgrid.grandprix == true
M_DrawCup(
templevelsearch.cup,
x * FRACUNIT, y * FRACUNIT,
(M_GetFirstLevelInList(&temp, &templevelsearch) == NEXTMAP_INVALID) ? ((cupgrid.previewanim % 4) + 1) : 0,
isGP,
windata ? windata->best_placement : 0
);
if (cupgrid.grandprix == true
&& templevelsearch.cup == cupsavedata.cup
&& id != CUPMENU_CURSORID)
{
V_DrawScaledPatch(x + 32, y + 32, 0, W_CachePatchName("CUPBKUP1", PU_CACHE));
}
{
V_DrawScaledPatch(x + 32, y + 32, 0, W_CachePatchName("CUPBKUP1", PU_CACHE));
}
if (windata && windata->best_placement != 0)
{
M_DrawCupWinData(
x,
8 + (j*100) - (30*menutransition.tics),
templevelsearch.cup,
cv_dummygpdifficulty.value,
(cupgrid.previewanim & 1),
false
);
}
if (windata && windata->best_placement != 0)
{
M_DrawCupWinData(
x,
8 + (j*100) - (30*menutransition.tics),
templevelsearch.cup,
cv_dummygpdifficulty.value,
(cupgrid.previewanim & 1),
false
);
}
}
}

View file

@ -1,614 +0,0 @@
// 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_podium.c
/// \brief Grand Prix podium cutscene
#include "k_podium.h"
#include "doomdef.h"
#include "d_main.h"
#include "d_netcmd.h"
#include "f_finale.h"
#include "g_game.h"
#include "hu_stuff.h"
#include "r_local.h"
#include "s_sound.h"
#include "i_time.h"
#include "i_video.h"
#include "v_video.h"
#include "w_wad.h"
#include "z_zone.h"
#include "i_system.h"
#include "i_threads.h"
#include "dehacked.h"
#include "g_input.h"
#include "console.h"
#include "m_random.h"
#include "m_misc.h" // moviemode functionality
#include "y_inter.h"
#include "m_cond.h"
#include "p_local.h"
#include "p_saveg.h"
#include "p_setup.h"
#include "st_stuff.h" // hud hiding
#include "fastcmp.h"
#include "lua_hud.h"
#include "lua_hook.h"
#include "k_menu.h"
#include "k_grandprix.h"
#include "k_rank.h"
static struct podiumData_s
{
boolean ranking;
gpRank_t rank;
gp_rank_e grade;
UINT8 state;
UINT8 delay;
UINT8 fade;
} podiumData;
#define PODIUM_STATES (9) // TODO: enum when this actually gets made
/*--------------------------------------------------
boolean K_PodiumSequence(void)
See header file for description.
--------------------------------------------------*/
boolean K_PodiumSequence(void)
{
return (gamestate == GS_CEREMONY);
}
/*--------------------------------------------------
boolean K_PodiumRanking(void)
See header file for description.
--------------------------------------------------*/
boolean K_PodiumRanking(void)
{
return (gamestate == GS_CEREMONY && podiumData.ranking == true);
}
/*--------------------------------------------------
boolean K_PodiumGrade(void)
See header file for description.
--------------------------------------------------*/
gp_rank_e K_PodiumGrade(void)
{
if (K_PodiumSequence() == false)
{
return 0;
}
return podiumData.grade;
}
/*--------------------------------------------------
boolean K_PodiumHasEmerald(void)
See header file for description.
--------------------------------------------------*/
boolean K_PodiumHasEmerald(void)
{
if (K_PodiumSequence() == false)
{
return false;
}
return podiumData.rank.specialWon;
}
/*--------------------------------------------------
UINT8 K_GetPodiumPosition(player_t *player)
See header file for description.
--------------------------------------------------*/
UINT8 K_GetPodiumPosition(player_t *player)
{
UINT8 position = 1;
INT32 i;
for (i = 0; i < MAXPLAYERS; i++)
{
player_t *other = NULL;
if (playeringame[i] == false)
{
continue;
}
other = &players[i];
if (other->bot == false && other->spectator == true)
{
continue;
}
if (other->score > player->score)
{
// Final score is the important part.
position++;
}
else if (other->score == player->score)
{
if (other->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++;
}
}
}
return position;
}
/*--------------------------------------------------
static void K_SetPodiumWaypoint(player_t *const player, waypoint_t *const waypoint)
Changes the player's current and next waypoints, for
use during the podium sequence.
Input Arguments:-
player - The player to update the waypoints of.
waypoint - The new current waypoint.
Return:-
None
--------------------------------------------------*/
static void K_SetPodiumWaypoint(player_t *const player, waypoint_t *const waypoint)
{
// Set the new waypoint.
player->currentwaypoint = waypoint;
if ((waypoint == NULL)
|| (waypoint->nextwaypoints == NULL)
|| (waypoint->numnextwaypoints == 0U))
{
// No waypoint, or no next waypoint.
player->nextwaypoint = NULL;
return;
}
// Simply use the first available next waypoint.
// No need for split paths in these cutscenes.
player->nextwaypoint = waypoint->nextwaypoints[0];
}
/*--------------------------------------------------
void K_InitializePodiumWaypoint(player_t *const player)
See header file for description.
--------------------------------------------------*/
void K_InitializePodiumWaypoint(player_t *const player)
{
if ((player != NULL) && (player->mo != NULL))
{
player->position = K_GetPodiumPosition(player);
if (player->position > 0 && player->position <= MAXPLAYERS)
{
// Initialize our first waypoint to the one that
// matches our position.
K_SetPodiumWaypoint(player, K_GetWaypointFromID(player->position));
}
else
{
// None does, so remove it if we happen to have one.
K_SetPodiumWaypoint(player, NULL);
}
}
}
/*--------------------------------------------------
void K_UpdatePodiumWaypoints(player_t *const player)
See header file for description.
--------------------------------------------------*/
void K_UpdatePodiumWaypoints(player_t *const player)
{
if ((player != NULL) && (player->mo != NULL))
{
if (player->currentwaypoint != NULL)
{
const fixed_t xydist = P_AproxDistance(
player->mo->x - player->currentwaypoint->mobj->x,
player->mo->y - player->currentwaypoint->mobj->y
);
const fixed_t xyzdist = P_AproxDistance(
xydist,
player->mo->z - player->currentwaypoint->mobj->z
);
//const fixed_t speed = P_AproxDistance(player->mo->momx, player->mo->momy);
if (xyzdist <= player->mo->radius + player->currentwaypoint->mobj->radius)
{
// Reached waypoint, go to the next waypoint.
K_SetPodiumWaypoint(player, player->nextwaypoint);
}
}
}
}
/*--------------------------------------------------
boolean K_StartCeremony(void)
See header file for description.
--------------------------------------------------*/
boolean K_StartCeremony(void)
{
if (grandprixinfo.gp == false)
{
return false;
}
INT32 i;
INT32 podiumMapNum = NEXTMAP_INVALID;
if (grandprixinfo.cup != NULL
&& grandprixinfo.cup->cachedlevels[CUPCACHE_PODIUM] != NEXTMAP_INVALID)
{
podiumMapNum = grandprixinfo.cup->cachedlevels[CUPCACHE_PODIUM];
}
else if (podiummap)
{
podiumMapNum = G_MapNumber(podiummap);
}
if (podiumMapNum < nummapheaders
&& mapheaderinfo[podiumMapNum]
&& mapheaderinfo[podiumMapNum]->lumpnum != LUMPERROR)
{
gamemap = podiumMapNum+1;
encoremode = grandprixinfo.encore;
if (savedata.lives > 0)
{
K_LoadGrandPrixSaveGame();
savedata.lives = 0;
}
// Make sure all of the GAME OVER'd players can spawn
// and be present for the podium
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i])
{
if (players[i].lives < 1)
players[i].lives = 1;
if (players[i].bot)
players[i].spectator = false;
}
}
G_SetGametype(GT_RACE);
G_DoLoadLevelEx(false, GS_CEREMONY);
wipegamestate = GS_CEREMONY; // I don't know what else to do here
r_splitscreen = 0; // Only one screen for the ceremony
R_ExecuteSetViewSize();
return true;
}
return false;
}
/*--------------------------------------------------
void K_FinishCeremony(void)
See header file for description.
--------------------------------------------------*/
void K_FinishCeremony(void)
{
if (K_PodiumSequence() == false)
{
return;
}
podiumData.ranking = true;
// Play the noise now (via G_UpdateVisited's concluding gamedata save)
prevmap = gamemap-1;
G_UpdateVisited();
}
/*--------------------------------------------------
void K_ResetCeremony(void)
See header file for description.
--------------------------------------------------*/
void K_ResetCeremony(void)
{
SINT8 i;
memset(&podiumData, 0, sizeof(struct podiumData_s));
if (K_PodiumSequence() == false)
{
return;
}
// Establish rank and grade for this play session.
podiumData.rank = grandprixinfo.rank;
podiumData.grade = K_CalculateGPGrade(&podiumData.rank);
// Set up music for podium.
{
if (podiumData.rank.position <= 1)
mapmusrng = 2;
else if (podiumData.rank.position == 2
|| podiumData.rank.position == 3)
mapmusrng = 1;
else
mapmusrng = 0;
while (mapmusrng >= max(1, mapheaderinfo[gamemap-1]->musname_size))
mapmusrng--;
mapmusflags |= MUSIC_RELOADRESET;
}
if (!grandprixinfo.cup)
{
return;
}
// Write grade, position, and emerald-having-ness for later sessions!
i = (grandprixinfo.masterbots) ? KARTGP_MASTER : grandprixinfo.gamespeed;
// All results populate downwards in difficulty. This prevents someone
// who's just won on Normal from feeling obligated to complete Easy too.
for (; i >= 0; i--)
{
boolean anymerit = false;
if ((grandprixinfo.cup->windata[i].best_placement == 0) // First run
|| (podiumData.rank.position <= grandprixinfo.cup->windata[i].best_placement)) // Later, better run
{
grandprixinfo.cup->windata[i].best_placement = podiumData.rank.position;
// The following will not occur in unmodified builds, but pre-emptively sanitise gamedata if someone just changes MAXPLAYERS and calls it a day
if (grandprixinfo.cup->windata[i].best_placement > 0x0F)
grandprixinfo.cup->windata[i].best_placement = 0x0F;
anymerit = true;
}
if (podiumData.grade >= grandprixinfo.cup->windata[i].best_grade)
{
grandprixinfo.cup->windata[i].best_grade = podiumData.grade;
anymerit = true;
}
if (podiumData.rank.specialWon == true)
{
grandprixinfo.cup->windata[i].got_emerald = true;
anymerit = true;
}
if (anymerit == true)
{
grandprixinfo.cup->windata[i].best_skin.id = podiumData.rank.skin;
grandprixinfo.cup->windata[i].best_skin.unloaded = NULL;
}
}
// Save before playing the noise
G_SaveGameData();
}
/*--------------------------------------------------
void K_CeremonyTicker(boolean run)
See header file for description.
--------------------------------------------------*/
void K_CeremonyTicker(boolean run)
{
// don't trigger if doing anything besides idling
if (gameaction != ga_nothing || gamestate != GS_CEREMONY)
{
return;
}
P_TickAltView(&titlemapcam);
if (titlemapcam.mobj != NULL)
{
camera[0].x = titlemapcam.mobj->x;
camera[0].y = titlemapcam.mobj->y;
camera[0].z = titlemapcam.mobj->z;
camera[0].angle = titlemapcam.mobj->angle;
camera[0].aiming = titlemapcam.mobj->pitch;
camera[0].subsector = titlemapcam.mobj->subsector;
}
if (podiumData.ranking == false)
{
return;
}
if (run == true)
{
if (podiumData.fade < 16)
{
podiumData.fade++;
}
else
{
if (podiumData.state < PODIUM_STATES)
{
podiumData.delay++;
if (podiumData.delay > TICRATE/2)
{
podiumData.state++;
podiumData.delay = 0;
}
}
else if (podiumData.delay == TICRATE)
{
if (!menuactive && M_MenuConfirmPressed(0))
{
podiumData.delay++;
}
}
else
{
if (++podiumData.delay == 2*TICRATE)
{
if (grandprixinfo.gp == true
&& grandprixinfo.cup != NULL
&& grandprixinfo.cup->playcredits == true)
{
nextmap = NEXTMAP_CREDITS;
}
else
{
nextmap = NEXTMAP_TITLE;
}
G_EndGame();
return;
}
}
}
}
}
/*--------------------------------------------------
void K_CeremonyDrawer(void)
See header file for description.
--------------------------------------------------*/
void K_CeremonyDrawer(void)
{
if (podiumData.ranking == true)
{
char gradeChar = '?';
INT32 x = 64;
INT32 y = 48;
INT32 i;
switch (podiumData.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_DrawFadeScreen(0xFF00, podiumData.fade);
for (i = 0; i <= podiumData.state; i++)
{
switch (i)
{
case 1:
{
V_DrawString(x, y, 0,
va("POS: %d / %d", podiumData.rank.position, RANK_NEUTRAL_POSITION)
);
break;
}
case 2:
{
V_DrawString(x, y, 0,
va("PTS: %d / %d", podiumData.rank.winPoints, podiumData.rank.totalPoints)
);
break;
}
case 3:
{
V_DrawString(x, y, 0,
va("LAPS: %d / %d", podiumData.rank.laps, podiumData.rank.totalLaps)
);
break;
}
case 4:
{
V_DrawString(x, y, 0,
va("CONTINUES: %d", podiumData.rank.continuesUsed)
);
break;
}
case 5:
{
V_DrawString(x, y, 0,
va("PRISONS: %d / %d", podiumData.rank.prisons, podiumData.rank.totalPrisons)
);
break;
}
case 6:
{
V_DrawString(x, y, 0,
va("RINGS: %d / %d", podiumData.rank.rings, podiumData.rank.totalRings)
);
break;
}
case 7:
{
const char *emeraldstr = "???";
if (gamedata->everseenspecial == true)
{
emeraldstr =
(grandprixinfo.gp == true
&& grandprixinfo.cup != NULL
&& grandprixinfo.cup->emeraldnum > 0)
? "EMERALD"
: "PRIZE";
}
V_DrawString(x, y, 0,
va("%s: %s",
emeraldstr,
(podiumData.rank.specialWon == true) ? "YES" : "NO")
);
break;
}
case 8:
{
V_DrawString(x, y + 10, V_YELLOWMAP,
va(" ** FINAL GRADE: %c", gradeChar)
);
break;
}
default:
{
break;
}
}
y += 10;
}
}
// See d_main.c and V_DrawCustomFadeScreen for the hacks that prevents this being here
/*if (timeinmap < 16)
{
// Level fade-in
V_DrawCustomFadeScreen(((levelfadecol == 0) ? "FADEMAP1" : "FADEMAP0"), 31-(timeinmap*2));
}*/
if (podiumData.state == PODIUM_STATES)
{
Y_DrawIntermissionButton(TICRATE - podiumData.delay, podiumData.delay - TICRATE);
}
}

1136
src/k_podium.cpp Normal file

File diff suppressed because it is too large Load diff

View file

@ -80,6 +80,7 @@ gp_rank_e K_PodiumGrade(void);
Return:-
true if the Emerald/Prize was collected during the GP, otherwise false.
--------------------------------------------------*/
boolean K_PodiumHasEmerald(void);

View file

@ -139,6 +139,7 @@ static void RankCapsules_LoadTextmap(void)
}
}
#if 0
/*--------------------------------------------------
static void RankCapsules_LoadThingsLump(UINT8 *data)
@ -175,6 +176,7 @@ static void RankCapsules_LoadThingsLump(UINT8 *data)
}
}
}
#endif
/*--------------------------------------------------
static boolean RankCapsules_LoadMapData(const virtres_t *virt)
@ -225,7 +227,11 @@ static boolean RankCapsules_LoadMapData(const virtres_t *virt)
}
else
{
#if 0
RankCapsules_LoadThingsLump(virtthings->data);
#else
CONS_Printf("binary maps SMELL!!!!!\n");
#endif
}
return true;
@ -263,15 +269,16 @@ static UINT32 RankCapsules_CountFromMap(const virtres_t *virt)
See header file for description.
--------------------------------------------------*/
void K_InitGrandPrixRank(gpRank_t *rankData)
void gpRank_t::Init(void)
{
UINT8 numHumans = 0;
UINT32 laps = 0;
INT32 i;
memset(rankData, 0, sizeof(gpRank_t));
memset(this, 0, sizeof(gpRank_t));
skin = MAXSKINS;
if (grandprixinfo.cup == NULL)
if (grandprixinfo.cup == nullptr)
{
return;
}
@ -288,20 +295,20 @@ void K_InitGrandPrixRank(gpRank_t *rankData)
}
// Calculate players
rankData->players = numHumans;
rankData->totalPlayers = K_GetGPPlayerCount(numHumans);
numPlayers = numHumans;
totalPlayers = K_GetGPPlayerCount(numHumans);
// Initialize to the neutral value.
rankData->position = RANK_NEUTRAL_POSITION;
position = RANK_NEUTRAL_POSITION;
// 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);
totalPoints += grandprixinfo.cup->numlevels * K_CalculateGPRankPoints(i + 1, totalPlayers);
}
rankData->totalRings = grandprixinfo.cup->numlevels * numHumans * 20;
totalRings = grandprixinfo.cup->numlevels * numHumans * 20;
for (i = 0; i < grandprixinfo.cup->numlevels; i++)
{
@ -315,7 +322,7 @@ void K_InitGrandPrixRank(gpRank_t *rankData)
// +1, since 1st place laps are worth 2 pts.
for (i = 0; i < numHumans+1; i++)
{
rankData->totalLaps += laps;
totalLaps += laps;
}
// Search through all of the cup's bonus levels
@ -339,42 +346,97 @@ void K_InitGrandPrixRank(gpRank_t *rankData)
continue;
}
rankData->totalPrisons += RankCapsules_CountFromMap(virt);
totalPrisons += RankCapsules_CountFromMap(virt);
vres_Free(virt);
}
}
}
void K_InitGrandPrixRank(gpRank_t *rankData)
{
rankData->Init();
}
/*--------------------------------------------------
void K_UpdateGPRank(void)
void K_UpdateGPRank(gpRank_t *rankData)
See header file for description.
--------------------------------------------------*/
void K_UpdateGPRank(void)
void gpRank_t::Update(void)
{
if (grandprixinfo.gp != true)
return;
gpRank_level_t *const lvl = &levels[numLevels];
prisons += numtargets;
position = MAXPLAYERS;
skin = MAXSKINS;
lvl->id = gamemap;
if (grandprixinfo.gp == true)
{
lvl->event = grandprixinfo.eventmode;
}
else
{
lvl->event = (gametype != GT_RACE) ? GPEVENT_BONUS : GPEVENT_NONE;
}
lvl->time = UINT32_MAX;
lvl->totalLapPoints = K_RaceLapCount(gamemap - 1) * 2;
lvl->totalPrisons = maptargets;
UINT8 i;
grandprixinfo.rank.prisons += numtargets;
grandprixinfo.rank.position = MAXPLAYERS;
grandprixinfo.rank.skin = MAXSKINS;
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i]
|| players[i].spectator == true
|| players[i].bot == true)
if (playeringame[i] == false
|| players[i].spectator == true
|| players[i].bot == true)
{
continue;
}
UINT8 podiumposition = K_GetPodiumPosition(&players[i]);
if (podiumposition >= grandprixinfo.rank.position) // port priority
continue;
player_t *const player = &players[i];
grandprixinfo.rank.position = podiumposition;
grandprixinfo.rank.skin = players[i].skin;
UINT8 podiumPosition = K_GetPodiumPosition(player);
if (podiumPosition < position) // port priority
{
position = podiumPosition;
skin = player->skin;
}
}
for (i = 0; i < numPlayers; i++)
{
if (playeringame[displayplayers[i]] == false
|| players[displayplayers[i]].spectator == true
|| players[displayplayers[i]].bot == true)
{
continue;
}
const player_t *player = &players[displayplayers[i]]; // TODO: needs looked at for online GP
gpRank_level_perplayer_t *const dta = &lvl->perPlayer[i];
if (player->realtime < lvl->time)
{
lvl->time = player->realtime;
}
dta->position = player->tally.position;
dta->rings = player->tally.rings;
dta->lapPoints = player->tally.laps;
dta->prisons = player->tally.prisons;
dta->gotSpecialPrize = !!!(player->pflags & PF_NOCONTEST);
dta->grade = static_cast<gp_rank_e>(player->tally.rank);
}
numLevels++;
}
void K_UpdateGPRank(gpRank_t *rankData)
{
rankData->Update();
}
/*--------------------------------------------------
@ -389,7 +451,7 @@ gp_rank_e K_CalculateGPGrade(gpRank_t *rankData)
if (cv_debugrank.value >= 2)
{
return GRADE_E + (cv_debugrank.value - 2);
return static_cast<gp_rank_e>(GRADE_E + (cv_debugrank.value - 2));
}
}
@ -400,7 +462,7 @@ gp_rank_e K_CalculateGPGrade(gpRank_t *rankData)
17*FRACUNIT/20 // A: 85% or higher
};
gp_rank_e retGrade = GRADE_E;
INT32 retGrade = GRADE_E;
const INT32 positionWeight = 150;
const INT32 pointsWeight = 100;
@ -459,7 +521,7 @@ gp_rank_e K_CalculateGPGrade(gpRank_t *rankData)
retGrade++;
}
return retGrade;
return static_cast<gp_rank_e>(retGrade);
}
/*--------------------------------------------------
@ -489,3 +551,32 @@ UINT16 K_GetGradeColor(gp_rank_e grade)
return SKINCOLOR_NONE;
}
/*--------------------------------------------------
char K_GetGradeChar(gp_rank_e grade)
See header file for description.
--------------------------------------------------*/
char K_GetGradeChar(gp_rank_e grade)
{
switch (grade)
{
case GRADE_E:
return 'E';
case GRADE_D:
return 'D';
case GRADE_C:
return 'C';
case GRADE_B:
return 'B';
case GRADE_A:
return 'A';
case GRADE_S:
return 'S';
default:
break;
}
return '?';
}

View file

@ -16,14 +16,30 @@
#include "doomdef.h"
#include "doomstat.h"
#ifdef __cplusplus
extern "C" {
#endif
// Please also see P_ArchiveMisc
struct gpRank_level_perplayer_t
{
UINT8 position;
UINT8 rings;
UINT16 lapPoints;
UINT16 prisons;
boolean gotSpecialPrize;
gp_rank_e grade;
};
struct gpRank_level_t
{
UINT16 id;
INT32 event;
UINT32 time;
UINT16 totalLapPoints;
UINT16 totalPrisons;
gpRank_level_perplayer_t perPlayer[MAXSPLITSCREENPLAYERS];
};
struct gpRank_t
{
UINT8 players;
UINT8 numPlayers;
UINT8 totalPlayers;
UINT8 position;
@ -44,8 +60,20 @@ struct gpRank_t
UINT32 totalRings;
boolean specialWon;
UINT8 numLevels;
gpRank_level_t levels[8];
#ifdef __cplusplus
void Init(void);
void Update(void);
#endif
};
#ifdef __cplusplus
extern "C" {
#endif
// gp_rank_e was once defined here, but moved to doomstat.h to prevent circular dependency
// 3rd place is neutral, anything below is a penalty
@ -68,18 +96,13 @@ void K_InitGrandPrixRank(gpRank_t *rankData);
/*--------------------------------------------------
void K_UpdateGPRank(void)
void K_UpdateGPRank(gpRank_t *rankData)
Updates the best ranking across all human
players.
Input Arguments:-
N/A
Return:-
N/A
--------------------------------------------------*/
void K_UpdateGPRank(void);
void K_UpdateGPRank(gpRank_t *rankData);
/*--------------------------------------------------
@ -109,9 +132,25 @@ gp_rank_e K_CalculateGPGrade(gpRank_t *rankData);
Return:-
skincolor ID representing the achieved grade.
--------------------------------------------------*/
UINT16 K_GetGradeColor(gp_rank_e grade);
/*--------------------------------------------------
char K_GetGradeChar(gp_rank_e grade)
Maps grades to a letter for strings.
Input Arguments:-
grade - gp_rank_e representing an achieved ranking.
Return:-
ASCII character for the grade.
--------------------------------------------------*/
char K_GetGradeChar(gp_rank_e grade);
#ifdef __cplusplus
} // extern "C"
#endif

View file

@ -1337,32 +1337,9 @@ void level_tally_t::Draw(void)
|| state == TALLY_ST_GRADE_VOICE
|| state == TALLY_ST_DONE)
{
const char *grade_letter = "X";
switch (rank)
{
case GRADE_E:
grade_letter = "E";
break;
case GRADE_D:
grade_letter = "D";
break;
case GRADE_C:
grade_letter = "C";
break;
case GRADE_B:
grade_letter = "B";
break;
case GRADE_A:
grade_letter = "A";
break;
case GRADE_S:
grade_letter = "S";
break;
default:
break;
}
char grade_letter = K_GetGradeChar( static_cast<gp_rank_e>(rank) );
patch_t *grade_img = static_cast<patch_t*>( W_CachePatchName(va("R_FINR%s%s", (r_splitscreen ? "S" : "N"), grade_letter), PU_CACHE) );
patch_t *grade_img = static_cast<patch_t*>( W_CachePatchName(va("R_FINR%c%c", (r_splitscreen ? 'S' : 'N'), grade_letter), PU_CACHE) );
srb2::Draw grade_drawer = drawer
.xy(v_width * 0.5, v_height - (2.0 * frac) - (grade_img->height * 0.5))
.colormap( static_cast<skincolornum_t>(K_GetGradeColor( static_cast<gp_rank_e>(rank) )) );

View file

@ -1087,7 +1087,17 @@ static void P_AddBrokenPrison(mobj_t *target, mobj_t *inflictor, mobj_t *source)
S_StartSound(NULL, sfx_s221);
if (timelimitintics)
{
extratimeintics += 10*TICRATE;
UINT16 bonustime = 10*TICRATE;
if (grandprixinfo.gp)
{
if (grandprixinfo.masterbots)
bonustime = 8*TICRATE;
else if (grandprixinfo.gamespeed == KARTSPEED_EASY)
bonustime = 15*TICRATE;
}
extratimeintics += bonustime;
secretextratime = TICRATE/2;
}
@ -2495,6 +2505,11 @@ static boolean P_KillPlayer(player_t *player, mobj_t *inflictor, mobj_t *source,
P_SetPlayerMobjState(player->mo, player->mo->info->deathstate);
if (player->sliptideZipIndicator && !P_MobjWasRemoved(player->sliptideZipIndicator))
P_RemoveMobj(player->sliptideZipIndicator);
if (player->stumbleIndicator && !P_MobjWasRemoved(player->stumbleIndicator))
P_RemoveMobj(player->stumbleIndicator);
if (type == DMG_TIMEOVER)
{
if (gametyperules & GTR_CIRCUIT)

View file

@ -5747,7 +5747,7 @@ static inline void P_ArchiveMisc(savebuffer_t *save)
// Rank information
{
WRITEUINT8(save->p, grandprixinfo.rank.players);
WRITEUINT8(save->p, grandprixinfo.rank.numPlayers);
WRITEUINT8(save->p, grandprixinfo.rank.totalPlayers);
WRITEUINT8(save->p, grandprixinfo.rank.position);
@ -5768,6 +5768,19 @@ static inline void P_ArchiveMisc(savebuffer_t *save)
WRITEUINT32(save->p, grandprixinfo.rank.totalRings);
WRITEUINT8(save->p, (UINT8)grandprixinfo.rank.specialWon);
/*
WRITEUINT8(save->p, grandprixinfo.rank.numLevels);
for (i = 0; i < grandprixinfo.rank.stages; i++)
{
UINT8 j;
for (j = 0; j < grandprixinfo.rank.numPlayers; j++)
{
}
}
*/
}
// Marathon information
@ -5910,7 +5923,7 @@ static boolean P_UnArchiveSPGame(savebuffer_t *save)
// Rank information
{
grandprixinfo.rank.players = READUINT8(save->p);
grandprixinfo.rank.numPlayers = READUINT8(save->p);
grandprixinfo.rank.totalPlayers = READUINT8(save->p);
grandprixinfo.rank.position = READUINT8(save->p);

View file

@ -2279,6 +2279,17 @@ static void P_UpdatePlayerAngle(player_t *player)
// With a full slam on the analog stick, how far could we steer in either direction?
INT16 steeringRight = K_UpdateSteeringValue(player->steering, KART_FULLTURN);
INT16 steeringLeft = K_UpdateSteeringValue(player->steering, -KART_FULLTURN);
// When entering/leaving drifts, allow all legal turns with no easing.
// This is the hardest case for the turn solver, because your handling properties on
// client side are very different than your handling properties on server side—at least,
// until your drift status makes the full round-trip and is reflected in your gamestate.
if (player->drift && abs(player->drift) < 5)
{
steeringRight = KART_FULLTURN;
steeringLeft = -KART_FULLTURN;
}
angle_t maxTurnRight = K_GetKartTurnValue(player, steeringRight) << TICCMD_REDUCE;
angle_t maxTurnLeft = K_GetKartTurnValue(player, steeringLeft) << TICCMD_REDUCE;

View file

@ -229,6 +229,8 @@ TYPEDEF (t_floor_t);
TYPEDEF (waypoint_t);
// k_rank.h
TYPEDEF (gpRank_level_perplayer_t);
TYPEDEF (gpRank_level_t);
TYPEDEF (gpRank_t);
// k_tally.h

View file

@ -283,7 +283,10 @@ static void Y_CalculateMatchData(UINT8 rankingsmode, void (*comparison)(INT32))
{
// Okay, player scores have been set now - we can calculate GP-relevant material.
{
K_UpdateGPRank();
if (grandprixinfo.gp == true)
{
K_UpdateGPRank(&grandprixinfo.rank);
}
// See also G_GetNextMap, M_DrawPause
data.showrank = false;