diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 08fe916e9..a784102ce 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -4043,10 +4043,27 @@ static void HandleConnect(SINT8 node) UINT8 maxplayers = min((dedicated ? MAXPLAYERS-1 : MAXPLAYERS), cv_maxconnections.value); UINT8 connectedplayers = 0; - for (i = dedicated ? 1 : 0; i < MAXPLAYERS; i++) - if (playernode[i] != UINT8_MAX) // We use this to count players because it is affected by SV_AddWaitingPlayers when more than one client joins on the same tic, unlike playeringame and D_NumPlayers. UINT8_MAX denotes no node for that player + { + // We use this to count players because it is affected by SV_AddWaitingPlayers when + // more than one client joins on the same tic, unlike playeringame and D_NumPlayers. + // UINT8_MAX denotes no node for that player. + + if (playernode[i] != UINT8_MAX) + { + // Sal: This hack sucks, but it should be safe. + // playeringame is set for bots immediately; they are deterministic instead of a netxcmd. + // If a bot is added with netxcmd in the future, then the node check is still here too. + // So at worst, a theoretical netxcmd added bot will block real joiners for the time + // it takes for the command to process, but not cause any horrifying player overwriting. + if (playeringame[i] && players[i].bot) + { + continue; + } + connectedplayers++; + } + } banrecord_t *ban = SV_GetBanByAddress(node); if (ban == NULL) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 18239fccf..54de0adbe 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -7282,13 +7282,6 @@ void KartEncore_OnChange(void) void KartEliminateLast_OnChange(void); void KartEliminateLast_OnChange(void) { -#ifndef DEVELOP - if (K_CanChangeRules(false) == false) - { - CV_StealthSet(&cv_karteliminatelast, cv_karteliminatelast.defaultvalue); - } -#endif - P_CheckRacers(); } diff --git a/src/d_player.h b/src/d_player.h index 166927be9..1eb5ba6aa 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -578,6 +578,15 @@ struct altview_t INT32 tics; }; +// enum for saved lap times +typedef enum +{ + LAP_CUR, + LAP_BEST, + LAP_LAST, + LAP__MAX +} laptime_e; + extern altview_t titlemapcam; // ======================================================================== @@ -897,6 +906,7 @@ struct player_t INT16 totalring; // Total number of rings obtained for GP tic_t realtime; // integer replacement for leveltime + tic_t laptime[LAP__MAX]; UINT8 laps; // Number of laps (optional) UINT8 latestlap; UINT32 lapPoints; // Points given from laps diff --git a/src/doomstat.h b/src/doomstat.h index 20f0531ba..38902b1d1 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -871,7 +871,6 @@ extern boolean inDuel; extern tic_t bombflashtimer; // Used to avoid causing seizures if multiple mines explode close to you :) extern boolean legitimateexit; extern boolean comebackshowninfo; -extern tic_t curlap, bestlap; #define VOTE_SPECIAL (MAXPLAYERS) #define VOTE_TOTAL (MAXPLAYERS+1) diff --git a/src/g_game.c b/src/g_game.c index 56b3538a9..8b509fd67 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -147,6 +147,7 @@ boolean modifiedgame = false; // Set if homebrew PWAD stuff has been added. boolean majormods = false; // Set if Lua/Gameplay SOC/replacement map has been added. boolean savemoddata = false; boolean usedCheats = false; // Set when a "cheats on" is ever used. +boolean usedTourney = false; // Entered the "Tournament Mode" cheat. UINT8 paused; UINT8 modeattacking = ATTACKING_NONE; boolean imcontinuing = false; @@ -322,8 +323,6 @@ boolean inDuel; // Boolean, keeps track of if it is a 1v1 tic_t bombflashtimer = 0; // Cooldown before another FlashPal can be intialized by a bomb exploding near a displayplayer. Avoids seizures. boolean legitimateexit; // Did this client actually finish the match? boolean comebackshowninfo; // Have you already seen the "ATTACK OR PROTECT" message? -tic_t curlap; // Current lap time -tic_t bestlap; // Best lap time boolean precache = true; // if true, load all graphics at start @@ -344,14 +343,10 @@ void G_ClearRecords(void) { UINT16 i; - for (i = 0; i < numskins; i++) - { - memset(&skins[i].records, 0, sizeof(skins[i].records)); - } - for (i = 0; i < nummapheaders; i++) { - memset(&mapheaderinfo[i]->records, 0, sizeof(recorddata_t)); + memset(&mapheaderinfo[i]->records.timeattack, 0, sizeof(recordtimes_t)); + memset(&mapheaderinfo[i]->records.spbattack, 0, sizeof(recordtimes_t)); } cupheader_t *cup; @@ -360,14 +355,11 @@ void G_ClearRecords(void) memset(&cup->windata, 0, sizeof(cup->windata)); } - unloaded_skin_t *unloadedskin, *nextunloadedskin = NULL; - for (unloadedskin = unloadedskins; unloadedskin; unloadedskin = nextunloadedskin) - { - nextunloadedskin = unloadedskin->next; - Z_Free(unloadedskin); - } - unloadedskins = NULL; - + // TODO: Technically, these should only remove time attack records here. + // But I'm out of juice for dev (+ literally, just finished some OJ). + // The stats need to be cleared in M_ClearStats, and I guess there's + // no perfect place to wipe mapvisited because it's not actually part of + // basegame progression... so here's fine for launch. ~toast 100424 unloaded_mapheader_t *unloadedmap, *nextunloadedmap = NULL; for (unloadedmap = unloadedmapheaders; unloadedmap; unloadedmap = nextunloadedmap) { @@ -561,8 +553,8 @@ void G_UpdateRecords(void) if (modeattacking & ATTACKING_LAP) { - if ((record->lap == 0) || (bestlap < record->lap)) - record->lap = bestlap; + if ((record->lap == 0) || (players[consoleplayer].laptime[LAP_BEST] < record->lap)) + record->lap = players[consoleplayer].laptime[LAP_BEST]; } // Check emblems when level data is updated @@ -606,7 +598,7 @@ static void G_UpdateRecordReplays(void) // Save demo! bestdemo[255] = '\0'; lastdemo[255] = '\0'; - G_SetDemoTime(players[consoleplayer].realtime, bestlap); + G_SetDemoTime(players[consoleplayer].realtime, players[consoleplayer].laptime[LAP_BEST]); G_CheckDemoStatus(); gpath = va("%s"PATHSEP"media"PATHSEP"replay"PATHSEP"%s", @@ -2179,6 +2171,10 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) level_tally_t tally; boolean tallyactive; + tic_t laptime[LAP__MAX]; + + INT32 i; + // This needs to be first, to permit it to wipe extra information jointime = players[player].jointime; if (jointime <= 1) @@ -2206,6 +2202,11 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) kartspeed = skins[players[player].skin].kartspeed; kartweight = skins[players[player].skin].kartweight; charflags = skins[players[player].skin].flags; + + for (i = 0; i < LAP__MAX; i++) + { + laptime[i] = 0; + } } else { @@ -2218,6 +2219,11 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) kartweight = players[player].kartweight; charflags = (skinflags & SF_IRONMAN) ? skinflags : players[player].charflags; + + for (i = 0; i < LAP__MAX; i++) + { + laptime[i] = players[player].laptime[i]; + } } lastfakeskin = players[player].lastfakeskin; @@ -2456,6 +2462,11 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) p->lapPoints = lapPoints; p->totalring = totalring; + for (i = 0; i < LAP__MAX; i++) + { + p->laptime[i] = laptime[i]; + } + p->bot = bot; p->botvars.difficulty = botdifficulty; p->rings = rings; @@ -2519,8 +2530,6 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) // Check to make sure their color didn't change somehow... if (G_GametypeHasTeams()) { - UINT8 i; - if (p->ctfteam == 1 && p->skincolor != skincolor_redteam) { for (i = 0; i <= splitscreen; i++) diff --git a/src/g_game.h b/src/g_game.h index 16676cf33..335294950 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -84,6 +84,8 @@ extern tic_t timeinmap; // Ticker for time spent in level (used for levelcard di extern INT32 pausedelay; extern boolean pausebreakkey; +extern boolean usedTourney; + extern boolean promptactive; extern consvar_t cv_tutorialprompt; diff --git a/src/k_battle.c b/src/k_battle.c index 05bc34b57..5e1b51a01 100644 --- a/src/k_battle.c +++ b/src/k_battle.c @@ -60,7 +60,7 @@ INT32 K_StartingBumperCount(void) if (tutorialchallenge == TUTORIALSKIP_INPROGRESS) return 0; - if (battleprisons || K_CheckBossIntro()) + if (battleprisons || K_CheckBossIntro() || !K_CanChangeRules(true)) { if (grandprixinfo.gp) { diff --git a/src/k_hud.cpp b/src/k_hud.cpp index 62e26ab6c..b25c79fa7 100644 --- a/src/k_hud.cpp +++ b/src/k_hud.cpp @@ -1889,7 +1889,7 @@ tic_t K_TranslateTimer(tic_t drawtime, UINT8 mode, INT32 *return_jitter) { INT32 jitter = 0; - if (!mode) + if (!mode && drawtime != UINT32_MAX) { if (timelimitintics > 0) { @@ -1948,7 +1948,6 @@ void K_drawKartTimestamp(tic_t drawtime, INT32 TX, INT32 TY, INT32 splitflags, U // TIME_X = BASEVIDWIDTH-124; // 196 // TIME_Y = 6; // 6 - tic_t worktime; INT32 jitter = 0; drawtime = K_TranslateTimer(drawtime, mode, &jitter); @@ -1957,19 +1956,27 @@ void K_drawKartTimestamp(tic_t drawtime, INT32 TX, INT32 TY, INT32 splitflags, U TX += 33; - worktime = drawtime/(60*TICRATE); - - if (worktime >= 100) + if (drawtime == UINT32_MAX) + ; + else if (mode && !drawtime) { - jitter = (drawtime & 1 ? 1 : -1); - worktime = 99; - drawtime = (100*(60*TICRATE))-1; - } + // apostrophe location _'__ __ + V_DrawTimerString(TX+24, TY+3, splitflags, va("'")); - if (mode && !drawtime) - V_DrawTimerString(TX, TY+3, splitflags, "--'--\"--"); + // quotation mark location _ __"__ + V_DrawTimerString(TX+60, TY+3, splitflags, va("\"")); + } else { + tic_t worktime = drawtime/(60*TICRATE); + + if (worktime >= 100) + { + jitter = (drawtime & 1 ? 1 : -1); + worktime = 99; + drawtime = (100*(60*TICRATE))-1; + } + // minutes time 00 __ __ V_DrawTimerString(TX, TY+3+jitter, splitflags, va("%d", worktime/10)); V_DrawTimerString(TX+12, TY+3-jitter, splitflags, va("%d", worktime%10)); @@ -6089,7 +6096,26 @@ void K_drawKartHUD(void) { bool ta = modeattacking && !demo.playback; INT32 flags = V_HUDTRANS|V_SLIDEIN|V_SNAPTOTOP|V_SNAPTORIGHT; - K_drawKartTimestamp(stplyr->realtime, TIME_X, TIME_Y + (ta ? 2 : 0), flags, 0); + + tic_t realtime = stplyr->realtime; + + if (stplyr->karthud[khud_lapanimation] + && !stplyr->exiting + && stplyr->laptime[LAP_LAST] != 0 + && stplyr->laptime[LAP_LAST] != UINT32_MAX) + { + if ((stplyr->karthud[khud_lapanimation] / 5) & 1) + { + realtime = stplyr->laptime[LAP_LAST]; + } + else + { + realtime = UINT32_MAX; + } + } + + K_drawKartTimestamp(realtime, TIME_X, TIME_Y + (ta ? 2 : 0), flags, 0); + if (modeattacking) { if (ta) diff --git a/src/k_kart.c b/src/k_kart.c index f96710a29..d5e0c23a3 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -72,6 +72,16 @@ // comeback is Battle Mode's karma comeback, also bool // mapreset is set when enough players fill an empty server +boolean K_ThunderDome(void) +{ + if (K_CanChangeRules(true)) + { + return (boolean)cv_thunderdome.value; + } + + return false; +} + // lat: used for when the player is in some weird state where it wouldn't be wise for it to be overwritten by another object that does similarly wacky shit. boolean K_isPlayerInSpecialState(player_t *p) { @@ -12181,7 +12191,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) if (player->ringboxdelay == 0) { UINT32 award = 5*player->ringboxaward + 10; - if (!cv_thunderdome.value) + if (!K_ThunderDome()) award = 3 * award / 2; if (modeattacking & ATTACKING_SPB) @@ -13559,6 +13569,7 @@ void K_CheckSpectateStatus(boolean considermapreset) { UINT8 respawnlist[MAXPLAYERS]; UINT8 i, j, numingame = 0, numjoiners = 0; + UINT8 numhumans = 0, numbots = 0; // Maintain spectate wait timer for (i = 0; i < MAXPLAYERS; i++) @@ -13571,6 +13582,16 @@ void K_CheckSpectateStatus(boolean considermapreset) if (!players[i].spectator) { numingame++; + + if (players[i].bot) + { + numbots++; + } + else + { + numhumans++; + } + players[i].spectatewait = 0; players[i].spectatorReentry = 0; continue; @@ -13600,7 +13621,7 @@ void K_CheckSpectateStatus(boolean considermapreset) return; // DON'T allow if you've hit the in-game player cap - if (cv_maxplayers.value && numingame >= cv_maxplayers.value) + if (cv_maxplayers.value && numhumans >= cv_maxplayers.value) return; // Get the number of players in game, and the players to be de-spectated. @@ -13684,16 +13705,47 @@ void K_CheckSpectateStatus(boolean considermapreset) } } + const UINT8 previngame = numingame; + INT16 removeBotID = MAXPLAYERS - 1; + // Finally, we can de-spectate everyone! for (i = 0; i < numjoiners; i++) { - //CONS_Printf("player %s is joining on tic %d\n", player_names[respawnlist[i]], leveltime); - - P_SpectatorJoinGame(&players[respawnlist[i]]); - // Hit the in-game player cap while adding people? - if (cv_maxplayers.value && numingame+i >= cv_maxplayers.value) - break; + if (cv_maxplayers.value && numingame >= cv_maxplayers.value) + { + if (numbots > 0) + { + // Find a bot to kill to make room + while (removeBotID >= 0) + { + if (playeringame[removeBotID] && players[removeBotID].bot) + { + //CONS_Printf("bot %s kicked to make room on tic %d\n", player_names[removeBotID], leveltime); + CL_RemovePlayer(removeBotID, KR_LEAVE); + numbots--; + numingame--; + break; + } + + removeBotID--; + } + + if (removeBotID < 0) + { + break; + } + } + else + { + break; + } + } + + //CONS_Printf("player %s is joining on tic %d\n", player_names[respawnlist[i]], leveltime); + P_SpectatorJoinGame(&players[respawnlist[i]]); + numhumans++; + numingame++; } if (considermapreset == false) @@ -13702,7 +13754,7 @@ void K_CheckSpectateStatus(boolean considermapreset) // Reset the match when 2P joins 1P, DUEL mode // Reset the match when 3P joins 1P and 2P, DUEL mode must be disabled extern consvar_t cv_debugnewchallenger; - if (i > 0 && !mapreset && gamestate == GS_LEVEL && (numingame < 3 && numingame+i >= 2) && !cv_debugnewchallenger.value) + if (i > 0 && !mapreset && gamestate == GS_LEVEL && (previngame < 3 && numingame >= 2) && !cv_debugnewchallenger.value) { Music_Play("comeon"); // COME ON mapreset = 3*TICRATE; // Even though only the server uses this for game logic, set for everyone for HUD diff --git a/src/k_kart.h b/src/k_kart.h index 0c3ceb41e..eec786f93 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -274,6 +274,8 @@ void K_MakeObjectReappear(mobj_t *mo); void K_BumperInflate(player_t *player); +boolean K_ThunderDome(void); + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/k_menu.h b/src/k_menu.h index bc2a95f31..699831642 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -478,7 +478,6 @@ typedef enum { gopt_spacer0 = 0, gopt_gamespeed, - gopt_baselapcount, gopt_frantic, gopt_encore, gopt_exitcountdown, diff --git a/src/k_podium.cpp b/src/k_podium.cpp index d0539614b..497db18df 100644 --- a/src/k_podium.cpp +++ b/src/k_podium.cpp @@ -640,12 +640,15 @@ void podiumData_s::Draw(void) if (emeraldNum == 0) { - emeraldPatch = static_cast( W_CachePatchName("K_BLNA", PU_CACHE) ); + // Prize -- todo, currently using fake Emerald + emeraldColor = SKINCOLOR_GOLD; } else { emeraldColor = static_cast( SKINCOLOR_CHAOSEMERALD1 + ((emeraldNum - 1) % 7) ); + } + { std::string emeraldName; if (emeraldNum > 7) { @@ -853,12 +856,15 @@ void podiumData_s::Draw(void) if (emeraldNum == 0) { - emeraldOverlay = static_cast( W_CachePatchName("KBLNC0", PU_CACHE) ); + // Prize -- todo, currently using fake Emerald + emeraldColor = SKINCOLOR_GOLD; } else { emeraldColor = static_cast( SKINCOLOR_CHAOSEMERALD1 + ((emeraldNum - 1) % 7) ); + } + { if (emeraldNum > 7) { emeraldOverlay = static_cast( W_CachePatchName("SEMRA0", PU_CACHE) ); diff --git a/src/m_cond.c b/src/m_cond.c index f4d5422a6..cfe396886 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -650,7 +650,8 @@ void M_ClearConditionSet(UINT16 set) // Clear ALL secrets. void M_ClearStats(void) { - UINT8 i; + UINT16 i; + gamedata->totalplaytime = 0; gamedata->totalnetgametime = 0; gamedata->timeattackingtotaltime = 0; @@ -678,6 +679,54 @@ void M_ClearStats(void) gamedata->musicstate = GDMUSIC_NONE; gamedata->importprofilewins = false; + + // Skins only store stats, not progression metrics. Good to clear entirely here. + + for (i = 0; i < numskins; i++) + { + memset(&skins[i].records, 0, sizeof(skins[i].records)); + } + + unloaded_skin_t *unloadedskin, *nextunloadedskin = NULL; + for (unloadedskin = unloadedskins; unloadedskin; unloadedskin = nextunloadedskin) + { + nextunloadedskin = unloadedskin->next; + Z_Free(unloadedskin); + } + unloadedskins = NULL; + + // We retain exclusively the most important stuff from maps. + + UINT8 restoremapvisited; + recordtimes_t restoretimeattack; + recordtimes_t restorespbattack; + + for (i = 0; i < nummapheaders; i++) + { + restoremapvisited = mapheaderinfo[i]->records.mapvisited; + restoretimeattack = mapheaderinfo[i]->records.timeattack; + restorespbattack = mapheaderinfo[i]->records.spbattack; + + memset(&mapheaderinfo[i]->records, 0, sizeof(recorddata_t)); + + mapheaderinfo[i]->records.mapvisited = restoremapvisited; + mapheaderinfo[i]->records.timeattack = restoretimeattack; + mapheaderinfo[i]->records.spbattack = restorespbattack; + } + + unloaded_mapheader_t *unloadedmap; + for (unloadedmap = unloadedmapheaders; unloadedmap; unloadedmap = unloadedmap->next) + { + restoremapvisited = unloadedmap->records.mapvisited; + restoretimeattack = unloadedmap->records.timeattack; + restorespbattack = unloadedmap->records.spbattack; + + memset(&unloadedmap->records, 0, sizeof(recorddata_t)); + + unloadedmap->records.mapvisited = restoremapvisited; + unloadedmap->records.timeattack = restoretimeattack; + unloadedmap->records.spbattack = restorespbattack; + } } void M_ClearSecrets(void) @@ -707,6 +756,8 @@ void M_ClearSecrets(void) if (!mapheaderinfo[i]) continue; + mapheaderinfo[i]->records.mapvisited = 0; + mapheaderinfo[i]->cache_spraycan = UINT16_MAX; mapheaderinfo[i]->cache_maplock = MAXUNLOCKABLES; @@ -2972,6 +3023,11 @@ char *M_BuildConditionSetString(UINT16 unlockid) } } + if (usedTourney && unlockables[unlockid].conditionset == 55 && gamedata->unlocked[unlockid] == false) + { + strcpy(message, "Power shrouds this challenge in darkness... (Return here without Tournament Mode!)\0"); + } + // Finally, do a clean wordwrap! return V_ScaledWordWrap( DESCRIPTIONWIDTH << FRACBITS, diff --git a/src/m_pw.cpp b/src/m_pw.cpp index b49834b3e..894492932 100644 --- a/src/m_pw.cpp +++ b/src/m_pw.cpp @@ -110,6 +110,8 @@ void f_tournament() { if (!unlockables[i].conditionset) continue; + if (unlockables[i].conditionset == 55) + continue; if (gamedata->unlocked[i]) continue; @@ -146,10 +148,12 @@ void f_tournament() S_StartSound(0, sfx_kc42); text = M_GetText( - "All challenges temporarily unlocked.\n" + "Unlocked\x83 almost\x80 everything.\n" "Saving is disabled - the game will\n" "return to normal on next launch.\n" ); + + usedTourney = true; } else { @@ -158,18 +162,20 @@ void f_tournament() if (usedCheats) { text = M_GetText( - "This is the correct password, but\n" - "you already have every challenge\n" - "unlocked, so nothing has changed.\n" + "This is the correct password,\n" + "but there's nothing to unlock\n" + "right now -- nothing has changed.\n" ); } else { text = M_GetText( "This is the correct password, but\n" - "you already have every challenge\n" - "unlocked, so saving is still allowed!\n" + "there's nothing to unlock right\n" + "now, so saving is still allowed!\n" ); + + usedTourney = true; } } @@ -308,7 +314,7 @@ try_password_e M_TryPassword(const char *password, boolean conditions) add_job(pw.hash_.data(), &pw); // Only consider challenges passwords as needed. - if (conditions) + if (conditions && !usedTourney) iter_conditions([&](condition_t* cn) { add_job((const UINT8*)cn->stringvar, cn); }); try_password_e return_code = M_PW_INVALID; diff --git a/src/menus/extras-challenges.c b/src/menus/extras-challenges.c index dc488f953..b6d48f296 100644 --- a/src/menus/extras-challenges.c +++ b/src/menus/extras-challenges.c @@ -847,7 +847,7 @@ boolean M_ChallengesInputs(INT32 ch) { M_ChallengesTutorial(CCTUTORIAL_MAJORSKIP); } - else if (M_CanKeyHiliTile()) + else if (M_CanKeyHiliTile() && !usedTourney) { challengesmenu.chaokeyhold = 1; } diff --git a/src/menus/options-gameplay-1.c b/src/menus/options-gameplay-1.c index 96f992c0e..fc81174de 100644 --- a/src/menus/options-gameplay-1.c +++ b/src/menus/options-gameplay-1.c @@ -21,9 +21,6 @@ menuitem_t OPTIONS_Gameplay[] = {IT_STRING | IT_CVAR, "Game Speed", "Gear for the next map.", NULL, {.cvar = &cv_kartspeed}, 0, 0}, - {IT_STRING | IT_CVAR, "Base Lap Count", "How many laps must be completed per race.", - NULL, {.cvar = &cv_numlaps}, 0, 0}, - {IT_STRING | IT_CVAR, "Frantic Items", "Make item odds crazier with more powerful items!", NULL, {.cvar = &cv_kartfrantic}, 0, 0}, diff --git a/src/objects/random-item.c b/src/objects/random-item.c index 72ff9557d..b6e79aad5 100644 --- a/src/objects/random-item.c +++ b/src/objects/random-item.c @@ -19,6 +19,7 @@ #include "../k_battle.h" #include "../m_random.h" #include "../k_specialstage.h" // specialstageinfo +#include "../k_kart.h" #define FLOAT_HEIGHT ( 12 * FRACUNIT ) #define FLOAT_TIME ( 2 * TICRATE ) @@ -122,7 +123,7 @@ void Obj_RandomItemVisuals(mobj_t *mobj) // the player's cleared out a good portion of the map. // // Then extraval1 starts ticking up and triggers the transformation from Ringbox to Random Item. - if (mobj->fuse == 0 && !(mobj->flags & MF_NOCLIPTHING) && !(mobj->flags2 & MF2_BOSSDEAD) && !cv_thunderdome.value + if (mobj->fuse == 0 && !(mobj->flags & MF_NOCLIPTHING) && !(mobj->flags2 & MF2_BOSSDEAD) && !K_ThunderDome() && (modeattacking == ATTACKING_NONE || !!(modeattacking & ATTACKING_SPB) || specialstageinfo.valid)) // Time Attacking in Special is a fucked-looking exception { mobj->extravalue1++; diff --git a/src/objects/ufo.c b/src/objects/ufo.c index be3be399f..dd1e8958e 100644 --- a/src/objects/ufo.c +++ b/src/objects/ufo.c @@ -1244,9 +1244,10 @@ static mobj_t *InitSpecialUFO(waypoint_t *start) } else { - // Prize -- todo, currently using standard Emerald + // Prize -- todo, currently using fake Emerald P_SetMobjState(emerald, S_CHAOSEMERALD1); P_SetMobjState(overlay, S_CHAOSEMERALD_UNDER); + emerald->color = SKINCOLOR_GOLD; } P_SetTarget(&emerald->target, ufo); diff --git a/src/p_inter.c b/src/p_inter.c index 55ec1a6fc..dc51f2d84 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1525,7 +1525,7 @@ boolean P_CheckRacers(void) { const boolean griefed = (spectateGriefed > 0); - boolean eliminateLast = cv_karteliminatelast.value; + boolean eliminateLast = (!K_CanChangeRules(true) || (cv_karteliminatelast.value != 0)); boolean allHumansDone = true; //boolean allBotsDone = true; diff --git a/src/p_saveg.c b/src/p_saveg.c index 90e575b19..a9a22ac14 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -274,6 +274,10 @@ static void P_NetArchivePlayers(savebuffer_t *save) WRITEINT16(save->p, players[i].totalring); WRITEUINT32(save->p, players[i].realtime); + for (j = 0; j < LAP__MAX; j++) + { + WRITEUINT32(save->p, players[i].laptime[j]); + } WRITEUINT8(save->p, players[i].laps); WRITEUINT8(save->p, players[i].latestlap); WRITEUINT32(save->p, players[i].lapPoints); @@ -910,6 +914,10 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) players[i].totalring = READINT16(save->p); // Total number of rings obtained for GP players[i].realtime = READUINT32(save->p); // integer replacement for leveltime + for (j = 0; j < LAP__MAX; j++) + { + players[i].laptime[j] = READUINT32(save->p); + } players[i].laps = READUINT8(save->p); // Number of laps (optional) players[i].latestlap = READUINT8(save->p); players[i].lapPoints = READUINT32(save->p); diff --git a/src/p_setup.cpp b/src/p_setup.cpp index 5b78c9b80..548bdf914 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -7675,7 +7675,6 @@ static void P_InitLevelSettings(void) } racecountdown = exitcountdown = musiccountdown = exitfadestarted = 0; - curlap = bestlap = 0; // SRB2Kart g_exit.losing = false; g_exit.retry = false; diff --git a/src/p_spec.c b/src/p_spec.c index 68356d7ca..f7f46b4e0 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -2094,16 +2094,14 @@ static void K_HandleLapIncrement(player_t *player) if (player->laps > 1) { // save best lap for record attack - if (modeattacking && player == &players[consoleplayer]) + if (player->laptime[LAP_CUR] < player->laptime[LAP_BEST] || player->laptime[LAP_BEST] == 0) { - if (curlap < bestlap || bestlap == 0) - { - bestlap = curlap; - } - - curlap = 0; + player->laptime[LAP_BEST] = player->laptime[LAP_CUR]; } + player->laptime[LAP_LAST] = player->laptime[LAP_CUR]; + player->laptime[LAP_CUR] = 0; + // Update power levels for this lap. K_UpdatePowerLevels(player, player->laps, false); @@ -2223,7 +2221,7 @@ static void K_HandleLapDecrement(player_t *player) player->cheatchecknum = numcheatchecks; player->laps--; K_UpdateAllPlayerPositions(); - curlap = UINT32_MAX; + player->laptime[LAP_CUR] = UINT32_MAX; } } } diff --git a/src/p_user.c b/src/p_user.c index 94451957b..1741f36f8 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -4322,19 +4322,16 @@ void P_PlayerThink(player_t *player) if (leveltime >= starttime) { player->realtime = leveltime - starttime; - if (player == &players[consoleplayer]) - { - if (player->spectator) - curlap = 0; - else if (curlap != UINT32_MAX) - curlap++; // This is too complicated to sync to realtime, just sorta hope for the best :V - } + + if (player->spectator) + player->laptime[LAP_CUR] = 0; + else if (player->laptime[LAP_CUR] != UINT32_MAX) + player->laptime[LAP_CUR]++; // This is too complicated to sync to realtime, just sorta hope for the best :V } else { player->realtime = 0; - if (player == &players[consoleplayer]) - curlap = 0; + player->laptime[LAP_CUR] = 0; } }