From fbb7335011ef4ff8d6a40096889b3f66f22f0a4a Mon Sep 17 00:00:00 2001 From: NepDisk Date: Fri, 7 Mar 2025 17:30:53 -0500 Subject: [PATCH 01/27] Revert "Fix Bubble Shield duplicate collisions" This doesn't really apply to us since we don't have hitlag and it causes problems. This reverts commit b048467aa2aa814d95ac4ae6b4d7bb670a4313ed. --- src/k_collide.c | 13 +------------ src/p_mobj.c | 2 -- 2 files changed, 1 insertion(+), 14 deletions(-) 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/p_mobj.c b/src/p_mobj.c index b7963dc94..ef02fc917 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -7410,9 +7410,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: From 65f992fda8850928dbc18b28ab6635a4a92283b7 Mon Sep 17 00:00:00 2001 From: NepDisk Date: Fri, 7 Mar 2025 17:33:31 -0500 Subject: [PATCH 02/27] Oops From 8df4f4966b799f7830c0ddc36d192ceb583b48bb Mon Sep 17 00:00:00 2001 From: NepDisk Date: Fri, 7 Mar 2025 20:29:12 -0500 Subject: [PATCH 03/27] Fix goof in turn check --- src/p_user.c | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/src/p_user.c b/src/p_user.c index 7eb46fdea..15551ad7c 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -1954,20 +1954,31 @@ 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; } From b9ad30c9e2afa2ee6e212b21a71cb62e400e75c5 Mon Sep 17 00:00:00 2001 From: NepDisk Date: Sat, 8 Mar 2025 11:00:54 -0500 Subject: [PATCH 04/27] Add BlanKart Credits and append new scene to intro --- src/console.c | 2 +- src/d_main.cpp | 6 + src/d_netcmd.c | 2 +- src/deh_tables.c | 1 + src/f_finale.c | 327 +++++++++++++++++++++++++++++++++++++---------- src/f_finale.h | 4 + src/g_game.c | 26 +++- src/g_state.h | 5 +- src/hu_stuff.c | 4 +- src/m_menu.c | 14 +- src/v_video.c | 6 + src/v_video.h | 3 + 12 files changed, 326 insertions(+), 74 deletions(-) diff --git a/src/console.c b/src/console.c index f7610c9fe..4b1081ab6 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_main.cpp b/src/d_main.cpp index 0ffb31350..d59268ae7 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -436,6 +436,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 cb2a6b385..4848f2235 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -5412,7 +5412,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/deh_tables.c b/src/deh_tables.c index a90c9dd74..67c0b6f88 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -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/f_finale.c b/src/f_finale.c index b3ab3ccff..232828798 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,31 +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) @@ -692,9 +748,9 @@ void F_StartCredits(void) void F_CreditDrawer(void) { UINT16 i; - fixed_t y = (80<>1); + fixed_t y = (80<>1; @@ -717,6 +773,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< Date: Sat, 8 Mar 2025 11:03:37 -0500 Subject: [PATCH 05/27] Add back missing titlescreen sound --- src/f_finale.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/f_finale.c b/src/f_finale.c index 232828798..987d6801e 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -1536,6 +1536,7 @@ void F_TitleScreenTicker(boolean run) S_ChangeMusic(menupres[MN_MAIN].musname, menupres[MN_MAIN].mustrack, menupres[MN_MAIN].muslooping); else S_ChangeMusicInternal("titles", looptitle); + S_StartSound(NULL, sfx_s23c); } } From 94b8c815bd7366e3a9b8e2cc59656eb86fef4c26 Mon Sep 17 00:00:00 2001 From: NepDisk Date: Sat, 8 Mar 2025 11:05:51 -0500 Subject: [PATCH 06/27] specfiy which is which --- src/m_menu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/m_menu.c b/src/m_menu.c index 480ebf353..df8e54b19 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -1082,8 +1082,8 @@ static menuitem_t OP_MainMenu[] = {IT_SUBMENU|IT_STRING, NULL, "Data Options...", {.submenu = &OP_DataOptionsDef}, 100}, {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, "Play Credits", {.routine = M_BlanCredits}, 140}, + {IT_CALL|IT_STRING, NULL, "Play Kart Credits", {.routine = M_Credits}, 130}, + {IT_CALL|IT_STRING, NULL, "Play BlanKart Credits", {.routine = M_BlanCredits}, 140}, }; static menuitem_t OP_ControlsMenu[] = From 8ee3624b27ac163df7ba48430c49277c23607c1e Mon Sep 17 00:00:00 2001 From: NepDisk Date: Sat, 8 Mar 2025 11:39:57 -0500 Subject: [PATCH 07/27] Fix kart credits crash --- src/f_finale.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/f_finale.c b/src/f_finale.c index 987d6801e..d1f71d867 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -716,8 +716,6 @@ static struct { #undef CREDITS_LEFT #undef CREDITS_RIGHT -static const UINT8 credits_numpics = sizeof(credits_pics)/sizeof(credits_pics[0]) - 1; - void F_StartCredits(void) { G_SetGamestate(GS_CREDITS); @@ -759,7 +757,7 @@ void F_CreditDrawer(void) V_DrawSciencePatch(320<>1; From dfab6cc94598f38510ce8831dd4a272fbc8a657e Mon Sep 17 00:00:00 2001 From: NepDisk Date: Sat, 8 Mar 2025 13:03:56 -0500 Subject: [PATCH 08/27] Add customcutscene to F_BlanStartCredits --- src/f_finale.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/f_finale.c b/src/f_finale.c index d1f71d867..9187caf09 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -943,6 +943,12 @@ void F_BlanStartCredits(void) // Just in case they're open ... somehow M_ClearMenus(true); + if (creditscutscene) + { + F_StartCustomCutscene(creditscutscene - 1, false, false); + return; + } + gameaction = ga_nothing; paused = false; CON_ToggleOff(); From d7162bed71bb9c04093d27bfb1ff66361d87747d Mon Sep 17 00:00:00 2001 From: NepDisk Date: Sat, 8 Mar 2025 15:28:33 -0500 Subject: [PATCH 09/27] implement safe guards for softlock respawns in New Waypoints if you fail to respawn properly (die while respawning) you get placed at the next waypoint. Code was also adjusted so the next waypoint can't be the same as your current one if you are detected as going backwards, helps on turns that sometimes face you towards the previous waypoint. Respawning angle for both respawn fallbacks now gets the angle by comparing x and y of the current waypoint and next waypoint to guarntee you are facing the correct direction --- src/k_kart.c | 22 ++++++++++++++++++++-- src/p_mobj.c | 22 ++++++++++++++++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 81a2bd4bc..63a2a6673 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -7891,7 +7891,23 @@ 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]; + } + } nextbestdelta = angledelta; nextbestmomdelta = momdelta; @@ -8207,12 +8223,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; diff --git a/src/p_mobj.c b/src/p_mobj.c index ef02fc917..cc2ad1b8d 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -6322,6 +6322,28 @@ 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->player) + { + // Set players next respawn point to the next waypoint + // If they die while still in respawn state for extra safety. + if (mobj->player->nextwaypoint && mobj->player->respawn > 0) + { + mobj_t *currentwaypoint = mobj->player->currentwaypoint->mobj; + mobj_t *safewaypoint = mobj->player->nextwaypoint->mobj; + angle_t respawnangle = R_PointToAngle2(currentwaypoint->x, currentwaypoint->y, safewaypoint->x, safewaypoint->y); + + mobj->player->starposttime = mobj->player->realtime; + mobj->player->starpostz = safewaypoint->spawnpoint->z >> FRACBITS; + mobj->player->starpostflip = (safewaypoint->flags2 & MF2_OBJECTFLIP); + mobj->player->starpostangle = respawnangle; + + // Then do x and y + mobj->player->starpostx = safewaypoint->x >> FRACBITS; + mobj->player->starposty = safewaypoint->y >> FRACBITS; + } + } + 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. From 7c650956712d8ebfb02634edf98aaf8a400d7a66 Mon Sep 17 00:00:00 2001 From: NepDisk Date: Sat, 8 Mar 2025 15:40:30 -0500 Subject: [PATCH 10/27] yen --- src/k_kart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_kart.c b/src/k_kart.c index 63a2a6673..6d6617d12 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -7900,7 +7900,7 @@ static boolean K_SetPlayerNextWaypoint(player_t *player) continue; } - if (waypoint->nextwaypoints[j] == waypoint)\ + if (waypoint->nextwaypoints[j] == waypoint) { continue; } From da01a9185207caeedfa99d8b64e83fcfc8cab5d9 Mon Sep 17 00:00:00 2001 From: NepDisk Date: Sat, 8 Mar 2025 21:08:42 -0500 Subject: [PATCH 11/27] Break from loop when valid target is found --- src/k_kart.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/k_kart.c b/src/k_kart.c index 6d6617d12..2047df23f 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -7906,6 +7906,7 @@ static boolean K_SetPlayerNextWaypoint(player_t *player) } bestwaypoint = waypoint->nextwaypoints[j]; + break; } } From 033b65a70b6b3b6f75e1371d81cb4da8d41b30d5 Mon Sep 17 00:00:00 2001 From: NepDisk Date: Sun, 9 Mar 2025 09:30:21 -0400 Subject: [PATCH 12/27] Move Waypoint check code to P_DeathThink --- src/p_mobj.c | 21 --------------------- src/p_user.c | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index cc2ad1b8d..11076b381 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -6323,27 +6323,6 @@ 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->player) - { - // Set players next respawn point to the next waypoint - // If they die while still in respawn state for extra safety. - if (mobj->player->nextwaypoint && mobj->player->respawn > 0) - { - mobj_t *currentwaypoint = mobj->player->currentwaypoint->mobj; - mobj_t *safewaypoint = mobj->player->nextwaypoint->mobj; - angle_t respawnangle = R_PointToAngle2(currentwaypoint->x, currentwaypoint->y, safewaypoint->x, safewaypoint->y); - - mobj->player->starposttime = mobj->player->realtime; - mobj->player->starpostz = safewaypoint->spawnpoint->z >> FRACBITS; - mobj->player->starpostflip = (safewaypoint->flags2 & MF2_OBJECTFLIP); - mobj->player->starpostangle = respawnangle; - - // Then do x and y - mobj->player->starpostx = safewaypoint->x >> FRACBITS; - mobj->player->starposty = safewaypoint->y >> FRACBITS; - } - } - 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. diff --git a/src/p_user.c b/src/p_user.c index 15551ad7c..b61518acf 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -2597,6 +2597,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) From 8ed52a3a9de7577e2751e42d0e94edf6b9efca03 Mon Sep 17 00:00:00 2001 From: NepDisk Date: Sun, 9 Mar 2025 11:07:49 -0400 Subject: [PATCH 13/27] G_Ticker fixes from RR https://git.do.srb2.org/KartKrew/RingRacers/-/commit/ab0094b8d0b72d651cfefe9847a367fcf1a0ebcc https://git.do.srb2.org/KartKrew/RingRacers/-/commit/95216563a6980576d31f219eb770adc72c134a9d --- src/d_clisrv.c | 15 ++++++++++++++- src/g_game.c | 2 +- src/p_tick.c | 7 ------- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 01a81cfbd..c9edef5c3 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" @@ -5598,6 +5599,8 @@ boolean TryRunTics(tic_t realtics) } else { + boolean tickInterp = true; + // run the count * tics while (neededtic > gametic) { @@ -5625,7 +5628,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/g_game.c b/src/g_game.c index 24ae2be76..330719adb 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2071,7 +2071,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++) diff --git a/src/p_tick.c b/src/p_tick.c index 862956742..4962fada9 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()) From 58e243a20c1c8546ee9f9e85f7bdbeb62d422957 Mon Sep 17 00:00:00 2001 From: NepDisk Date: Sun, 9 Mar 2025 11:20:33 -0400 Subject: [PATCH 14/27] Don't Do levelstart or Map Music in Titlemap --- src/p_tick.c | 43 +++++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/src/p_tick.c b/src/p_tick.c index 4962fada9..d4c705658 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -769,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(); + } } } From bda7d26a0fc960895dfad6535f3670224db6ccb4 Mon Sep 17 00:00:00 2001 From: NepDisk Date: Sun, 9 Mar 2025 20:03:10 -0400 Subject: [PATCH 15/27] Port basic lap anticheat for new waypoints from RR --- src/d_netcmd.c | 1 + src/d_netcmd.h | 2 +- src/d_player.h | 6 +++- src/deh_soc.c | 2 ++ src/deh_tables.c | 2 +- src/doomstat.h | 1 + src/g_game.c | 13 ++++++++ src/k_hud.c | 5 +++ src/k_kart.c | 81 ++++++++++++++++++++++++++++++++++++++++----- src/k_kart.h | 3 ++ src/lua_maplib.c | 2 ++ src/lua_playerlib.c | 12 +++++++ src/p_saveg.c | 9 +++++ src/p_setup.c | 1 + src/p_spec.c | 18 +++++++++- 15 files changed, 145 insertions(+), 13 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 4848f2235..0ecb50e84 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); 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 67c0b6f88..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? diff --git a/src/doomstat.h b/src/doomstat.h index a148e2dd7..2789b6dbd 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -413,6 +413,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/g_game.c b/src/g_game.c index 330719adb..9f0c44b5c 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2359,6 +2359,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; @@ -2412,6 +2415,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; @@ -2479,6 +2485,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) @@ -2513,6 +2523,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; 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 2047df23f..4ffc56c20 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) @@ -8134,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(); } } @@ -8183,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)) && @@ -8209,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)) { @@ -8244,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) @@ -8819,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); } diff --git a/src/k_kart.h b/src/k_kart.h index 93624bc97..539cd5376 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 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/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..feec6a6d8 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; 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 From a7167bfbcd1a1928223716ebb1c3ff6890bdc2be Mon Sep 17 00:00:00 2001 From: NepDisk Date: Mon, 10 Mar 2025 10:34:24 -0400 Subject: [PATCH 16/27] Demo code updating Still has stuff to resolve but it kinda works now. Bots should turn correctly now but replays that have bots added at the race start don't play for whatever reason. Ghosts have been fixed and no longer crash the game after they end. --- src/g_demo.c | 266 +++++++++++++++++++++++++++++++++++---------------- src/g_demo.h | 15 +-- 2 files changed, 190 insertions(+), 91 deletions(-) diff --git a/src/g_demo.c b/src/g_demo.c index 62abc324f..334f53319 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -52,6 +52,7 @@ #include "k_bot.h" #include "k_color.h" #include "k_follower.h" +#include "k_grandprix.h" static CV_PossibleValue_t recordmultiplayerdemos_cons_t[] = {{0, "Disabled"}, {1, "Manual Save"}, {2, "Auto Save"}, {0, NULL}}; consvar_t cv_recordmultiplayerdemos = CVAR_INIT ("netdemo_record", "Manual Save", CV_SAVE, recordmultiplayerdemos_cons_t, NULL); @@ -124,10 +125,12 @@ demoghost *ghosts = NULL; #define DF_ATTACKSHIFT 1 #define DF_ENCORE 0x40 #define DF_MULTIPLAYER 0x80 // This demo was recorded in multiplayer mode! +#define DF_GRANDPRIX 0x0100 #define DEMO_SPECTATOR 0x01 #define DEMO_KICKSTART 0x02 #define DEMO_SHRINKME 0x04 +#define DEMO_BOT 0x08 // For demos #define ZT_FWD 0x0001 @@ -255,13 +258,73 @@ void G_ReadDemoExtraData(void) { extradata = READUINT8(demobuf.p); - if (extradata & DXD_RESPAWN) + if (extradata & DXD_JOINDATA) { - if (players[p].mo) + if (!playeringame[p]) { - // Is this how this should work..? - P_DamageMobj(players[p].mo, NULL, NULL, 1, DMG_INSTAKILL); + G_AddPlayer(p, p); } + + players[p].bot = !!(READUINT8(demobuf.p)); + if (players[p].bot) + { + 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); + } + } + if (extradata & DXD_PLAYSTATE) + { + i = READUINT8(demobuf.p); + + switch (i) { + case DXD_PST_PLAYING: + if (players[p].spectator == true) + { + if (players[p].bot) + { + players[p].spectator = false; + } + else + { + players[p].pflags |= PF_WANTSTOJOIN; + } + } + //CONS_Printf("player %s is despectating on tic %d\n", player_names[p], leveltime); + break; + + case DXD_PST_SPECTATING: + if (players[p].spectator) + { + players[p].pflags &= ~PF_WANTSTOJOIN; + } + else + { + if (players[p].mo) + { + P_DamageMobj(players[p].mo, NULL, NULL, 1, DMG_SPECTATOR); + } + P_SetPlayerSpectator(p); + } + + break; + + case DXD_PST_LEFT: + CL_RemovePlayer(p, 0); + break; + } + + G_ResetViews(); + + // maybe these are necessary? + K_CheckBumpers(); + P_CheckRacers(); + } + if (extradata & DXD_NAME) + { + // Name + M_Memcpy(player_names[p],demobuf.p,16); + demobuf.p += 16; } if (extradata & DXD_SKIN) { @@ -295,12 +358,6 @@ void G_ReadDemoExtraData(void) break; } } - if (extradata & DXD_NAME) - { - // Name - M_Memcpy(player_names[p],demobuf.p,16); - demobuf.p += 16; - } if (extradata & DXD_FOLLOWER) { // Set our follower @@ -320,47 +377,13 @@ void G_ReadDemoExtraData(void) } } } - if (extradata & DXD_PLAYSTATE) + if (extradata & DXD_RESPAWN) { - i = READUINT8(demobuf.p); - - switch (i) { - case DXD_PST_PLAYING: - players[p].pflags |= PF_WANTSTOJOIN; // fuck you - //CONS_Printf("player %s is despectating on tic %d\n", player_names[p], leveltime); - break; - - case DXD_PST_SPECTATING: - players[p].pflags &= ~PF_WANTSTOJOIN; // double-fuck you - if (!playeringame[p]) - { - CL_ClearPlayer(p); - playeringame[p] = true; - G_AddPlayer(p, p); - players[p].spectator = true; - //CONS_Printf("player %s is joining server on tic %d\n", player_names[p], leveltime); - } - else - { - //CONS_Printf("player %s is spectating on tic %d\n", player_names[p], leveltime); - players[p].spectator = true; - if (players[p].mo) - P_DamageMobj(players[p].mo, NULL, NULL, 1, DMG_INSTAKILL); - else - players[p].playerstate = PST_REBORN; - } - break; - - case DXD_PST_LEFT: - CL_RemovePlayer(p, 0); - break; + if (players[p].mo) + { + // Is this how this should work..? + P_DamageMobj(players[p].mo, NULL, NULL, 1, DMG_INSTAKILL); } - - G_ResetViews(); - - // maybe these are necessary? - K_CheckBumpers(); - P_CheckRacers(); } if (extradata & DXD_WEAPONPREF) { @@ -413,7 +436,44 @@ void G_WriteDemoExtraData(void) WRITEUINT8(demobuf.p, i); WRITEUINT8(demobuf.p, demo_extradata[i]); - //if (demo_extradata[i] & DXD_RESPAWN) has no extra data + if (demo_extradata[i] & DXD_JOINDATA) + { + WRITEUINT8(demobuf.p, (UINT8)players[i].bot); + if (players[i].bot) + { + WRITEUINT8(demobuf.p, players[i].botvars.difficulty); + WRITEUINT8(demobuf.p, players[i].botvars.diffincrease); // needed to avoid having to duplicate logic + WRITEUINT8(demobuf.p, (UINT8)players[i].botvars.rival); + } + } + if (demo_extradata[i] & DXD_PLAYSTATE) + { + UINT8 pst = DXD_PST_PLAYING; + + demo_writerng = 1; + + if (!playeringame[i]) + { + pst = DXD_PST_LEFT; + } + else if ( + players[i].spectator && + !(players[i].pflags & PF_WANTSTOJOIN) // <= fuck you specifically + ) + { + pst = DXD_PST_SPECTATING; + } + + WRITEUINT8(demobuf.p, pst); + } + if (demo_extradata[i] & DXD_NAME) + { + // Name + memset(name, 0, 16); + strncpy(name, player_names[i], 16); + M_Memcpy(demobuf.p,name,16); + demobuf.p += 16; + } if (demo_extradata[i] & DXD_SKIN) { // Skin @@ -434,14 +494,6 @@ void G_WriteDemoExtraData(void) M_Memcpy(demobuf.p,name,16); demobuf.p += 16; } - if (demo_extradata[i] & DXD_NAME) - { - // Name - memset(name, 0, 16); - strncpy(name, player_names[i], 16); - M_Memcpy(demobuf.p,name,16); - demobuf.p += 16; - } if (demo_extradata[i] & DXD_FOLLOWER) { // write follower @@ -460,19 +512,7 @@ void G_WriteDemoExtraData(void) demobuf.p += 16; } - if (demo_extradata[i] & DXD_PLAYSTATE) - { - demo_writerng = 1; - if (!playeringame[i]) - WRITEUINT8(demobuf.p, DXD_PST_LEFT); - else if ( - players[i].spectator && - !(players[i].pflags & PF_WANTSTOJOIN) // <= fuck you specifically - ) - WRITEUINT8(demobuf.p, DXD_PST_SPECTATING); - else - WRITEUINT8(demobuf.p, DXD_PST_PLAYING); - } + //if (demo_extradata[i] & DXD_RESPAWN) has no extra data if (demo_extradata[i] & DXD_WEAPONPREF) { WeaponPref_Save(&demobuf.p, i); @@ -1164,23 +1204,27 @@ void G_GhostTicker(void) UINT16 ziptic = READUINT8(g->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 From 431d504e8d9a912e352477f059796ef046859e68 Mon Sep 17 00:00:00 2001 From: NepDisk Date: Mon, 10 Mar 2025 12:36:08 -0400 Subject: [PATCH 17/27] Update ACS code and add playerfinishscript --- src/acs/interface.cpp | 32 +++++++++++++++++++++++++------- src/acs/interface.h | 14 ++++++++++++++ src/acs/thread.hpp | 1 + src/p_user.c | 4 ++++ 4 files changed, 44 insertions(+), 7 deletions(-) diff --git a/src/acs/interface.cpp b/src/acs/interface.cpp index 638e87957..2b1b5b050 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)); @@ -274,6 +269,29 @@ void ACS_RunLapScript(mobj_t *mo, line_t *line) map->scriptStartTypeForced(ACS_ST_LAP, scriptInfo); } +/*-------------------------------------------------- + void ACS_RunPlayerFinishScript(player_t *player) + + See header file for description. +--------------------------------------------------*/ +void ACS_RunPlayerFinishScript(player_t *player) +{ + 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, player->mo); + + scriptInfo.info = &info; + + map->scriptStartTypeForced(ACS_ST_FINISH, scriptInfo); +} + /*-------------------------------------------------- void ACS_RunPositionScript(void) diff --git a/src/acs/interface.h b/src/acs/interface.h index 06a90454c..11544cb21 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); diff --git a/src/acs/thread.hpp b/src/acs/thread.hpp index 478879d67..3f86fb54d 100644 --- a/src/acs/thread.hpp +++ b/src/acs/thread.hpp @@ -43,6 +43,7 @@ enum acs_scriptType_e 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_FINISH = 11, // FINISH: Runs when a player finishes }; // diff --git a/src/p_user.c b/src/p_user.c index b61518acf..bd4ac18b2 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); } // From 1935895f885f30a4ca949cbe07d9624b6e4bfd69 Mon Sep 17 00:00:00 2001 From: NepDisk Date: Mon, 10 Mar 2025 12:42:01 -0400 Subject: [PATCH 18/27] Remove Unused ACS code and rename POSITION script type --- src/acs/interface.cpp | 29 +++-------------------------- src/acs/interface.h | 14 ++------------ src/acs/thread.hpp | 10 ++++------ src/p_tick.c | 2 +- 4 files changed, 10 insertions(+), 45 deletions(-) diff --git a/src/acs/interface.cpp b/src/acs/interface.cpp index 2b1b5b050..13a53c04e 100644 --- a/src/acs/interface.cpp +++ b/src/acs/interface.cpp @@ -293,11 +293,11 @@ void ACS_RunPlayerFinishScript(player_t *player) } /*-------------------------------------------------- - void ACS_RunPositionScript(void) + void ACS_RunRaceStartScript(void) See header file for description. --------------------------------------------------*/ -void ACS_RunPositionScript(void) +void ACS_RunRaceStartScript(void) { Environment *env = &ACSEnv; @@ -305,7 +305,7 @@ void ACS_RunPositionScript(void) ACSVM::HubScope *const hub = global->getHubScope(0); ACSVM::MapScope *const map = hub->getMapScope(0); - map->scriptStartType(ACS_ST_POSITION, {}); + map->scriptStartType(ACS_ST_RACESTART, {}); } /*-------------------------------------------------- @@ -324,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 11544cb21..12acc1ca7 100644 --- a/src/acs/interface.h +++ b/src/acs/interface.h @@ -171,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); /*-------------------------------------------------- @@ -190,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 3f86fb54d..95d4f3a59 100644 --- a/src/acs/thread.hpp +++ b/src/acs/thread.hpp @@ -38,12 +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_FINISH = 11, // FINISH: Runs when a player finishes + 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/p_tick.c b/src/p_tick.c index d4c705658..66912d0db 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -820,7 +820,7 @@ void P_Ticker(boolean run) if (starttime > introtime && leveltime == starttime) { - ACS_RunPositionScript(); + ACS_RunRaceStartScript(); } if (timelimitintics > 0 && leveltime == (timelimitintics + starttime + 1)) From e8adbfb8b7e2d630409cf937e26ea89338a5f129 Mon Sep 17 00:00:00 2001 From: NepDisk Date: Mon, 10 Mar 2025 12:57:45 -0400 Subject: [PATCH 19/27] Fix typo in namespace code --- src/p_setup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_setup.c b/src/p_setup.c index feec6a6d8..7dc990526 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -7727,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")) { From 7ad73c040d0fa16e318e674ec0683043694b4668 Mon Sep 17 00:00:00 2001 From: NepDisk Date: Mon, 10 Mar 2025 14:14:07 -0400 Subject: [PATCH 20/27] Add Camera submenu from Saturn --- src/m_menu.c | 130 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 98 insertions(+), 32 deletions(-) diff --git a/src/m_menu.c b/src/m_menu.c index df8e54b19..83df350a2 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -305,6 +305,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 @@ -1070,20 +1074,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 Kart Credits", {.routine = M_Credits}, 130}, - {IT_CALL|IT_STRING, NULL, "Play BlanKart Credits", {.routine = M_BlanCredits}, 140}, + {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[] = @@ -2037,6 +2106,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); @@ -2411,33 +2486,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) @@ -6347,9 +6412,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 = 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 #ifdef HAVE_DISCORDRPC OP_DataOptionsMenu[4].status = (Playing()) ? (IT_GRAYEDOUT) : (IT_STRING|IT_SUBMENU); // Erase data From 178ba3b4a660ae64fa993a4eee1283388359adab Mon Sep 17 00:00:00 2001 From: NepDisk Date: Mon, 10 Mar 2025 14:16:22 -0400 Subject: [PATCH 21/27] Fix copy paste mistake --- src/m_menu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/m_menu.c b/src/m_menu.c index 83df350a2..44f73da0b 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -6412,7 +6412,7 @@ static void M_Options(INT32 choice) (void)choice; // if the player is not admin or server, disable gameplay & server options - OP_MainMenu[5].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 From 9d2c895b2af205451d058da4a4e7b2cde0289a2d Mon Sep 17 00:00:00 2001 From: NepDisk Date: Mon, 10 Mar 2025 14:20:23 -0400 Subject: [PATCH 22/27] Fix offset for options main status --- src/m_menu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/m_menu.c b/src/m_menu.c index 44f73da0b..c6438b3cc 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -6414,8 +6414,8 @@ static void M_Options(INT32 choice) // if the player is not admin or server, disable gameplay & server options 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 From 95750e50e415fa8cd19901f9b441317f83ba3d20 Mon Sep 17 00:00:00 2001 From: NepDisk Date: Mon, 10 Mar 2025 15:17:17 -0400 Subject: [PATCH 23/27] Implement MultiItems into capsules/dropped items as well --- src/info/sprites.h | 9 ++++++--- src/k_kart.c | 18 +++++++++++++++--- src/k_kart.h | 2 +- src/p_mobj.c | 34 ++++++++++++++-------------------- 4 files changed, 36 insertions(+), 27 deletions(-) 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_kart.c b/src/k_kart.c index 4ffc56c20..f08d74af3 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -10116,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) @@ -10174,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 539cd5376..7fa06f995 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -169,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/p_mobj.c b/src/p_mobj.c index 11076b381..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) { From 8884f7d3c9ce3957965001160588eaac144f6942 Mon Sep 17 00:00:00 2001 From: NepDisk Date: Mon, 10 Mar 2025 15:22:52 -0400 Subject: [PATCH 24/27] Add WumboSpasm to the credits --- src/f_finale.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/f_finale.c b/src/f_finale.c index 9187caf09..8fabfe37a 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -911,6 +911,7 @@ static const char *blancredits[] = { "", "\1Support Programming", "\"hayaunderscore\" aka \"DeltaKaynx\"", + "\"WumboSpasm\"", "", "\1External Programming", "\"xyzzy\"", From c6377f884f16df2480380382073b5ebc3b6000a3 Mon Sep 17 00:00:00 2001 From: NepDisk Date: Mon, 10 Mar 2025 17:18:12 -0400 Subject: [PATCH 25/27] Update some ACS documentation --- extras/ACS/BlanKart_ACS.cfg | 77 +++------------------------- extras/ACS/lib/inc/ACS/bkspecial.acs | 2 +- src/acs/call-funcs.cpp | 16 +++--- 3 files changed, 16 insertions(+), 79 deletions(-) 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; } From 4cf9c7f6b6ec41404b3a7968edee1d60f8adc8ea Mon Sep 17 00:00:00 2001 From: GenericHeroGuy Date: Mon, 10 Mar 2025 23:08:19 +0100 Subject: [PATCH 26/27] Make vertical aiming work again --- src/g_game.c | 4 ++-- src/g_game.h | 2 +- src/p_user.c | 40 ++++++++++++++++++++++++++++------------ 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 9f0c44b5c..24537c23c 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1240,12 +1240,12 @@ 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; } axis = PlayerJoyAxis(ssplayer, AXISLOOK); if (analogjoystickmove && axis != 0 && lookaxis && player->spectator) - cmd->aiming += (axis<<16) * screen_invert; + cmd->aiming += axis * screen_invert; // spring back if not using keyboard neither mouselookin' if (*kbl == false && !lookaxis && !mouseaiming) diff --git a/src/g_game.h b/src/g_game.h index e39b006a1..929685d6d 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -106,7 +106,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/p_user.c b/src/p_user.c index bd4ac18b2..f995b863d 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -1836,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))) @@ -1987,6 +1985,31 @@ boolean P_CanPlayerTurn(player_t *player, ticcmd_t *cmd) 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 // @@ -1998,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)) @@ -2046,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) @@ -2073,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)) @@ -2856,7 +2872,7 @@ static ticcmd_t *P_CameraCmd(camera_t *cam) kbl = false; // looking up/down - cmd->aiming += (mlooky<<19)*player_invert*screen_invert; + cmd->aiming += (mlooky<<3)*player_invert*screen_invert; axis = PlayerJoyAxis(1, AXISLOOK); From c719ad49aa51c5c1ec8475f4592d9b1303d3c5be Mon Sep 17 00:00:00 2001 From: GenericHeroGuy Date: Mon, 10 Mar 2025 23:19:36 +0100 Subject: [PATCH 27/27] MISSING BRACES AROUND INITIALIZER!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! amazing warning spam --- src/m_menu.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/m_menu.c b/src/m_menu.c index c6438b3cc..4f1361654 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -1093,7 +1093,7 @@ static menuitem_t OP_MainMenu[] = static menuitem_t OP_CamOptionsMenu[] = { - {IT_HEADER, NULL, "Camera Options", NULL, 0}, + {IT_HEADER, NULL, "Camera Options", {NULL}, 0}, {IT_STRING | IT_CVAR, NULL, "Lagless Camera", {.cvar = &cv_laglesscam}, 20}, @@ -1105,7 +1105,7 @@ static menuitem_t OP_CamOptionsMenu[] = static menuitem_t OP_Player1CamOptionsMenu[] = { - {IT_HEADER, NULL, "Player 1 Camera Options", NULL, 0}, + {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}, @@ -1118,7 +1118,7 @@ static menuitem_t OP_Player1CamOptionsMenu[] = static menuitem_t OP_Player2CamOptionsMenu[] = { - {IT_HEADER, NULL, "Player 2 Camera Options", NULL, 0}, + {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}, @@ -1131,7 +1131,7 @@ static menuitem_t OP_Player2CamOptionsMenu[] = static menuitem_t OP_Player3CamOptionsMenu[] = { - {IT_HEADER, NULL, "Player 3 Camera Options", NULL, 0}, + {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}, @@ -1144,7 +1144,7 @@ static menuitem_t OP_Player3CamOptionsMenu[] = static menuitem_t OP_Player4CamOptionsMenu[] = { - {IT_HEADER, NULL, "Player 4 Camera Options", NULL, 0}, + {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},