diff --git a/extras/ACS/BlanKart_ACS.cfg b/extras/ACS/BlanKart_ACS.cfg index 76360c0e8..ac821aedd 100644 --- a/extras/ACS/BlanKart_ACS.cfg +++ b/extras/ACS/BlanKart_ACS.cfg @@ -803,10 +803,6 @@ keywords Returns the best position of all non-CPU players.\n Intended for branching camera movement in podium maps."; - PodiumFinish = "void PodiumFinish(void)\n - Brings up final Grand Prix results screen in podium maps.\n - Does nothing in standard maps."; - SetLineRenderStyle = "void SetLineRenderStyle(int tag, int blend, fixed alpha)\n Changes the rendering of the tagged linedefs' middle textures.\n - tag: The linedef tag to change.\n @@ -849,18 +845,17 @@ keywords Ends the level. - showintermission: Whether to show the results screen. If omitted, use the default behavior."; - Music_Play = "void Music_Play(str tune, [bool onlyforactivator])\n - Play a tune. If it's already playing, restarting from the beginning.\n - - tune: The ID of the tune. Note: this is separate from the music lump.\n - - onlyforactivator: Only play the tune for the activator (if activator is a player)."; + Music_Play = "void Music_Play(str song, [bool onlyforactivator])\n + Play a song. If it's already playing, restarting from the beginning.\n + - song: The name of the song lump, without 'O_' at the beginning and without a file extension.\n + - onlyforactivator: Only play the song for the activator (if activator is a player)."; Music_StopAll = "void Music_StopAll([bool onlyforactivator])\n - Stop every tune that is currently playing.\n + Stop the music that is currently playing.\n - onlyforactivator: Only stop for the activator (if activator is a player)."; - Music_Remap = "void Music_Remap(str tune, str song, [bool onlyforactivator])\n - Change the actual song lump that a tune will play.\n - - tune: The ID of the tune.\n + Music_Remap = "void Music_Remap(str song, [bool onlyforactivator])\n + Change the song thats playing while keeping its position.\n - song: The name of the song lump, without 'O_' at the beginning and without a file extension.\n - onlyforactivator: Only remap for the activator (if activator is a player)."; @@ -869,61 +864,6 @@ keywords - fade: Time (tics) to fade between full volume and silence.\n - duration: Silent duration (tics) (not including fade in and fade out), -1 = infinite (default if omitted)."; - Freeze = "void Freeze(bool value)\n - Pauses or unpauses the level's thinkers.\n - - value: True to freeze, false to unfreeze."; - - Dialogue_SetSpeaker = "void Dialogue_SetSpeaker(str character, int sprite)\n - Display a new dialogue box, using a player skin.\n - - character: The name of the skin to use.\n - - sprite: Which frame of the TALK sprite to display."; - - Dialogue_SetCustomSpeaker = "void Dialogue_SetCustomSpeaker(str nametag, str graphic, [str color, str voice])\n - Display a new dialogue box, using a custom nametag, graphic, and voice.\n - - nametag: The name to display on the dialogue box.\n - - graphic: The name of the graphic lump to display.\n - - color: The name of a skincolor to use for the graphic. Defaults to 'None'.\n - - voice: The name of the voice sound effect to use. Defaults to 'sfx_ktalk'."; - - Dialogue_NewText = "void Dialogue_NewText(str text)\n - Set the text to start displaying on the current dialogue box.\n - - text: The contents of the dialogue box."; - - Dialogue_WaitForDismiss = "void Dialogue_WaitForDismiss(void)\n - Pause the current script until the current dialogue box\n - has been dismissed by the player."; - - Dialogue_WaitForText = "void Dialogue_WaitForText(void)\n - Pause the current script until the current dialogue box\n - finishes rendering all of its text."; - - Dialogue_NewDismissText = "void Dialogue_NewDismissText(str text)\n - Sets new text to display on the dialogue box, and then waits for\n - the player to dismiss it. This is exactly equivalent to calling\n - Dialogue_NewText and then Dialogue_WaitForDismiss immediately after.\n - - text: The contents of the dialogue box."; - - Dialogue_AutoDismiss = "void Dialogue_AutoDismiss(void)\n - Dismisses the current dialogue (including from other threads)."; - - AddMessage = "void AddMessage(str message, bool interrupt, bool persist)\n - Display a message at the top of every player's HUD.\n - - message: Text of the message.\n - - interrupt: True to interrupt other messages, False to display when they're done.\n - - persist: True to last forever (Tutorial objectives), False to disappear after a time limit."; - - AddMessageForPlayer = "void AddMessageForPlayer(str message, bool interrupt, bool persist)\n - Display a message at the top of the triggering player's HUD.\n - - message: Text of the message.\n - - interrupt: True to interrupt other messages, False to display when they're done.\n - - persist: True to last forever (Tutorial objectives), False to disappear after a time limit."; - - ClearPersistentMessages = "void ClearPersistentMessages(void)\n - Remove all HUD messages (AddMessage, AddMessageForPlayer) that are set to persist, for all players.\n"; - - ClearPersistentMessagesForPlayer = "void ClearPersistentMessagesForPlayer(void)\n - Remove the triggering player's HUD messages (AddMessage, AddMessageForPlayer) that are set to persist.\n"; - FinishLine = "void FinishLine([bool flip])\n Increments the current lap of the activating player, like when crossing the finish line.\n If called from an activating line and from the wrong side, then it will decrement a lap instead.\n @@ -949,9 +889,6 @@ keywords - BOT_CONTROLLER_FASTFALL: Bots will try to fastfall when they're in the air.\n - forcedir: Force the bots to drive in an exact angle. Requires BOT_CONTROLLER_FORCEDIR to be set."; - DismountFlyingObject = "void DismountFlyingObject(void)\n - Makes the activator player dismount their Dead Line Rocket or Rideroid."; - GetLineProperty = "any GetLineProperty(int tag, int property)\n Gets the value of a line property directly.\n - tag: The line tag to retrieve the property from. 0 uses the activating line, if it exists.\n diff --git a/extras/ACS/lib/inc/ACS/bkspecial.acs b/extras/ACS/lib/inc/ACS/bkspecial.acs index 8c7df628b..997176af2 100644 --- a/extras/ACS/lib/inc/ACS/bkspecial.acs +++ b/extras/ACS/lib/inc/ACS/bkspecial.acs @@ -298,7 +298,7 @@ special void -500:CameraWait(1, int), int -501:PodiumPosition(0), - void -502:PodiumFinish(0), + //void -502:PodiumFinish(0), void -503:SetLineRenderStyle(3, int, int, fixed), void -504:MapWarp(2, str, bool), int -505:AddBot(0, str, int, int), diff --git a/src/acs/call-funcs.cpp b/src/acs/call-funcs.cpp index f1d25ee7b..d941898b7 100644 --- a/src/acs/call-funcs.cpp +++ b/src/acs/call-funcs.cpp @@ -3189,7 +3189,7 @@ bool CallFunc_ExitLevel(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::W /*-------------------------------------------------- bool CallFunc_MusicPlay(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) - Play a tune. If it's already playing, restart from the + Play a song. If it's already playing, restart from the beginning. --------------------------------------------------*/ bool CallFunc_MusicPlay(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) @@ -3204,7 +3204,7 @@ bool CallFunc_MusicPlay(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::W return false; } - S_ChangeMusicEx(map->getString(argV[0])->str, 0, false, mapmusposition, 0, 0); + S_ChangeMusicEx(map->getString(argV[0])->str, 0, true, mapmusposition, 0, 0); return false; } @@ -3232,22 +3232,22 @@ bool CallFunc_MusicStopAll(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM /*-------------------------------------------------- bool CallFunc_MusicRemap(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) - Change the actual song lump that a tune will play. + Change the song while keeping the same music posititon. --------------------------------------------------*/ bool CallFunc_MusicRemap(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) { ACSVM::MapScope *map = thread->scopeMap; + UINT32 lastmapmusposition = mapmusposition; - // 0: str tune - id for the tune to play - // 1: str song - lump name for the song to map to - // 2: [bool foractivator] - only do this if the activator is a player and is being viewed + // 0: str song - lump name for the song to map to + // 1: [bool foractivator] - only do this if the activator is a player and is being viewed - if (argC > 2 && argV[2] && !ACS_ActivatorIsLocal(thread)) + if (argC > 1 && argV[1] && !ACS_ActivatorIsLocal(thread)) { return false; } - S_ChangeMusicEx(map->getString(argV[1])->str, 0, false, mapmusposition, 0, 0); + S_ChangeMusicEx(map->getString(argV[0])->str, 0, true, lastmapmusposition, 0, 0); return false; } diff --git a/src/acs/interface.cpp b/src/acs/interface.cpp index 638e87957..13a53c04e 100644 --- a/src/acs/interface.cpp +++ b/src/acs/interface.cpp @@ -144,15 +144,10 @@ void ACS_LoadLevelScripts(size_t mapID) // Insert BEHAVIOR lump into the list. { - - static const char *maplumpname; - - maplumpname = G_BuildMapName(gamemap); - ACSVM::ModuleName name = ACSVM::ModuleName( - env->getString( maplumpname ), + env->getString( mapheaderinfo[mapID]->lumpname ), nullptr, - W_CheckNumForMap(maplumpname) + mapheaderinfo[mapID]->lumpnum ); modules.push_back(env->getModule(name)); @@ -275,11 +270,11 @@ void ACS_RunLapScript(mobj_t *mo, line_t *line) } /*-------------------------------------------------- - void ACS_RunPositionScript(void) + void ACS_RunPlayerFinishScript(player_t *player) See header file for description. --------------------------------------------------*/ -void ACS_RunPositionScript(void) +void ACS_RunPlayerFinishScript(player_t *player) { Environment *env = &ACSEnv; @@ -287,7 +282,30 @@ void ACS_RunPositionScript(void) ACSVM::HubScope *const hub = global->getHubScope(0); ACSVM::MapScope *const map = hub->getMapScope(0); - map->scriptStartType(ACS_ST_POSITION, {}); + ACSVM::MapScope::ScriptStartInfo scriptInfo; + ThreadInfo info; + + P_SetTarget(&info.mo, player->mo); + + scriptInfo.info = &info; + + map->scriptStartTypeForced(ACS_ST_FINISH, scriptInfo); +} + +/*-------------------------------------------------- + void ACS_RunRaceStartScript(void) + + See header file for description. +--------------------------------------------------*/ +void ACS_RunRaceStartScript(void) +{ + Environment *env = &ACSEnv; + + ACSVM::GlobalScope *const global = env->getGlobalScope(0); + ACSVM::HubScope *const hub = global->getHubScope(0); + ACSVM::MapScope *const map = hub->getMapScope(0); + + map->scriptStartType(ACS_ST_RACESTART, {}); } /*-------------------------------------------------- @@ -306,29 +324,6 @@ void ACS_RunOvertimeScript(void) map->scriptStartType(ACS_ST_OVERTIME, {}); } -/*-------------------------------------------------- - void ACS_RunEmeraldScript(mobj_t *mo) - - See header file for description. ---------------------------------------------------*/ -void ACS_RunEmeraldScript(mobj_t *mo) -{ - Environment *env = &ACSEnv; - - ACSVM::GlobalScope *const global = env->getGlobalScope(0); - ACSVM::HubScope *const hub = global->getHubScope(0); - ACSVM::MapScope *const map = hub->getMapScope(0); - - ACSVM::MapScope::ScriptStartInfo scriptInfo; - ThreadInfo info; - - P_SetTarget(&info.mo, mo); - - scriptInfo.info = &info; - - map->scriptStartTypeForced(ACS_ST_EMERALD, scriptInfo); -} - /*-------------------------------------------------- void ACS_RunGameOverScript(void) diff --git a/src/acs/interface.h b/src/acs/interface.h index 06a90454c..12acc1ca7 100644 --- a/src/acs/interface.h +++ b/src/acs/interface.h @@ -138,6 +138,20 @@ void ACS_RunPlayerDeathScript(player_t *player); void ACS_RunPlayerEnterScript(player_t *player); +/*-------------------------------------------------- + void ACS_RunPlayerFinishScript(player_t *player); + + Runs the map's special script for a player + finishing (P_DoPlayerExit). + + Input Arguments:- + player: The player to run the script for. + + Return:- + None +--------------------------------------------------*/ + +void ACS_RunPlayerFinishScript(player_t *player); /*-------------------------------------------------- void ACS_RunLapScript(mobj_t *mo, line_t *line); @@ -157,13 +171,13 @@ void ACS_RunLapScript(mobj_t *mo, line_t *line); /*-------------------------------------------------- - void ACS_RunPositionScript(void); + void ACS_RunRaceStartScript(void); Runs the map's special script for when the level goes past the POSITION period. --------------------------------------------------*/ -void ACS_RunPositionScript(void); +void ACS_RunRaceStartScript(void); /*-------------------------------------------------- @@ -176,16 +190,6 @@ void ACS_RunPositionScript(void); void ACS_RunOvertimeScript(void); -/*-------------------------------------------------- - void ACS_RunEmeraldScript(mobj_t *mo); - - Runs the map's special script for when the - Special Stage Chaos Emerald is collected. ---------------------------------------------------*/ - -void ACS_RunEmeraldScript(mobj_t *mo); - - /*-------------------------------------------------- void ACS_RunGameOverScript(void); diff --git a/src/acs/thread.hpp b/src/acs/thread.hpp index 478879d67..95d4f3a59 100644 --- a/src/acs/thread.hpp +++ b/src/acs/thread.hpp @@ -38,11 +38,10 @@ enum acs_scriptType_e ACS_ST_DEATH = 3, // DEATH: Runs when a player dies. ACS_ST_ENTER = 4, // ENTER: Runs when a player enters the game; both on start of the level, and when un-spectating. ACS_ST_LAP = 5, // LAP: Runs when a player's lap increases from crossing the finish line. - ACS_ST_POSITION = 6, // POSITION: Runs when the POSITION period ends. - ACS_ST_OVERTIME = 7, // OVERTIME: Runs when Overtime starts in timed game modes. - ACS_ST_UFO = 8, // UFO: Runs when the UFO Catcher is destroyed in a Special Stage. - ACS_ST_EMERALD = 9, // EMERALD: Runs when the Chaos Emerald is collected in a Special Stage. - ACS_ST_GAMEOVER = 10, // GAMEOVER: Runs when the level ends due to a losing condition and no player has an extra life. + ACS_ST_RACESTART = 6, // RACESTART: Runs when the RACE starts. + ACS_ST_OVERTIME = 8, // OVERTIME: Runs when Overtime starts in timed game modes. + ACS_ST_GAMEOVER = 9, // GAMEOVER: Runs when the level ends due to a losing condition and no player has an extra life. + ACS_ST_FINISH = 10, // FINISH: Runs when a player finishes }; // diff --git a/src/console.c b/src/console.c index d3be7c8e8..1ae5efec9 100644 --- a/src/console.c +++ b/src/console.c @@ -1890,7 +1890,7 @@ void CON_Drawer(void) if (con_curlines > 0) CON_DrawConsole(); - else if (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_CUTSCENE || gamestate == GS_CREDITS + else if (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_CUTSCENE || gamestate == GS_CREDITS || gamestate == GS_BLANCREDITS || gamestate == GS_VOTING || gamestate == GS_EVALUATION || gamestate == GS_WAITINGPLAYERS) CON_DrawHudlines(); diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 722270930..c97792f34 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -57,6 +57,7 @@ #include "k_boss.h" #include "doomstat.h" #include "s_sound.h" // sfx_syfail +#include "r_fps.h" // cl loading screen #include "v_video.h" @@ -5600,6 +5601,8 @@ boolean TryRunTics(tic_t realtics) } else { + boolean tickInterp = true; + // run the count * tics while (neededtic > gametic) { @@ -5627,7 +5630,17 @@ boolean TryRunTics(tic_t realtics) P_PostLoadLevel(); } - G_Ticker((gametic % NEWTICRATERATIO) == 0); + boolean run = (gametic % NEWTICRATERATIO) == 0; + + if (run && tickInterp) + { + // Update old view state BEFORE ticking so resetting + // the old interpolation state from game logic works. + R_UpdateViewInterpolation(); + tickInterp = false; // do not update again in sped-up tics + } + + G_Ticker(run); } if (Playing() && netgame && (gametic % TICRATE == 0)) diff --git a/src/d_main.cpp b/src/d_main.cpp index 100c30100..91e16ed7a 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -437,6 +437,12 @@ static bool D_Display(void) HU_Drawer(); break; + case GS_BLANCREDITS: + F_BlanCreditDrawer(); + HU_Erase(); + HU_Drawer(); + break; + case GS_WAITINGPLAYERS: // The clientconnect drawer is independent... if (netgame) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 056bb721a..b789db919 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -469,6 +469,7 @@ consvar_t cv_kartdebugdistribution = CVAR_INIT ("kartdebugdistribution", "Off", consvar_t cv_kartdebughuddrop = CVAR_INIT ("kartdebughuddrop", "Off", CV_NETVAR|CV_CHEAT|CV_NOSHOWHELP, CV_OnOff, NULL); static CV_PossibleValue_t kartdebugwaypoint_cons_t[] = {{0, "Off"}, {1, "Forwards"}, {2, "Backwards"}, {0, NULL}}; consvar_t cv_kartdebugwaypoints = CVAR_INIT ("kartdebugwaypoints", "Off", CV_NETVAR|CV_CHEAT|CV_NOSHOWHELP, kartdebugwaypoint_cons_t, NULL); +consvar_t cv_kartdebuglap = CVAR_INIT ("kartdebuglap", "Off", CV_NETVAR|CV_CHEAT|CV_NOSHOWHELP, kartdebugwaypoint_cons_t, NULL); consvar_t cv_kartdebugbotpredict = CVAR_INIT ("kartdebugbotpredict", "Off", CV_NETVAR|CV_CHEAT|CV_NOSHOWHELP, CV_OnOff, NULL); consvar_t cv_kartdebugcheckpoint = CVAR_INIT ("kartdebugcheckpoint", "Off", CV_NOSHOWHELP, CV_OnOff, NULL); @@ -5399,7 +5400,7 @@ static void Command_ExitLevel_f(void) CONS_Printf(M_GetText("This only works in a netgame.\n")); else if (!(server || (IsPlayerAdmin(consoleplayer)))) CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n")); - else if (( gamestate != GS_LEVEL && gamestate != GS_CREDITS ) || demo.playback) + else if (( gamestate != GS_LEVEL && gamestate != GS_CREDITS && gamestate != GS_BLANCREDITS ) || demo.playback) CONS_Printf(M_GetText("You must be in a level to use this.\n")); else SendNetXCmd(XD_EXITLEVEL, NULL, 0); diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 1b6ff0d11..285d4b19c 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -106,7 +106,7 @@ extern consvar_t cv_votetime; extern consvar_t cv_kartdebugitem, cv_kartdebugamount, cv_kartallowgiveitem, cv_kartdebugdistribution, cv_kartdebughuddrop; extern consvar_t cv_kartdebugcheckpoint, cv_kartdebugnodes, cv_kartdebugcolorize, cv_kartdebugdirector; -extern consvar_t cv_kartdebugwaypoints, cv_kartdebugbotpredict; +extern consvar_t cv_kartdebugwaypoints, cv_kartdebuglap,cv_kartdebugbotpredict; extern consvar_t cv_itemfinder; diff --git a/src/d_player.h b/src/d_player.h index 44899a11f..a081e7476 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -102,7 +102,7 @@ typedef enum PF_UPDATEMYRESPAWN = 1<<19, PF_FLIPCAM = 1<<20, - //free = 1<<21, + PF_TRUSTWAYPOINTS = 1<<21, // Do not activate lap cheat prevention next time finish line distance is updated PF_HITFINISHLINE = 1<<22, // Already hit the finish line this tic PF_WRONGWAY = 1<<23, // Moving the wrong way with respect to waypoints? @@ -548,6 +548,7 @@ struct player_t UINT32 distancetofinishprev; waypoint_t *currentwaypoint; waypoint_t *nextwaypoint; + UINT16 bigwaypointgap; tic_t airtime; // Keep track of how long you've been in the air UINT8 startboost; // (0 to 125) - Boost you get from start of race or respawn drop dash @@ -657,6 +658,9 @@ struct player_t UINT16 breathTimer; // Holding your breath underwater + UINT8 lastsafelap; + UINT8 lastsafestarpost; + // SINT8 lives; diff --git a/src/deh_soc.c b/src/deh_soc.c index d30957647..783cc14e1 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -1408,6 +1408,8 @@ void readlevelheader(MYFILE *f, char * name) mapheaderinfo[num]->encorepal = (UINT16)i; else if (fastcmp(word, "NUMLAPS")) mapheaderinfo[num]->numlaps = (UINT8)i; + else if (fastcmp(word, "LAPSPERSECTION")) + mapheaderinfo[num]->lapspersection = max((UINT8)i, 1u); else if (fastcmp(word, "UNLOCKABLE")) { if (i >= 0 && i <= MAXUNLOCKABLES) // 0 for no unlock required, anything else requires something diff --git a/src/deh_tables.c b/src/deh_tables.c index a90c9dd74..ddaa1fb03 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -267,7 +267,7 @@ const char *const PLAYERFLAG_LIST[] = { "UPDATEMYRESPAWN", "FLIPCAM", - "\x01", + "TRUSTWAYPOINTS", "HITFINISHLINE", // Already hit the finish line this tic "WRONGWAY", // Moving the wrong way with respect to waypoints? @@ -1315,6 +1315,7 @@ struct int_const_s const INT_CONST[] = { {"GS_CUTSCENE",GS_CUTSCENE}, {"GS_DEDICATEDSERVER",GS_DEDICATEDSERVER}, {"GS_WAITINGPLAYERS",GS_WAITINGPLAYERS}, + {"GS_BLANCREDITS",GS_BLANCREDITS}, // SRB2Kart // kartitems_t diff --git a/src/doomstat.h b/src/doomstat.h index 38d4a9cca..23f113966 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -412,6 +412,7 @@ struct mapheader_t UINT16 levelflags; ///< LF_flags: merged booleans into one UINT16 for space, see below UINT32 typeoflevel; ///< Combination of typeoflevel flags. UINT8 numlaps; ///< Number of laps in circuit mode, unless overridden. + UINT8 lapspersection; ///< Number of laps per section in hybrid section-circuit maps. fixed_t gravity; ///< Map-wide gravity. // Music information diff --git a/src/f_finale.c b/src/f_finale.c index a8c87d314..b82321eb8 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -271,7 +271,7 @@ static void F_TitleBGScroll(INT32 scrollspeed) // ============= // INTRO SCENE // ============= -#define NUMINTROSCENES 1 +#define NUMINTROSCENES 2 INT32 intro_scenenum = 0; INT32 intro_curtime = 0; @@ -279,7 +279,8 @@ const char *introtext[NUMINTROSCENES]; static tic_t introscenetime[NUMINTROSCENES] = { - 4*TICRATE, // KART KR(eW + 2*TICRATE, // KART KR(eW + 3*TICRATE, // Stuff :) }; // custom intros @@ -338,6 +339,12 @@ static void F_IntroDrawScene(void) highres = true; } + if (intro_scenenum == 1) + { + background = W_CachePatchName("BLANKART", PU_CACHE); + highres = false; + } + V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 0); if (background) @@ -372,10 +379,6 @@ void F_IntroTicker(void) timetonext--; - if (intro_scenenum == 0) - { - if (timetonext <= 0) - { #if 0 // The necessary apparatus for constructing more elaborate intros... intro_scenenum++; F_NewCutscene(introtext[intro_scenenum]); @@ -383,6 +386,42 @@ void F_IntroTicker(void) wipegamestate = -1; animtimer = stoptimer = 0; #endif + + if (intro_scenenum == 0) + { + if (timetonext <= 0) + { + if (rendermode != render_none) + { + F_WipeStartScreen(); + F_WipeColorFill(31); + F_WipeEndScreen(); + F_RunWipe(99, true); + } + + intro_scenenum++; + F_NewCutscene(introtext[intro_scenenum]); + timetonext = introscenetime[intro_scenenum]; + wipegamestate = -1; + animtimer = stoptimer = 0; + } + + if (finalecount == 16) + S_StartSound(NULL, sfx_vroom); + else if (finalecount == 47) + { + // Need to use M_Random otherwise it always uses the same sound + INT32 rskin = M_RandomKey(numskins); + UINT8 rtaunt = M_RandomKey(2); + sfxenum_t rsound = skins[rskin].soundsid[SKSKBST1+rtaunt]; + S_StartSound(NULL, rsound); + } + } + + if (intro_scenenum == 1) + { + if (timetonext <= 0) + { if (rendermode != render_none) { F_WipeStartScreen(); @@ -395,7 +434,7 @@ void F_IntroTicker(void) { tic_t nowtime, quittime, lasttime; nowtime = lasttime = I_GetTime(); - quittime = nowtime + NEWTICRATE*2; // Shortened the quit time, used to be 2 seconds + quittime = nowtime + NEWTICRATE; // Shortened the quit time, used to be 2 seconds while (quittime > nowtime) { while (!((nowtime = I_GetTime()) - lasttime)) @@ -424,14 +463,10 @@ void F_IntroTicker(void) D_StartTitle(); return; } - if (finalecount == 8) - S_StartSound(NULL, sfx_vroom); - else if (finalecount == 47) + if (finalecount == 80) { - // Need to use M_Random otherwise it always uses the same sound - INT32 rskin = M_RandomKey(numskins); - UINT8 rtaunt = M_RandomKey(2); - sfxenum_t rsound = skins[rskin].soundsid[SKSKBST1+rtaunt]; + sfxenum_t rsound = skins[1].soundsid[SKSKWIN]; + S_StartSound(NULL, sfx_flgcap); S_StartSound(NULL, rsound); } } @@ -516,43 +551,62 @@ static const char *credits[] = { "\1Lead Programming", "Sally \"TehRealSalt\" Cochenour", "Vivian \"toaster\" Grannell", + "Ronald \"Eidolon\" Kinard", + "James Robert Roman", "Sean \"Sryder\" Ryder", "Ehab \"wolfs\" Saeed", "\"ZarroTsu\"", "", "\1Support Programming", - "Colette \"fickleheart\" Bordelon", - "James R.", + "\"Lach\"", "\"Lat\'\"", + "AJ \"Tyron\" Martinez", "\"Monster Iestyn\"", - "\"Shuffle\"", "\"SteelT\"", "", + "\1External Programming", + "Alam Ed Arias", + "\"alphaRexJames\"", + "\"Ashnal\"", + "\"filpAM\"", + "\"FlykeSpice\"", + "\"Hannu Hanhi\"", + "\"himie\"", + "\"JugadorXEI\"", + "\"Kimberly\"", + "\"Lighto97\"", + "\"Lonsfor\"", + "\"mazmazz\"", + "\"minenice\"", + "\"Shuffle\"", + "\"Snu\"", + "\"X.organic\"", + "", "\1Lead Artists", "Desmond \"Blade\" DesJardins", "\"VelocitOni\"", "", "\1Support Artists", "Sally \"TehRealSalt\" Cochenour", + "\"Chengi\"", + "\"Chrispy\"", "Sherman \"CoatRack\" DesJardins", "\"DrTapeworm\"", "Jesse \"Jeck Jims\" Emerick", "Wesley \"Charyb\" Gillebaard", + "\"Nev3r\"", "Vivian \"toaster\" Grannell", "James \"SeventhSentinel\" Hall", "\"Lat\'\"", + "\"rairai104n\"", "\"Tyrannosaur Chao\"", "\"ZarroTsu\"", "", "\1External Artists", "\"1-Up Mason\"", - "\"Chengi\"", - "\"Chrispy\"", "\"DirkTheHusky\"", "\"LJSTAR\"", "\"MotorRoach\"", - "\"Nev3r\"", - "\"rairai104n\"", "\"Ritz\"", "\"Rob\"", "\"SmithyGNC\"", @@ -569,7 +623,7 @@ static const char *credits[] = { "\"VAdaPEGA\"", "\"VelocitOni\"", "", - "\1Music", + "\1Original Music", "\"DrTapeworm\"", "Wesley \"Charyb\" Gillebaard", "James \"SeventhSentinel\" Hall", @@ -587,7 +641,6 @@ static const char *credits[] = { "\"DrTapeworm\"", "Paul \"Boinciel\" Clempson", "Sherman \"CoatRack\" DesJardins", - "Colette \"fickleheart\" Bordelon", "Vivian \"toaster\" Grannell", "\"Gunla\"", "James \"SeventhSentinel\" Hall", @@ -597,16 +650,19 @@ static const char *credits[] = { "Sean \"Sryder\" Ryder", "\"Ryuspark\"", "\"Simsmagic\"", + "Ivo Solarin", "\"SP47\"", "\"TG\"", "\"Victor Rush Turbo\"", "\"ZarroTsu\"", "", "\1Testing", + "RKH License holders", + "The KCS", "\"CyberIF\"", "\"Dani\"", "Karol \"Fooruman\" D""\x1E""browski", // DÄ…browski, accents in srb2 :ytho: - "\"VirtAnderson\"", + "\"Virt\"", "", "\1Special Thanks", "SEGA", @@ -621,8 +677,8 @@ static const char *credits[] = { "\"Tyler52\"", "", "", - "\1Thank you ", - "\1for playing! ", + "\1Thank you", + "\1for playing!", NULL }; @@ -635,33 +691,31 @@ static struct { UINT8 colorize; } credits_pics[] = { // We don't have time to be fancy, let's just colorize some item sprites :V - {224, 80+(200* 1), "K_ITJAWZ", SKINCOLOR_CREAMSICLE}, - {224, 80+(200* 2), "K_ITSPB", SKINCOLOR_GARDEN}, - {224, 80+(200* 3), "K_ITBANA", SKINCOLOR_LILAC}, - {224, 80+(200* 4), "K_ITHYUD", SKINCOLOR_DREAM}, - {224, 80+(200* 5), "K_ITBHOG", SKINCOLOR_TANGERINE}, - {224, 80+(200* 6), "K_ITSHRK", SKINCOLOR_JAWZ}, - {224, 80+(200* 7), "K_ITSHOE", SKINCOLOR_MINT}, - {224, 80+(200* 8), "K_ITGROW", SKINCOLOR_RUBY}, - {224, 80+(200* 9), "K_ITPOGO", SKINCOLOR_SAPPHIRE}, - {224, 80+(200*10), "K_ITRSHE", SKINCOLOR_YELLOW}, - {224, 80+(200*11), "K_ITORB4", SKINCOLOR_DUSK}, - {224, 80+(200*12), "K_ITEGGM", SKINCOLOR_GREEN}, - {224, 80+(200*13), "K_ITMINE", SKINCOLOR_BRONZE}, - {224, 80+(200*14), "K_ITTHNS", SKINCOLOR_RASPBERRY}, - {224, 80+(200*15), "K_ITINV1", SKINCOLOR_GREY}, + {224, 80+(216* 1), "K_ITJAWZ", SKINCOLOR_CREAMSICLE}, + {224, 80+(216* 2), "K_ITSPB", SKINCOLOR_GARDEN}, + {224, 80+(216* 3), "K_ITBANA", SKINCOLOR_LILAC}, + {224, 80+(216* 4), "K_ITHYUD", SKINCOLOR_DREAM}, + {224, 80+(216* 5), "K_ITBHOG", SKINCOLOR_TANGERINE}, + {224, 80+(216* 6), "K_ITSHRK", SKINCOLOR_JAWZ}, + {224, 80+(216* 7), "K_ITSHOE", SKINCOLOR_MINT}, + {224, 80+(216* 8), "K_ITGROW", SKINCOLOR_RUBY}, + {224, 80+(216* 9), "K_ITPOGO", SKINCOLOR_SAPPHIRE}, + {224, 80+(216*10), "K_ITRSHE", SKINCOLOR_YELLOW}, + {224, 80+(216*11), "K_ITORB4", SKINCOLOR_DUSK}, + {224, 80+(216*12), "K_ITEGGM", SKINCOLOR_GREEN}, + {224, 80+(216*13), "K_ITMINE", SKINCOLOR_BRONZE}, + {224, 80+(216*14), "K_ITTHNS", SKINCOLOR_RASPBERRY}, + {224, 80+(216*15), "K_ITINV1", SKINCOLOR_GREY}, // This Tyler52 gag is troublesome // Alignment should be ((spaces+1 * 100) + (headers+1 * 38) + (lines * 15)) - // Current max image spacing: (200*17) - {112, (15*100)+(17*38)+(88*15), "TYLER52", SKINCOLOR_NONE} + // Current max image spacing: (216*17) + {112, (16*100)+(19*38)+(103*15), "TYLER52", SKINCOLOR_NONE}, + {0, 0, NULL, SKINCOLOR_NONE} }; #undef CREDITS_LEFT #undef CREDITS_RIGHT -static UINT32 credits_height = 0; -static const UINT8 credits_numpics = sizeof(credits_pics)/sizeof(credits_pics[0]) - 1; - void F_StartCredits(void) { G_SetGamestate(GS_CREDITS); @@ -692,9 +746,9 @@ void F_StartCredits(void) void F_CreditDrawer(void) { UINT16 i; - fixed_t y = (80<>1); + fixed_t y = (80<>1; @@ -717,6 +771,9 @@ void F_CreditDrawer(void) V_DrawFixedPatch(credits_pics[i].x<>1))<>FRACBITS > -10) - V_DrawStringAtFixed((BASEVIDWIDTH-V_StringWidth(&credits[i][1], V_ALLOWLOWERCASE|V_YELLOWMAP))<>1, y, V_ALLOWLOWERCASE|V_YELLOWMAP, &credits[i][1]); - y += 12<>FRACBITS > -10) V_DrawStringAtFixed(32<>1); - - // Calculate credits height to display art properly - if (credits_height == 0) - { - for (i = 0; credits[i]; i++) - { - switch(credits[i][0]) - { - case 0: credits_height += 80; break; - case 1: credits_height += 30; break; - default: credits_height += 12; break; - } - } - credits_height = 131*credits_height/80; // account for scroll speeds. This is a guess now, so you may need to update this if you change the credits length. - } + fixed_t y = (80<>FRACBITS > -20) + V_DrawCenteredStringAtFixed(160<>FRACBITS > -10) + V_DrawCenteredStringAtFixed(160<>FRACBITS) * vid.dupy) > vid.height) + break; + } +} + +void F_BlanCreditTicker(void) +{ + // "Simulate" the drawing of the credits so that dedicated mode doesn't get stuck + UINT16 i; + fixed_t y = (80< vid.height) + break; + } + + // Do this here rather than in the drawer you doofus! (this is why dedicated mode broke at credits) + if (!blancredits[i] && y <= 120<p); UINT8 xziptic = 0; + if (g->done) + { + continue; + } + while (ziptic != DW_END) // Get rid of extradata stuff { if (ziptic < MAXPLAYERS) { +#ifdef DEVELOP UINT8 playerid = ziptic; +#endif // We want to skip *any* player extradata because some demos have extradata for bogus players, // but if there is tic data later for those players *then* we'll consider it invalid. ziptic = READUINT8(g->p); - if (ziptic & DXD_SKIN) - g->p += 18; // We _could_ read this info, but it shouldn't change anything in record attack... - if (ziptic & DXD_COLOR) - g->p += 16; // Same tbh - if (ziptic & DXD_NAME) - g->p += 16; // yea - if (ziptic & DXD_FOLLOWER) - g->p += 32; // ok (32 because there's both the skin and the colour) + if (ziptic & DXD_JOINDATA) + { + if (READUINT8(g->p) != 0) + I_Error("Ghost is not a record attack ghost (bot JOINDATA)"); + } if (ziptic & DXD_PLAYSTATE) { UINT8 playstate = READUINT8(g->p); @@ -1192,6 +1236,14 @@ void G_GhostTicker(void) ; } } + if (ziptic & DXD_NAME) + g->p += 16; // yea + if (ziptic & DXD_SKIN) + g->p += 16; // We _could_ read this info, but it shouldn't change anything in record attack... + if (ziptic & DXD_COLOR) + g->p += 16; // Same tbh + if (ziptic & DXD_FOLLOWER) + g->p += 32; // ok (32 because there's both the skin and the colour) if (ziptic & DXD_WEAPONPREF) g->p++; // ditto } @@ -2129,6 +2181,13 @@ void G_BeginRecording(void) // Save netvar data CV_SaveDemoVars(&demobuf.p); + + if ((demoflags & DF_GRANDPRIX)) + { + WRITEUINT8(demobuf.p, grandprixinfo.gamespeed); + WRITEUINT8(demobuf.p, grandprixinfo.masterbots == true); + WRITEUINT8(demobuf.p, grandprixinfo.eventmode); + } // Save "mapmusrng" used for altmusic selection WRITEUINT8(demobuf.p, mapmusrng); @@ -2152,8 +2211,17 @@ void G_BeginRecording(void) i |= DEMO_KICKSTART; if (player->pflags & PF_SHRINKME) i |= DEMO_SHRINKME; + if (player->bot == true) + i |= DEMO_BOT; WRITEUINT8(demobuf.p, i); + if (i & DEMO_BOT) + { + WRITEUINT8(demobuf.p, player->botvars.difficulty); + WRITEUINT8(demobuf.p, player->botvars.diffincrease); // needed to avoid having to duplicate logic + WRITEUINT8(demobuf.p, (UINT8)player->botvars.rival); + } + // Name memset(name, 0, 16); strncpy(name, player_names[p], 16); @@ -2806,7 +2874,7 @@ void G_DoPlayDemo(char *defdemoname) UINT32 randseed; char msg[1024]; - boolean spectator; + boolean spectator, bot; UINT8 slots[MAXPLAYERS], kartspeed[MAXPLAYERS], kartweight[MAXPLAYERS], numslots = 0; #if defined(SKIPERRORS) && !defined(DEVELOP) @@ -3083,6 +3151,15 @@ void G_DoPlayDemo(char *defdemoname) // net var data CV_LoadDemoVars(&demobuf.p); + + memset(&grandprixinfo, 0, sizeof grandprixinfo); + if ((demoflags & DF_GRANDPRIX)) + { + grandprixinfo.gp = true; + grandprixinfo.gamespeed = READUINT8(demobuf.p); + grandprixinfo.masterbots = READUINT8(demobuf.p) != 0; + grandprixinfo.eventmode = READUINT8(demobuf.p); + } // Load "mapmusrng" used for altmusic selection mapmusrng = READUINT8(demobuf.p); @@ -3135,12 +3212,13 @@ void G_DoPlayDemo(char *defdemoname) UINT8 flags = READUINT8(demobuf.p); spectator = !!(flags & DEMO_SPECTATOR); + bot = !!(flags & DEMO_BOT); - if (spectator == true) + if ((spectator || bot)) { if (modeattacking) { - snprintf(msg, 1024, M_GetText("%s is a Record Attack replay with spectators, and is thus invalid.\n"), pdemoname); + snprintf(msg, 1024, M_GetText("%s is a Record Attack replay with %s, and is thus invalid.\n"), pdemoname, (bot ? "bots" : "spectators")); CONS_Alert(CONS_ERROR, "%s", msg); M_StartMessage(msg, NULL, MM_NOTHING); Z_Free(pdemoname); @@ -3184,6 +3262,13 @@ void G_DoPlayDemo(char *defdemoname) K_UpdateShrinkCheat(&players[p]); + if ((players[p].bot = bot) == true) + { + players[p].botvars.difficulty = READUINT8(demobuf.p); + players[p].botvars.diffincrease = READUINT8(demobuf.p); // needed to avoid having to duplicate logic + players[p].botvars.rival = (boolean)READUINT8(demobuf.p); + } + // Name M_Memcpy(player_names[p],demobuf.p,16); demobuf.p += 16; @@ -3435,6 +3520,11 @@ void G_AddGhost(char *defdemoname) p++; } + if ((flags & DF_GRANDPRIX)) + { + p += 3; + } + // Skip mapmusrng p++; @@ -3449,9 +3539,10 @@ void G_AddGhost(char *defdemoname) p++; // player number - doesn't really need to be checked, TODO maybe support adding multiple players' ghosts at once // any invalidating flags? - if ((READUINT8(p) & (DEMO_SPECTATOR)) != 0) + i = READUINT8(p); + if ((i & (DEMO_SPECTATOR|DEMO_BOT)) != 0) { - CONS_Alert(CONS_NOTICE, M_GetText("Failed to add ghost %s: Invalid player slot. (Spectator)\n"), pdemoname); + CONS_Alert(CONS_NOTICE, M_GetText("Failed to add ghost %s: Invalid player slot (spectator/bot)\n"), defdemoname); Z_Free(pdemoname); Z_Free(buffer); return; @@ -3656,9 +3747,16 @@ void G_UpdateStaffGhostName(lumpnum_t l) p++; // stealth } + if ((flags & DF_GRANDPRIX)) + { + p += 3; + } + // Assert first player is in and then read name if (READUINT8(p) != 0) goto fail; + if (READUINT8(p) & (DEMO_SPECTATOR|DEMO_BOT)) + goto fail; M_Memcpy(dummystaffname, p,16); dummystaffname[16] = '\0'; diff --git a/src/g_demo.h b/src/g_demo.h index 420809845..8a73145ad 100644 --- a/src/g_demo.h +++ b/src/g_demo.h @@ -116,13 +116,14 @@ typedef enum extern UINT8 demo_extradata[MAXPLAYERS]; extern UINT8 demo_writerng; -#define DXD_RESPAWN 0x01 // "respawn" command in console -#define DXD_SKIN 0x02 // skin changed -#define DXD_NAME 0x04 // name changed -#define DXD_COLOR 0x08 // color changed -#define DXD_PLAYSTATE 0x10 // state changed between playing, spectating, or not in-game -#define DXD_FOLLOWER 0x20 // follower was changed -#define DXD_WEAPONPREF 0x40 // netsynced playsim settings were changed +#define DXD_JOINDATA 0x01 // join-specific data +#define DXD_PLAYSTATE 0x02 // state changed between playing, spectating, or not in-game +#define DXD_NAME 0x04 // name changed +#define DXD_SKIN 0x08 // skin changed +#define DXD_COLOR 0x10 // color changed +#define DXD_FOLLOWER 0x20 // follower was changed +#define DXD_RESPAWN 0x40 // "respawn" command in console +#define DXD_WEAPONPREF 0x80 // netsynced playsim settings were changed #define DXD_PST_PLAYING 0x01 #define DXD_PST_SPECTATING 0x02 diff --git a/src/g_game.c b/src/g_game.c index fca617a10..3e83d25ac 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1177,7 +1177,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) *kbl = false; // looking up/down - cmd->aiming += (mlooky<<19)*player_invert*screen_invert; + cmd->aiming += (mlooky<<3)*player_invert*screen_invert; } */ @@ -1185,7 +1185,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) INT32 axis = G_PlayerInputAnalog(forplayer, gc_lookup, false) - G_PlayerInputAnalog(forplayer, gc_lookdown, false); if (axis != 0 && spectating) - cmd->aiming += (axis<<16) * screen_invert; + cmd->aiming += axis * screen_invert; // spring back if not using keyboard neither mouselookin' /* @@ -1570,6 +1570,24 @@ boolean G_Responder(event_t *ev) return true; } } + else if (gamestate == GS_BLANCREDITS) + { + if (HU_Responder(ev)) + { + hu_keystrokes = true; + return true; // chat ate the event + } + + if (F_CreditResponder(ev)) + { + // Skip credits for everyone + if (! netgame) + F_StartGameEvaluation(); + else if (server || IsPlayerAdmin(consoleplayer)) + SendNetXCmd(XD_EXITLEVEL, NULL, 0); + return true; + } + } else if (gamestate == GS_CONTINUING) { return true; @@ -1978,7 +1996,7 @@ void G_Ticker(boolean run) } } - D_MapChange(gamemap, gametype, (cv_kartencore.value == 1), false, 1, false, false); + D_MapChange(gamemap, gametype, encoremode, false, 1, false, false); } for (i = 0; i < MAXPLAYERS; i++) @@ -2098,6 +2116,12 @@ void G_Ticker(boolean run) HU_Ticker(); break; + case GS_BLANCREDITS: + if (run) + F_BlanCreditTicker(); + HU_Ticker(); + break; + case GS_TITLESCREEN: if (titlemapinaction) P_Ticker(run); @@ -2260,6 +2284,9 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) UINT16 nocontrol; INT32 kickstartaccel; boolean enteredGame; + UINT8 lastsafelap; + UINT8 lastsafestarpost; + UINT16 bigwaypointgap; score = players[player].score; lives = players[player].lives; @@ -2313,6 +2340,9 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) wanted = 0; rings = 10; kickstartaccel = 0; + lastsafelap = 0; + lastsafestarpost = 0; + bigwaypointgap = 0; nocontrol = 0; laps = 0; latestlap = 0; @@ -2380,6 +2410,10 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) prevcheck = players[player].nextcheck; pflags |= (players[player].pflags & (PF_STASIS|PF_ELIMINATED|PF_NOCONTEST|PF_LOSTLIFE|PF_FLIPCAM)); + + lastsafelap = players[player].lastsafelap; + lastsafestarpost = players[player].lastsafestarpost; + bigwaypointgap = players[player].bigwaypointgap; } if (!betweenmaps) @@ -2414,6 +2448,9 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) p->splitscreenindex = splitscreenindex; p->spectator = spectator; p->angleturn = playerangleturn; + p->lastsafelap = lastsafelap; + p->lastsafestarpost = lastsafestarpost; + p->bigwaypointgap = bigwaypointgap; // save player config truth reborn p->skincolor = skincolor; @@ -3071,7 +3108,7 @@ void G_FinishExitLevel(void) // Don't save demos immediately here! Let standings write first } - else if (gamestate == GS_CREDITS) + else if (gamestate == GS_CREDITS || gamestate == GS_BLANCREDITS) { F_StartGameEvaluation(); } diff --git a/src/g_game.h b/src/g_game.h index fa137f136..ad6456798 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -96,7 +96,7 @@ void weaponPrefChange3(void); void weaponPrefChange4(void); // mouseaiming (looking up/down with the mouse or keyboard) -#define KB_LOOKSPEED (1<<25) +#define KB_LOOKSPEED (1<<9) #define MAXPLMOVE (50) #define SLOWTURNTICS (cv_turnsmooth.value * 3) diff --git a/src/g_state.h b/src/g_state.h index fe8bcfca4..2dc1cc82f 100644 --- a/src/g_state.h +++ b/src/g_state.h @@ -43,7 +43,10 @@ typedef enum // Not fadable GS_DEDICATEDSERVER, // new state for dedicated server - GS_WAITINGPLAYERS // waiting for players in a net game + GS_WAITINGPLAYERS, // waiting for players in a net game + + // New + GS_BLANCREDITS, // BlanKart: Credits for BlanKart } gamestate_t; typedef enum diff --git a/src/hu_stuff.c b/src/hu_stuff.c index a5e82346a..6e1408cc2 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -2143,7 +2143,9 @@ void HU_Drawer(void) || gamestate == GS_INTERMISSION || gamestate == GS_CUTSCENE || gamestate == GS_CREDITS || gamestate == GS_EVALUATION || gamestate == GS_GAMEEND - || gamestate == GS_VOTING || gamestate == GS_WAITINGPLAYERS) // SRB2kart + || gamestate == GS_VOTING || gamestate == GS_WAITINGPLAYERS + || gamestate == GS_BLANCREDITS + ) // SRB2kart return; // draw multiplayer rankings diff --git a/src/info/sprites.h b/src/info/sprites.h index 8cb5208f6..55922f37c 100644 --- a/src/info/sprites.h +++ b/src/info/sprites.h @@ -568,9 +568,12 @@ _(ISTB) // instashield layer B _(PWCL) // Invinc/grow clash VFX _(ARRO) // player arrows -_(ITEM) -_(ITMO) -_(ITMI) +_(ITEM) // base item +_(ITMO) // Multi-Orbinaut +_(ITMI) // Invincibility +_(ITSN) // Multi-Sneaker +_(ITBA) // Multi-Banana +_(ITJA) // Multi-Jawz _(ITMN) _(WANT) diff --git a/src/k_collide.c b/src/k_collide.c index 0472273d8..ef0e931d3 100644 --- a/src/k_collide.c +++ b/src/k_collide.c @@ -717,17 +717,6 @@ void K_ThunderShieldAttack(mobj_t *actor, fixed_t size) boolean K_BubbleShieldCollide(mobj_t *t1, mobj_t *t2) { - if (t1->type == MT_PLAYER) - { - // Bubble Shield already has a hitbox, and it gets - // teleported every tic so the Bubble itself will - // always make contact with other objects. - // - // Therefore, we don't need a second, smaller hitbox - // on the player. - return true; - } - if (t2->type == MT_PLAYER) { // Counter desyncs @@ -745,7 +734,7 @@ boolean K_BubbleShieldCollide(mobj_t *t1, mobj_t *t2) return true; // Player Damage - P_DamageMobj(t2, t1->target, t1, 1, DMG_NORMAL); + P_DamageMobj(t2, ((t1->type == MT_BUBBLESHIELD) ? t1->target : t1), t1, 1, DMG_NORMAL); if (t1->target->player) { diff --git a/src/k_hud.c b/src/k_hud.c index 1c98e0204..93d893b2d 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -4299,6 +4299,11 @@ static void K_DrawWaypointDebugger(void) if (stplyr != &players[displayplayers[0]]) // only for p1 return; + if (stplyr->bigwaypointgap) + { + V_DrawString(8, 146, 0, va("Auto Respawn Timer: %d", stplyr->bigwaypointgap)); + } + V_DrawString(8, 156, 0, va("Current Waypoint ID: %d", K_GetWaypointID(stplyr->currentwaypoint))); V_DrawString(8, 166, 0, va("Next Waypoint ID: %d", K_GetWaypointID(stplyr->nextwaypoint))); V_DrawString(8, 176, 0, va("Finishline Distance: %d", stplyr->distancetofinish)); diff --git a/src/k_kart.c b/src/k_kart.c index 81a2bd4bc..f08d74af3 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -238,6 +238,7 @@ void K_RegisterKartStuff(void) CV_RegisterVar(&cv_kartdebugdistribution); CV_RegisterVar(&cv_kartdebughuddrop); CV_RegisterVar(&cv_kartdebugwaypoints); + CV_RegisterVar(&cv_kartdebuglap); CV_RegisterVar(&cv_kartdebugbotpredict); CV_RegisterVar(&cv_kartdebugcheckpoint); @@ -7025,6 +7026,18 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) if (player->checkskip) player->checkskip--; + if (player->bigwaypointgap && (player->bigwaypointgap > AUTORESPAWN_THRESHOLD || !P_PlayerInPain(player))) + { + player->bigwaypointgap--; + if (!player->bigwaypointgap) + P_KillMobj(player->mo, NULL, NULL, DMG_INSTAKILL); + else if (player->bigwaypointgap == AUTORESPAWN_THRESHOLD) + { + S_StartSound(player->mo, sfx_s26d); + CONS_Printf("You are going the wrong way! You will automatically respawn in 7 seconds.\n"); + } + } + if (player->growshrinktimer != 0) { if (player->growshrinktimer > 0) @@ -7891,7 +7904,24 @@ static boolean K_SetPlayerNextWaypoint(player_t *player) continue; } - bestwaypoint = waypoint->prevwaypoints[i]; + if ((waypoint->nextwaypoints != NULL) && (waypoint->numnextwaypoints > 0U)) + { + for (size_t j = 0U; j < waypoint->numnextwaypoints; j++) + { + if (!K_GetWaypointIsEnabled(waypoint->nextwaypoints[j])) + { + continue; + } + + if (waypoint->nextwaypoints[j] == waypoint) + { + continue; + } + + bestwaypoint = waypoint->nextwaypoints[j]; + break; + } + } nextbestdelta = angledelta; nextbestmomdelta = momdelta; @@ -8117,7 +8147,7 @@ static void K_UpdateDistanceFromFinishLine(player_t *player) const mapheader_t *mapheader = mapheaderinfo[gamemap - 1]; if ((mapheader->levelflags & LF_SECTIONRACE) == 0U) { - const UINT8 numfulllapsleft = ((UINT8)numlaps - player->laps); + const UINT8 numfulllapsleft = ((UINT8)numlaps - player->laps) / mapheader->lapspersection; player->distancetofinish += numfulllapsleft * K_GetCircuitLength(); } } @@ -8166,24 +8196,59 @@ static void K_UpdatePlayerWaypoints(player_t *const player) boolean updaterespawn = K_SetPlayerNextWaypoint(player); // Update prev value (used for grief prevention code) - K_UpdateDistanceFromFinishLine(player); player->distancetofinishprev = player->distancetofinish; + K_UpdateDistanceFromFinishLine(player); // Respawning should be a full reset. UINT32 delta = u32_delta(player->distancetofinish, player->distancetofinishprev); - if (!player->respawn && delta > distance_threshold) + if (delta > distance_threshold && + !player->respawn && // Respawning should be a full reset. + old_currentwaypoint != NULL && // So should touching the first waypoint ever. + player->laps != 0 && // POSITION rooms may have unorthodox waypoints to guide bots. + !(player->pflags & PF_TRUSTWAYPOINTS)) // Special exception. { - CONS_Debug(DBG_GAMELOGIC, "Player %s: waypoint ID %d too far away (%u > %u)\n", - sizeu1(player - players), K_GetWaypointID(player->nextwaypoint), delta, distance_threshold); +#define debug_args "Player %s: waypoint ID %d too far away (%u > %u)\n", \ + sizeu1(player - players), K_GetWaypointID(player->nextwaypoint), delta, distance_threshold + if (cv_kartdebuglap.value) + CONS_Printf(debug_args); + else + CONS_Debug(DBG_GAMELOGIC, debug_args); +#undef debug_args - // Distance jump is too great, keep the old waypoints and recalculate distance. - player->currentwaypoint = old_currentwaypoint; - player->nextwaypoint = old_nextwaypoint; - player->distancetofinish = player->distancetofinishprev; + if (!cv_kartdebuglap.value) + { + // Distance jump is too great, keep the old waypoints and old distance. + player->currentwaypoint = old_currentwaypoint; + player->nextwaypoint = old_nextwaypoint; + player->distancetofinish = player->distancetofinishprev; + + // Start the auto respawn timer when the distance jumps. + if (!player->bigwaypointgap) + { + player->bigwaypointgap = AUTORESPAWN_TIME; + } + } + } + else + { + // Reset the auto respawn timer if distance changes are back to normal. + if (player->bigwaypointgap && player->bigwaypointgap <= AUTORESPAWN_THRESHOLD + 1) + { + player->bigwaypointgap = 0; + + // While the player was in the "bigwaypointgap" state, laps did not change from crossing finish lines. + // So reset the lap back to normal, in case they were able to get behind the line. + player->laps = player->lastsafelap; + if (numbosswaypoints == 0) + { + player->starpostnum = player->lastsafestarpost; + } + } } // Respawn point should only be updated when we're going to a nextwaypoint if ((updaterespawn) && + (player->bigwaypointgap == 0) && (!player->respawn) && (player->nextwaypoint != old_nextwaypoint) && (K_GetWaypointIsSpawnpoint(player->nextwaypoint)) && @@ -8192,6 +8257,9 @@ static void K_UpdatePlayerWaypoints(player_t *const player) if (!(player->pflags & PF_WRONGWAY)) player->grieftime = 0; + player->lastsafelap = player->laps; + player->lastsafestarpost = player->starpostnum; + // Check if respawn is safe. If not then goto next spawnpoint and respawn there. if (K_SafeRespawnPosition(player->mo)) { @@ -8207,12 +8275,14 @@ static void K_UpdatePlayerWaypoints(player_t *const player) } else { + mobj_t *currentwaypoint = player->currentwaypoint->mobj; mobj_t *safewaypoint = player->nextwaypoint->mobj; + angle_t respawnangle = R_PointToAngle2(currentwaypoint->x, currentwaypoint->y, safewaypoint->x, safewaypoint->y); player->starposttime = player->realtime; player->starpostz = safewaypoint->spawnpoint->z >> FRACBITS; player->starpostflip = (safewaypoint->flags2 & MF2_OBJECTFLIP); - player->starpostangle = safewaypoint->spawnpoint ? FixedAngle(safewaypoint->spawnpoint->angle * FRACUNIT) : player->mo->angle; + player->starpostangle = respawnangle; // Then do x and y player->starpostx = safewaypoint->x >> FRACBITS; @@ -8225,6 +8295,8 @@ static void K_UpdatePlayerWaypoints(player_t *const player) K_FudgeRespawn(player, player->nextwaypoint); } } + + player->pflags &= ~PF_TRUSTWAYPOINTS; // clear special exception } INT32 K_GetKartRingPower(player_t *player, boolean boosted) @@ -8800,6 +8872,16 @@ void K_UpdateAllPlayerPositions(void) continue; } + if (player->respawn > 0 && player->lastsafelap != player->laps) + { + player->laps = player->lastsafelap; + + if (numbosswaypoints == 0) + { + player->starpostnum = player->lastsafestarpost; + } + } + K_UpdatePlayerWaypoints(player); } @@ -10034,9 +10116,9 @@ UINT8 K_GetInvincibilityItemFrame(void) return ((leveltime % (7*3)) / 3); } -UINT8 K_GetOrbinautItemFrame(UINT8 count) +UINT8 K_GetMultItemFrame(UINT8 count, UINT8 max) { - return min(count - 1, 3); + return min(count - 1, max); } boolean K_IsSPBInGame(void) @@ -10092,9 +10174,21 @@ void K_UpdateMobjItemOverlay(mobj_t *part, SINT8 itemType, UINT8 itemCount) { switch (itemType) { + case KITEM_SNEAKER: + part->sprite = SPR_ITSN; + part->frame = FF_FULLBRIGHT|FF_PAPERSPRITE|K_GetMultItemFrame(itemCount, 2); + break; case KITEM_ORBINAUT: part->sprite = SPR_ITMO; - part->frame = FF_FULLBRIGHT|FF_PAPERSPRITE|K_GetOrbinautItemFrame(itemCount); + part->frame = FF_FULLBRIGHT|FF_PAPERSPRITE|K_GetMultItemFrame(itemCount, 3); + break; + case KITEM_BANANA: + part->sprite = SPR_ITBA; + part->frame = FF_FULLBRIGHT|FF_PAPERSPRITE|K_GetMultItemFrame(itemCount, 3); + break; + case KITEM_JAWZ: + part->sprite = SPR_ITJA; + part->frame = FF_FULLBRIGHT|FF_PAPERSPRITE|K_GetMultItemFrame(itemCount, 1); break; case KITEM_INVINCIBILITY: part->sprite = SPR_ITMI; diff --git a/src/k_kart.h b/src/k_kart.h index 93624bc97..7fa06f995 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -26,6 +26,9 @@ Make sure this matches the actual number of states #define GROW_SCALE ((3*FRACUNIT)/2) #define SHRINK_SCALE ((6*FRACUNIT)/8) +#define AUTORESPAWN_TIME (10 * TICRATE) +#define AUTORESPAWN_THRESHOLD (7 * TICRATE) + // Used for respawning checks. typedef struct respawnresult_s @@ -166,7 +169,7 @@ SINT8 K_Sliptiding(player_t *player); void K_MoveKartPlayer(player_t *player, boolean onground); void K_CheckSpectateStatus(boolean considermapreset); UINT8 K_GetInvincibilityItemFrame(void); -UINT8 K_GetOrbinautItemFrame(UINT8 count); +UINT8 K_GetMultItemFrame(UINT8 count, UINT8 max); boolean K_IsSPBInGame(void); // sound stuff for lua diff --git a/src/lua_maplib.c b/src/lua_maplib.c index e945c74ab..a3e61f8e6 100644 --- a/src/lua_maplib.c +++ b/src/lua_maplib.c @@ -2609,6 +2609,8 @@ static int mapheaderinfo_get(lua_State *L) lua_pushinteger(L, header->palette); else if (fastcmp(field,"numlaps")) lua_pushinteger(L, header->numlaps); + else if (fastcmp(field,"lapspersection")) + lua_pushinteger(L, header->lapspersection); else if (fastcmp(field,"unlockrequired")) lua_pushinteger(L, header->unlockrequired); else if (fastcmp(field,"levelselect")) diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 0664301b9..c4ca9355e 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -388,6 +388,12 @@ static int player_get(lua_State *L) lua_pushinteger(L, plr->confirmVictimDelay); else if (fastcmp(field,"glanceDir")) lua_pushinteger(L, plr->glanceDir); + else if (fastcmp(field,"breathTimer")) + lua_pushinteger(L, plr->breathTimer); + else if (fastcmp(field,"lastsafelap")) + lua_pushinteger(L, plr->lastsafelap); + else if (fastcmp(field,"laststarpost")) + lua_pushinteger(L, plr->lastsafestarpost); else if (fastcmp(field,"roundscore")) plr->roundscore = luaL_checkinteger(L, 3); else if (fastcmp(field,"marescore")) @@ -799,6 +805,12 @@ static int player_set(lua_State *L) plr->confirmVictimDelay = luaL_checkinteger(L, 3); else if (fastcmp(field,"glanceDir")) plr->glanceDir = luaL_checkinteger(L, 3); + else if (fastcmp(field,"breathTimer")) + plr->breathTimer = luaL_checkinteger(L, 3); + else if (fastcmp(field,"lastsafelap")) + plr->lastsafelap = luaL_checkinteger(L, 3); + else if (fastcmp(field,"lastsafestarpost")) + plr->lastsafestarpost = luaL_checkinteger(L, 3); else if (fastcmp(field,"roundscore")) lua_pushinteger(L, plr->roundscore); else if (fastcmp(field,"marescore")) diff --git a/src/m_menu.c b/src/m_menu.c index 8831a32c8..9111d4722 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -225,6 +225,7 @@ static void M_GetAllEmeralds(INT32 choice); static void M_DestroyRobots(INT32 choice); //static void M_LevelSelectWarp(INT32 choice); static void M_Credits(INT32 choice); +static void M_BlanCredits(INT32 choice); static void M_PandorasBox(INT32 choice); static void M_EmblemHints(INT32 choice); static char *M_GetConditionString(condition_t cond); @@ -303,6 +304,10 @@ static void M_AssignJoystick(INT32 choice); static void M_ChangeControl(INT32 choice); static void M_ResetControls(INT32 choice); +//camera options menu +menu_t OP_CamOptionsDef; +menu_t OP_Player1CamOptionsDef, OP_Player2CamOptionsDef, OP_Player3CamOptionsDef, OP_Player4CamOptionsDef; + // Video & Sound menu_t OP_VideoOptionsDef, OP_VideoModeDef; #ifdef HWRENDER @@ -1071,19 +1076,85 @@ enum // Prefix: OP_ static menuitem_t OP_MainMenu[] = { - {IT_SUBMENU|IT_STRING, NULL, "Control Setup...", {.submenu = &OP_ControlsDef}, 10}, + {IT_SUBMENU|IT_STRING, NULL, "Control Setup...", {.submenu = &OP_ControlsDef}, 10}, - {IT_SUBMENU|IT_STRING, NULL, "Video Options...", {.submenu = &OP_VideoOptionsDef}, 30}, - {IT_SUBMENU|IT_STRING, NULL, "Sound Options...", {.submenu = &OP_SoundOptionsDef}, 40}, + {IT_SUBMENU|IT_STRING, NULL, "Video Options...", {.submenu = &OP_VideoOptionsDef}, 30}, + {IT_SUBMENU|IT_STRING, NULL, "Sound Options...", {.submenu = &OP_SoundOptionsDef}, 40}, - {IT_SUBMENU|IT_STRING, NULL, "HUD Options...", {.submenu = &OP_HUDOptionsDef}, 60}, - {IT_SUBMENU|IT_STRING, NULL, "Gameplay Options...", {.submenu = &OP_GameOptionsDef}, 70}, - {IT_SUBMENU|IT_STRING, NULL, "Server Options...", {.submenu = &OP_ServerOptionsDef}, 80}, + {IT_SUBMENU|IT_STRING, NULL, "HUD Options...", {.submenu = &OP_HUDOptionsDef}, 60}, + {IT_SUBMENU|IT_STRING, NULL, "Camera Options...", {.submenu = &OP_CamOptionsDef}, 70}, + {IT_SUBMENU|IT_STRING, NULL, "Gameplay Options...", {.submenu = &OP_GameOptionsDef}, 80}, + {IT_SUBMENU|IT_STRING, NULL, "Server Options...", {.submenu = &OP_ServerOptionsDef}, 90}, - {IT_SUBMENU|IT_STRING, NULL, "Data Options...", {.submenu = &OP_DataOptionsDef}, 100}, + {IT_SUBMENU|IT_STRING, NULL, "Data Options...", {.submenu = &OP_DataOptionsDef}, 110}, - {IT_CALL|IT_STRING, NULL, "Tricks & Secrets (F1)", {.routine = M_Manual}, 120}, - {IT_CALL|IT_STRING, NULL, "Play Credits", {.routine = M_Credits}, 130}, + {IT_CALL|IT_STRING, NULL, "Tricks & Secrets (F1)", {.routine = M_Manual}, 130}, + {IT_CALL|IT_STRING, NULL, "Play Kart Credits", {.routine = M_Credits}, 140}, + {IT_CALL|IT_STRING, NULL, "Play BlanKart Credits", {.routine = M_BlanCredits}, 150}, +}; + +static menuitem_t OP_CamOptionsMenu[] = +{ + {IT_HEADER, NULL, "Camera Options", {NULL}, 0}, + + {IT_STRING | IT_CVAR, NULL, "Lagless Camera", {.cvar = &cv_laglesscam}, 20}, + + {IT_STRING | IT_SUBMENU, NULL, "Player 1 Camera options...", {.submenu = &OP_Player1CamOptionsDef}, 40}, + {IT_STRING | IT_SUBMENU, NULL, "Player 2 Camera options...", {.submenu = &OP_Player2CamOptionsDef}, 50}, + {IT_STRING | IT_SUBMENU, NULL, "Player 3 Camera options...", {.submenu = &OP_Player3CamOptionsDef}, 60}, + {IT_STRING | IT_SUBMENU, NULL, "Player 4 Camera options...", {.submenu = &OP_Player4CamOptionsDef}, 70}, +}; + +static menuitem_t OP_Player1CamOptionsMenu[] = +{ + {IT_HEADER, NULL, "Player 1 Camera Options", {NULL}, 0}, + + {IT_STRING | IT_CVAR, NULL, "Flipcam", {.cvar = &cv_flipcam[0]}, 30}, + {IT_STRING | IT_CVAR | IT_CV_INTEGERSTEP, NULL,"Camera Distance", {.cvar = &cv_cam_dist[0]}, 40}, + {IT_STRING | IT_CVAR | IT_CV_INTEGERSTEP, NULL,"Camera Height", {.cvar = &cv_cam_height[0]}, 50}, + {IT_STRING | IT_CVAR, NULL, "Camera Speed", {.cvar = &cv_cam_speed[0]}, 60}, + //{IT_STRING | IT_CVAR, NULL, "Camera Rotation Speed", {.cvar = &cv_cam_rotspeed[0]}, 70}, + + {IT_STRING | IT_CVAR, NULL, "Third Person Camera", {.cvar = &cv_chasecam[0]}, 85}, +}; + +static menuitem_t OP_Player2CamOptionsMenu[] = +{ + {IT_HEADER, NULL, "Player 2 Camera Options", {NULL}, 0}, + + {IT_STRING | IT_CVAR, NULL, "Flipcam", {.cvar = &cv_flipcam[1]}, 30}, + {IT_STRING | IT_CVAR | IT_CV_INTEGERSTEP, NULL,"Camera Distance", {.cvar = &cv_cam_dist[1]}, 40}, + {IT_STRING | IT_CVAR | IT_CV_INTEGERSTEP, NULL,"Camera Height", {.cvar = &cv_cam_height[1]}, 50}, + {IT_STRING | IT_CVAR, NULL, "Camera Speed", {.cvar = &cv_cam_speed[1]}, 60}, + //{IT_STRING | IT_CVAR, NULL, "Camera Rotation Speed", {.cvar = &cv_cam_rotspeed[0]}, 70}, + + {IT_STRING | IT_CVAR, NULL, "Third Person Camera", {.cvar = &cv_chasecam[1]}, 85}, +}; + +static menuitem_t OP_Player3CamOptionsMenu[] = +{ + {IT_HEADER, NULL, "Player 3 Camera Options", {NULL}, 0}, + + {IT_STRING | IT_CVAR, NULL, "Flipcam", {.cvar = &cv_flipcam[2]}, 30}, + {IT_STRING | IT_CVAR | IT_CV_INTEGERSTEP, NULL,"Camera Distance", {.cvar = &cv_cam_dist[2]}, 40}, + {IT_STRING | IT_CVAR | IT_CV_INTEGERSTEP, NULL,"Camera Height", {.cvar = &cv_cam_height[2]}, 50}, + {IT_STRING | IT_CVAR, NULL, "Camera Speed", {.cvar = &cv_cam_speed[2]}, 60}, + //{IT_STRING | IT_CVAR, NULL, "Camera Rotation Speed", {.cvar = &cv_cam_rotspeed[0]}, 70}, + + {IT_STRING | IT_CVAR, NULL, "Third Person Camera", {.cvar = &cv_chasecam[2]}, 85}, +}; + +static menuitem_t OP_Player4CamOptionsMenu[] = +{ + {IT_HEADER, NULL, "Player 4 Camera Options", {NULL}, 0}, + + {IT_STRING | IT_CVAR, NULL, "Flipcam", {.cvar = &cv_flipcam[3]}, 30}, + {IT_STRING | IT_CVAR | IT_CV_INTEGERSTEP, NULL,"Camera Distance", {.cvar = &cv_cam_dist[3]}, 40}, + {IT_STRING | IT_CVAR | IT_CV_INTEGERSTEP, NULL,"Camera Height", {.cvar = &cv_cam_height[3]}, 50}, + {IT_STRING | IT_CVAR, NULL, "Camera Speed", {.cvar = &cv_cam_speed[3]}, 60}, + //{IT_STRING | IT_CVAR, NULL, "Camera Rotation Speed", {.cvar = &cv_cam_rotspeed[0]}, 70}, + + {IT_STRING | IT_CVAR, NULL, "Third Person Camera", {.cvar = &cv_chasecam[3]}, 85}, }; static menuitem_t OP_ControlsMenu[] = @@ -1990,6 +2061,12 @@ menu_t OP_HUDOptionsDef = NULL }; +menu_t OP_CamOptionsDef = DEFAULTMENUSTYLE(MN_NONE, NULL, OP_CamOptionsMenu, &OP_MainDef, 30, 30); +menu_t OP_Player1CamOptionsDef = DEFAULTMENUSTYLE(MN_NONE, NULL, OP_Player1CamOptionsMenu, &OP_CamOptionsDef, 30, 30); +menu_t OP_Player2CamOptionsDef = DEFAULTMENUSTYLE(MN_NONE, NULL, OP_Player2CamOptionsMenu, &OP_CamOptionsDef, 30, 30); +menu_t OP_Player3CamOptionsDef = DEFAULTMENUSTYLE(MN_NONE, NULL, OP_Player3CamOptionsMenu, &OP_CamOptionsDef, 30, 30); +menu_t OP_Player4CamOptionsDef = DEFAULTMENUSTYLE(MN_NONE, NULL, OP_Player4CamOptionsMenu, &OP_CamOptionsDef, 30, 30); + menu_t OP_ChatOptionsDef = DEFAULTMENUSTYLE(MN_NONE, "M_HUD", OP_ChatOptionsMenu, &OP_HUDOptionsDef, 30, 30); menu_t OP_GameOptionsDef = DEFAULTMENUSTYLE(MN_NONE, "M_GAME", OP_GameOptionsMenu, &OP_MainDef, 30, 30); @@ -2364,33 +2441,23 @@ static void M_ChangeCvar(INT32 choice) choice = (choice<<1) - 1; - if (((currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_SLIDER) - ||((currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_INVISSLIDER) - ||((currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_NOMOD)) + if (cv->flags & CV_FLOAT) { - if (cv == &cv_digmusicvolume || cv == &cv_soundvolume) + if (((currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_SLIDER) + ||((currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_INVISSLIDER) + ||((currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_NOMOD) + || !(currentMenu->menuitems[itemOn].status & IT_CV_INTEGERSTEP)) { - choice *= 5; + char s[20]; + float n = FIXED_TO_FLOAT(cv->value)+(choice)*(1.0f/16.0f); + sprintf(s,"%ld%s",(long)n,M_Ftrim(n)); + CV_Set(cv,s); } - - CV_SetValue(cv,cv->value+choice); - } - else if (cv->flags & CV_FLOAT) - { - char s[20]; - sprintf(s,"%f",FIXED_TO_FLOAT(cv->value)+(choice)*(1.0f/16.0f)); - CV_Set(cv,s); + else + CV_SetValue(cv,FIXED_TO_FLOAT(cv->value)+(choice)); } else - { - if (cv == &cv_nettimeout || cv == &cv_jointimeout) - choice *= (TICRATE/7); - else if (cv == &cv_maxsend) - choice *= 512; - else if (cv == &cv_maxping) - choice *= 50; CV_AddValue(cv,choice); - } } static boolean M_ChangeStringCvar(INT32 choice) @@ -2494,7 +2561,9 @@ boolean M_Responder(event_t *ev) if (dedicated || (demo.playback && demo.title) || gamestate == GS_INTRO || gamestate == GS_CUTSCENE || gamestate == GS_GAMEEND - || gamestate == GS_CREDITS || gamestate == GS_EVALUATION) + || gamestate == GS_CREDITS || gamestate == GS_EVALUATION + || gamestate == GS_BLANCREDITS + ) return false; if (CON_Ready() && gamestate != GS_WAITINGPLAYERS) @@ -6270,9 +6339,10 @@ static void M_Options(INT32 choice) (void)choice; // if the player is not admin or server, disable gameplay & server options - OP_MainMenu[4].status = OP_MainMenu[5].status = (Playing() && !(server || IsPlayerAdmin(consoleplayer))) ? (IT_GRAYEDOUT) : (IT_STRING|IT_SUBMENU); + OP_MainMenu[5].status = (Playing() && !(server || IsPlayerAdmin(consoleplayer))) ? (IT_GRAYEDOUT) : (IT_STRING|IT_SUBMENU); - OP_MainMenu[8].status = (Playing()) ? (IT_GRAYEDOUT) : (IT_STRING|IT_CALL); // Play credits + OP_MainMenu[9].status = (Playing()) ? (IT_GRAYEDOUT) : (IT_STRING|IT_CALL); // Play credits + OP_MainMenu[10].status = (Playing()) ? (IT_GRAYEDOUT) : (IT_STRING|IT_CALL); // Play credits #ifdef HAVE_DISCORDRPC OP_DataOptionsMenu[4].status = (Playing()) ? (IT_GRAYEDOUT) : (IT_STRING|IT_SUBMENU); // Erase data @@ -6773,6 +6843,14 @@ static void M_Credits(INT32 choice) F_StartCredits(); } +static void M_BlanCredits(INT32 choice) +{ + (void)choice; + cursaveslot = -2; + M_ClearMenus(true); + F_BlanStartCredits(); +} + /*static void M_CustomLevelSelect(INT32 choice) { INT32 ul = skyRoomMenuTranslations[choice-1]; diff --git a/src/p_mobj.c b/src/p_mobj.c index b7963dc94..dc2c82439 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -4151,8 +4151,20 @@ static void P_RefreshItemCapsuleParts(mobj_t *mobj) { switch (itemType) { + case KITEM_SNEAKER: + if (mobj->movecount - 1 > K_GetMultItemFrame(mobj->movecount, 2)) + count = mobj->movecount; + break; case KITEM_ORBINAUT: // only display the number when the sprite no longer changes - if (mobj->movecount - 1 > K_GetOrbinautItemFrame(mobj->movecount)) + if (mobj->movecount - 1 > K_GetMultItemFrame(mobj->movecount, 3)) + count = mobj->movecount; + break; + case KITEM_BANANA: // only display the number when the sprite no longer changes + if (mobj->movecount - 1 > K_GetMultItemFrame(mobj->movecount, 3)) + count = mobj->movecount; + break; + case KITEM_JAWZ: // only display the number when the sprite no longer changes + if (mobj->movecount - 1 > K_GetMultItemFrame(mobj->movecount, 1)) count = mobj->movecount; break; case KITEM_SUPERRING: // always display the number, and multiply it by 5 @@ -6048,25 +6060,7 @@ static void P_MobjSceneryThink(mobj_t *mobj) { P_SetMobjState(mobj, S_PLAYERARROW_BOX); - switch (mobj->target->player->itemtype) - { - case KITEM_ORBINAUT: - mobj->tracer->sprite = SPR_ITMO; - mobj->tracer->frame = FF_FULLBRIGHT|K_GetOrbinautItemFrame(mobj->target->player->itemamount); - break; - case KITEM_INVINCIBILITY: - mobj->tracer->sprite = SPR_ITMI; - mobj->tracer->frame = FF_FULLBRIGHT|K_GetInvincibilityItemFrame(); - break; - case KITEM_SAD: - mobj->tracer->sprite = SPR_ITEM; - mobj->tracer->frame = FF_FULLBRIGHT; - break; - default: - mobj->tracer->sprite = SPR_ITEM; - mobj->tracer->frame = FF_FULLBRIGHT|(mobj->target->player->itemtype); - break; - } + K_UpdateMobjItemOverlay(mobj->tracer, mobj->target->player->itemtype,mobj->target->player->itemamount); if (mobj->target->player->itemflags & IF_ITEMOUT) { @@ -6322,6 +6316,7 @@ static boolean P_MobjDeadThink(mobj_t *mobj) { case MT_PLAYER: /// \todo Have the player's dead body completely finish its animation even if they've already respawned. + if (!mobj->fuse) { // Go away. /// \todo Actually go ahead and remove mobj completely, and fix any bugs and crashes doing this creates. Chasecam should stop moving, and F12 should never return to it. @@ -7410,9 +7405,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj) mobj->extravalue2 = mobj->target->player->bubbleblowup; P_SetScale(mobj, (mobj->destscale = scale)); - mobj->flags &= ~(MF_NOCLIPTHING); P_MoveOrigin(mobj, mobj->target->x, mobj->target->y, mobj->target->z); - mobj->flags |= MF_NOCLIPTHING; break; } case MT_FLAMESHIELD: diff --git a/src/p_saveg.c b/src/p_saveg.c index 557b95200..c5b7d7feb 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -246,6 +246,7 @@ static void P_NetArchivePlayers(savebuffer_t *save) WRITEUINT32(save->p, players[i].distancetofinishprev); WRITEUINT32(save->p, K_GetWaypointHeapIndex(players[i].currentwaypoint)); WRITEUINT32(save->p, K_GetWaypointHeapIndex(players[i].nextwaypoint)); + WRITEUINT16(save->p, players[i].bigwaypointgap); WRITEUINT32(save->p, players[i].airtime); WRITEUINT8(save->p, players[i].startboost); @@ -348,6 +349,9 @@ static void P_NetArchivePlayers(savebuffer_t *save) WRITEUINT16(save->p, players[i].breathTimer); + WRITEUINT8(save->p, players[i].lastsafelap); + WRITEUINT8(save->p, players[i].lastsafestarpost); + WRITEUINT8(save->p, players[i].typing_timer); WRITEUINT8(save->p, players[i].typing_duration); @@ -552,7 +556,9 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) players[i].distancetofinish = READUINT32(save->p); players[i].distancetofinishprev = READUINT32(save->p); players[i].currentwaypoint = (waypoint_t *)(size_t)READUINT32(save->p); + players[i].nextwaypoint = (waypoint_t *)(size_t)READUINT32(save->p); + players[i].bigwaypointgap = READUINT16(save->p); players[i].airtime = READUINT32(save->p); players[i].startboost = READUINT8(save->p); @@ -655,6 +661,9 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) players[i].breathTimer = READUINT16(save->p); + players[i].lastsafelap = READUINT8(save->p); + players[i].lastsafestarpost = READUINT8(save->p); + players[i].typing_timer = READUINT8(save->p); players[i].typing_duration = READUINT8(save->p); diff --git a/src/p_setup.c b/src/p_setup.c index 96b4228ce..7dc990526 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -427,6 +427,7 @@ static void P_ClearSingleMapHeaderInfo(INT16 num) mapheaderinfo[num]->palette = UINT16_MAX; mapheaderinfo[num]->encorepal = UINT16_MAX; mapheaderinfo[num]->numlaps = NUMLAPS_DEFAULT; + mapheaderinfo[num]->lapspersection = 1; mapheaderinfo[num]->unlockrequired = -1; mapheaderinfo[num]->levelselect = 0; mapheaderinfo[num]->levelflags = 0; @@ -7726,7 +7727,7 @@ static boolean P_SetMapNamespace(void) else if(fastcmp(tkn, "ringracers")) { mapnamespace = MNS_RINGRACERS; - CONS_Alert(CONS_WARNING, "Ring Racers Map deteced. BlanKart has basic support for Ring Racers maps but does not fully support them. Consider converting your map to BlanKart format, consult documentation for help.\n"); + CONS_Alert(CONS_WARNING, "Ring Racers Map detected. BlanKart has basic support for Ring Racers maps but does not fully support them. Consider converting your map to BlanKart format, consult documentation for help.\n"); } else if (fastcmp(tkn, "srb2")) { diff --git a/src/p_spec.c b/src/p_spec.c index 224d24516..f45338f8a 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -1970,11 +1970,23 @@ void P_SwitchWeather(preciptype_t newWeather) P_SpawnPrecipitation(); } +static boolean K_IgnoreFinishLine(player_t *player) +{ + // If potential lap cheating has been detected, do not + // interact with the finish line at all. + if (player->bigwaypointgap) + return true; + + return false; +} + // Passed over the finish line forwards static void K_HandleLapIncrement(player_t *player) { if (player) { + if (K_IgnoreFinishLine(player)) + return; if (((numbosswaypoints > 0) ? (player->starpostnum >= (numstarposts - (numstarposts/2))) : (player->starpostnum == numstarposts)) || (player->laps == 0)) { size_t i = 0; @@ -1992,6 +2004,9 @@ static void K_HandleLapIncrement(player_t *player) player->starposttime = player->realtime; player->starpostnum = 0; + player->lastsafestarpost = 0; + player->laps++; + if (mapheaderinfo[gamemap - 1]->levelflags & LF_SECTIONRACE) { @@ -2010,7 +2025,8 @@ static void K_HandleLapIncrement(player_t *player) player->starpostangle = player->starpostx = player->starposty = player->starpostz = player->starpostflip = 0; } - player->laps++; + if (!cv_kartdebuglap.value && player->laps == 1) + player->pflags |= PF_TRUSTWAYPOINTS; K_UpdateAllPlayerPositions(); // Set up lap animation vars diff --git a/src/p_tick.c b/src/p_tick.c index 2bfdfd34c..6da044264 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -665,13 +665,6 @@ void P_Ticker(boolean run) players[i].jointime++; } - if (run) - { - // Update old view state BEFORE ticking so resetting - // the old interpolation state from game logic works. - R_UpdateViewInterpolation(); - } - if (objectplacing) { if (OP_FreezeObjectplace()) @@ -776,30 +769,33 @@ void P_Ticker(boolean run) // Plays the music after the starting countdown. else { - if (!(gametyperules & GTR_FREEROAM) && !(titlemapinaction == TITLEMAP_RUNNING)) + if (!(titlemapinaction == TITLEMAP_RUNNING)) { - if (leveltime == starttime-(3*TICRATE)) + if (!(gametyperules & GTR_FREEROAM)) { - S_StartSound(NULL, sfx_s3ka7); // 3, + if (leveltime == starttime-(3*TICRATE)) + { + S_StartSound(NULL, sfx_s3ka7); // 3, + } + else if ((leveltime == starttime-(2*TICRATE)) || (leveltime == starttime-TICRATE)) + { + S_StartSound(NULL, sfx_s3ka7); // 2, 1, + } + else if (leveltime == starttime) + { + S_StartSound(NULL, sfx_s3kad); // GO! + } } - else if ((leveltime == starttime-(2*TICRATE)) || (leveltime == starttime-TICRATE)) - { - S_StartSound(NULL, sfx_s3ka7); // 2, 1, - } - else if (leveltime == starttime) - { - S_StartSound(NULL, sfx_s3kad); // GO! - } - } - if (!(gametyperules & GTR_FREEROAM) && leveltime < starttime) // SRB2Kart - S_ChangeMusicInternal((encoremode ? "estart" : "kstart"), false); // yes this will be spammed otherwise encore and some stuff WILL overwrite it - else if (leveltime == starttime) // The GO! sound stops the level start ambience - S_StopMusic(); - else if (leveltime == starttime + (TICRATE/2)) // Plays the music after the starting countdown. - { - S_ChangeMusicEx(mapmusname, mapmusflags, true, mapmusposition, 0, 0); - S_ShowMusicCredit(); + if (!(gametyperules & GTR_FREEROAM) && leveltime < starttime) // SRB2Kart + S_ChangeMusicInternal((encoremode ? "estart" : "kstart"), false); // yes this will be spammed otherwise encore and some stuff WILL overwrite it + else if (leveltime == starttime) // The GO! sound stops the level start ambience + S_StopMusic(); + else if (leveltime == starttime + (TICRATE/2)) // Plays the music after the starting countdown. + { + S_ChangeMusicEx(mapmusname, mapmusflags, true, mapmusposition, 0, 0); + S_ShowMusicCredit(); + } } } @@ -824,7 +820,7 @@ void P_Ticker(boolean run) if (starttime > introtime && leveltime == starttime) { - ACS_RunPositionScript(); + ACS_RunRaceStartScript(); } if (timelimitintics > 0 && leveltime == (timelimitintics + starttime + 1)) diff --git a/src/p_user.c b/src/p_user.c index 7b3af75fc..8868f830b 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -61,6 +61,8 @@ #include "k_color.h" #include "k_follower.h" +#include "acs/interface.h" + #ifdef HW3SOUND #include "hardware/hw3sound.h" #endif @@ -1324,6 +1326,8 @@ void P_DoPlayerExit(player_t *player) if (player == &players[consoleplayer]) demo.savebutton = leveltime; + + ACS_RunPlayerFinishScript(player); } // @@ -1832,8 +1836,6 @@ static void P_3dMovement(player_t *player) // Do not let the player control movement if not onground. // SRB2Kart: pogo spring and speed bumps are supposed to control like you're on the ground onground = (P_IsObjectOnGround(player->mo) || (player->pogospring)); - - player->aiming = cmd->aiming<exiting || mapreset) || (P_PlayerInPain(player) && !onground))) @@ -1954,24 +1956,60 @@ static void P_3dMovement(player_t *player) boolean P_CanPlayerTurn(player_t *player, ticcmd_t *cmd) { - if (player->spectator || objectplacing) // Spectators come first. + if (player->spectator || objectplacing) + { + // Spectators come first. return true; + } - if (!(gametyperules & GTR_FREEROAM) && !(leveltime > starttime)) // You can't move yet. - return false; - - if ((cmd->buttons & BT_ACCELERATE) && (cmd->buttons & BT_BRAKE)) // You are rubberburn turning. - return true; - - if (player->respawn) // You are Respawning. - return true; - - if (player->mo && player->speed == 0) // You need to be moving. + if (((gametyperules & GTR_FREEROAM) || (leveltime > starttime)) + && (cmd->buttons & BT_ACCELERATE) + && (cmd->buttons & BT_BRAKE)) + { + // You are rubberburn turning. + return true; + } + + if (player->respawn) + { + // You are Respawning. + return true; + } + + if (player->mo && player->speed == 0) + { + // You need to be moving. return false; + } return true; } +static void P_UpdatePlayerAiming(player_t *player) +{ + ticcmd_t *cmd = &player->cmd; + int i; + + if (!cv_allowmlook.value || player->spectator == false) + { + player->aiming = 0; + } + else + { + player->aiming += (cmd->aiming << TICCMD_REDUCE); + player->aiming = G_ClipAimingPitch((INT32*) &player->aiming); + } + + for (i = 0; i <= r_splitscreen; i++) + { + if (player == &players[displayplayers[i]]) + { + localaiming[i] = player->aiming; + break; + } + } +} + // // P_UpdatePlayerAngle // @@ -1983,7 +2021,6 @@ static void P_UpdatePlayerAngle(player_t *player) boolean add_delta = true; ticcmd_t *cmd = &player->cmd; angle_t anglechange = player->angleturn; - int i; // Kart: store the current turn range for later use if (P_CanPlayerTurn(player, cmd)) @@ -2031,14 +2068,7 @@ static void P_UpdatePlayerAngle(player_t *player) player->angleturn = anglechange; player->mo->angle = player->angleturn; - for (i = 0; i <= r_splitscreen; i++) - { - if (player == &players[displayplayers[i]]) - { - localaiming[i] = player->aiming; - break; - } - } + P_UpdatePlayerAiming(player); } static void P_UpdateBotAngle(player_t* player) @@ -2058,6 +2088,7 @@ static void P_SpectatorMovement(player_t *player) ticcmd_t *cmd = &player->cmd; player->mo->angle = cmd->angle<<16; + P_UpdatePlayerAiming(player); ticruned++; if (!(cmd->flags & TICCMD_RECEIVED)) @@ -2586,6 +2617,24 @@ static void P_DeathThink(player_t *player) else player->karthud[khud_timeovercam] = 0; + // Set players next respawn point to the next waypoint + // If they die while still in respawn state for extra safety. + if (player->nextwaypoint && player->respawn > 0) + { + mobj_t *currentwaypoint = player->currentwaypoint->mobj; + mobj_t *safewaypoint = player->nextwaypoint->mobj; + angle_t respawnangle = R_PointToAngle2(currentwaypoint->x, currentwaypoint->y, safewaypoint->x, safewaypoint->y); + + player->starposttime = player->realtime; + player->starpostz = safewaypoint->spawnpoint->z >> FRACBITS; + player->starpostflip = (safewaypoint->flags2 & MF2_OBJECTFLIP); + player->starpostangle = respawnangle; + + // Then do x and y + player->starpostx = safewaypoint->x >> FRACBITS; + player->starposty = safewaypoint->y >> FRACBITS; + } + K_KartPlayerHUDUpdate(player); if (player->pflags & PF_NOCONTEST) diff --git a/src/v_video.c b/src/v_video.c index a6f66e32c..c0e079a40 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -2539,6 +2539,12 @@ void V_DrawRightAlignedThinStringAtFixed(fixed_t x, fixed_t y, INT32 option, con V_DrawThinStringAtFixed(x, y, option, string); } +void V_DrawCenteredStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *string) +{ + x -= (V_StringWidth(string, option) / 2) * FRACUNIT; + V_DrawStringAtFixed(x, y, option, string); +} + // Draws a number using the PING font thingy. // TODO: Merge number drawing functions into one with "font name" selection. diff --git a/src/v_video.h b/src/v_video.h index 43caa9987..f9f732a95 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -281,6 +281,9 @@ void V_DrawCenteredSmallString(INT32 x, INT32 y, INT32 option, const char *strin void V_DrawCenteredSmallStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *string); void V_DrawRightAlignedSmallString(INT32 x, INT32 y, INT32 option, const char *string); +// Draw a centered string. +void V_DrawCenteredStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *string); + // draw a string using the tny_font #define V_DrawThinString( x,y,option,string ) \ V__DrawDupxString (x,y,FRACUNIT,option,TINY_FONT,string)