From 78492874416cf0502779e8d5bc12b32ce6edb9ca Mon Sep 17 00:00:00 2001 From: NepDisk Date: Fri, 21 Mar 2025 10:28:38 -0400 Subject: [PATCH 01/45] Sweep Midtexture lines to prevent gremlins --- src/p_maputl.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/p_maputl.c b/src/p_maputl.c index 867e2b1a4..f6568e23b 100644 --- a/src/p_maputl.c +++ b/src/p_maputl.c @@ -439,6 +439,12 @@ P_GetMidtextureTopBottom // Get the midtexture's height texheight = textures[texnum]->height << FRACBITS; + if (g_tm.sweep) + { + // Sweep Midtexture lines to prevent issues with some midtextures. + P_TestLine(linedef); + } + // Set texbottom and textop to the Z coordinates of the texture's boundaries #if 0 // don't remove this code unless solid midtextures From 1e47df835a480e60a4ecc628517d81dc1adce79b Mon Sep 17 00:00:00 2001 From: NepDisk Date: Fri, 21 Mar 2025 10:34:32 -0400 Subject: [PATCH 02/45] Don't waste bubble if it doesn't collide --- src/k_collide.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/k_collide.c b/src/k_collide.c index 591a22615..5709f587b 100644 --- a/src/k_collide.c +++ b/src/k_collide.c @@ -791,6 +791,10 @@ boolean K_BubbleShieldCollide(mobj_t *t1, mobj_t *t2) if (t2->type == MT_PLAYER) { + if (P_PlayerInPain(t2->player) + || t2->player->flashing || t2->player->hyudorotimer + || t2->player->justbumped || t2->scale > t1->scale + (mapobjectscale/8)) + return true; // Player Damage if (K_KartBouncing(t2, t1->target, false, true)) { From 42ddaa5b1f3e6d0b21039265f969e1e834b8593b Mon Sep 17 00:00:00 2001 From: NepDisk Date: Fri, 21 Mar 2025 13:36:10 -0400 Subject: [PATCH 03/45] Move itemblink and itemblinkmode to player_t to restore hyudoro behaviour --- src/d_player.h | 5 ++--- src/deh_tables.c | 2 -- src/k_hud.c | 4 ++-- src/k_kart.c | 53 +++++++++++++++++++++++---------------------- src/lua_playerlib.c | 16 ++++++++++---- src/p_inter.c | 4 ++-- src/p_saveg.c | 4 ++++ 7 files changed, 49 insertions(+), 39 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index d57f9e1f2..8e5f57f5f 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -228,9 +228,6 @@ typedef enum typedef enum { // Unsynced, HUD or clientsided effects - // Item box - khud_itemblink, // Item flashing after roulette, serves as a mashing indicator - khud_itemblinkmode, // Type of flashing: 0 = white (normal), 1 = red (mashing), 2 = rainbow (enhanced items) // Rings khud_ringlock, // Ring lock @@ -590,6 +587,8 @@ struct player_t UINT8 tripwireReboundDelay; // When failing Tripwire, brieftly lock out speed-based tripwire pass (anti-cheese) UINT16 itemroulette; // Used for the roulette when deciding what item to give you (was "pw_kartitem") + UINT16 itemblink; // Item flashing after roulette, serves as a mashing indicator. Also prevents item from being stolen. + UINT16 itemblinkmode; // Type of flashing: 0 = white (normal), 1 = red (mashing), 2 = rainbow (enhanced items) UINT8 roulettetype; // Used for the roulette, for deciding type (0 = normal, 1 = better, 2 = eggman mark) // Item held stuff diff --git a/src/deh_tables.c b/src/deh_tables.c index a524e7aa8..c0f42c9e7 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -554,8 +554,6 @@ const char *const KARTSTUFF_LIST[] = { }; const char *const KARTHUD_LIST[] = { - "ITEMBLINK", - "ITEMBLINKMODE", "RINGFRAME", "RINGTICS", diff --git a/src/k_hud.c b/src/k_hud.c index 027ada4e4..9da03988b 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -1017,11 +1017,11 @@ static void K_drawKartItem(void) localpatch = kp_nodraw; } - if (stplyr->karthud[khud_itemblink] && (leveltime & 1)) + if (stplyr->itemblink && (leveltime & 1)) { colormode = TC_BLINK; - switch (stplyr->karthud[khud_itemblinkmode]) + switch (stplyr->itemblinkmode) { case 2: localcolor = K_RainbowColor(leveltime); diff --git a/src/k_kart.c b/src/k_kart.c index 6cc84af15..2275721c5 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -1478,8 +1478,8 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd) if (player->roulettetype == 2) { player->eggmanexplode = 4*TICRATE; - //player->karthud[khud_itemblink] = TICRATE; - //player->karthud[khud_itemblinkmode] = 1; + //player->itemblink = TICRATE; + //player->itemblinkmode = 1; player->itemroulette = 0; player->roulettetype = 0; if (P_IsDisplayPlayer(player) && !demo.freecam) @@ -1493,8 +1493,8 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd) { K_KartGetItemResult(player, cv_kartdebugitem.value); player->itemamount = cv_kartdebugamount.value; - player->karthud[khud_itemblink] = TICRATE; - player->karthud[khud_itemblinkmode] = 2; + player->itemblink = TICRATE; + player->itemblinkmode = 2; player->itemroulette = 0; player->roulettetype = 0; if (P_IsDisplayPlayer(player) && !demo.freecam) @@ -1509,8 +1509,8 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd) SINT8 itemroll = P_RandomRange(KITEM_SNEAKER, NUMKARTITEMS - 1); K_KartGetItemResult(player, itemroll); - player->karthud[khud_itemblink] = TICRATE; - player->karthud[khud_itemblinkmode] = 0; + player->itemblink = TICRATE; + player->itemblinkmode = 0; player->itemroulette = 0; player->roulettetype = 0; if (P_IsDisplayPlayer(player)) @@ -1527,7 +1527,7 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd) if (mashed && ((K_RingsActive() == true) && (modeattacking || cv_superring.value))) // ANY mashed value? You get rings. { K_KartGetItemResult(player, KITEM_SUPERRING); - player->karthud[khud_itemblinkmode] = 1; + player->itemblinkmode = 1; if (P_IsDisplayPlayer(player)) S_StartSound(NULL, sfx_itrolm); } @@ -1537,7 +1537,7 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd) K_KartGetItemResult(player, KITEM_SNEAKER); else // Default to sad if nothing's enabled... K_KartGetItemResult(player, KITEM_SAD); - player->karthud[khud_itemblinkmode] = 0; + player->itemblinkmode = 0; if (P_IsDisplayPlayer(player)) S_StartSound(NULL, sfx_itrolf); } @@ -1547,20 +1547,20 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd) if (mashed && (bossinfo.boss || cv_banana.value) && !itembreaker) // ANY mashed value? You get a banana. { K_KartGetItemResult(player, KITEM_BANANA); - player->karthud[khud_itemblinkmode] = 1; + player->itemblinkmode = 1; if (P_IsDisplayPlayer(player)) S_StartSound(NULL, sfx_itrolm); } else if (bossinfo.boss) { K_KartGetItemResult(player, KITEM_ORBINAUT); - player->karthud[khud_itemblinkmode] = 0; + player->itemblinkmode = 0; if (P_IsDisplayPlayer(player)) S_StartSound(NULL, sfx_itrolf); } } - player->karthud[khud_itemblink] = TICRATE; + player->itemblink = TICRATE; player->itemroulette = 0; player->roulettetype = 0; return; @@ -1574,8 +1574,8 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd) if (P_RandomChance((debtamount*FRACUNIT)/20)) { K_KartGetItemResult(player, KITEM_SUPERRING); - player->karthud[khud_itemblink] = TICRATE; - player->karthud[khud_itemblinkmode] = 1; + player->itemblink = TICRATE; + player->itemblinkmode = 1; player->itemroulette = 0; player->roulettetype = 0; if (P_IsDisplayPlayer(player)) @@ -1593,8 +1593,8 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd) && cv_selfpropelledbomb.value) { K_KartGetItemResult(player, KITEM_SPB); - player->karthud[khud_itemblink] = TICRATE; - player->karthud[khud_itemblinkmode] = 2; + player->itemblink = TICRATE; + player->itemblinkmode = 2; player->itemroulette = 0; player->roulettetype = 0; if (P_IsDisplayPlayer(player)) @@ -1618,8 +1618,8 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd) if (useodds == 69) { K_KartGetItemResult(player, KITEM_SPB); - player->karthud[khud_itemblink] = TICRATE; - player->karthud[khud_itemblinkmode] = 2; + player->itemblink = TICRATE; + player->itemblinkmode = 2; player->itemroulette = 0; player->roulettetype = 0; if (P_IsDisplayPlayer(player)) @@ -1662,8 +1662,8 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd) if (P_IsDisplayPlayer(player) && !demo.freecam) S_StartSound(NULL, ((player->roulettetype == 1) ? sfx_itrolk : (mashed ? sfx_itrolm : sfx_itrolf))); - player->karthud[khud_itemblink] = TICRATE; - player->karthud[khud_itemblinkmode] = ((player->roulettetype == 1) ? 2 : (mashed ? 1 : 0)); + player->itemblink = TICRATE; + player->itemblinkmode = ((player->roulettetype == 1) ? 2 : (mashed ? 1 : 0)); player->itemroulette = 0; // Since we're done, clear the roulette number player->roulettetype = 0; // This too @@ -5072,7 +5072,8 @@ static void K_DoHyudoroSteal(player_t *player) // Has an item && (players[i].itemtype && players[i].itemamount - && !(players[i].itemflags & IF_ITEMOUT))) + && !(players[i].itemflags & IF_ITEMOUT) + && !players[i].itemblink)) { playerswappable[numplayers] = i; numplayers++; @@ -6619,12 +6620,6 @@ void K_KartPlayerHUDUpdate(player_t *player) if (player->karthud[khud_tauntvoices]) player->karthud[khud_tauntvoices]--; - if (player->karthud[khud_itemblink] && player->karthud[khud_itemblink]-- <= 0) - { - player->karthud[khud_itemblinkmode] = 0; - player->karthud[khud_itemblink] = 0; - } - if (gametype == GT_RACE) { @@ -7246,6 +7241,12 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) if (player->tiregrease > 0) player->tiregrease--;; + if (player->itemblink && player->itemblink-- <= 0) + { + player->itemblinkmode = 0; + player->itemblink = 0; + } + K_UpdateTripwire(player); K_KartPlayerHUDUpdate(player); diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index f1ee10811..44fcd2ea4 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -310,6 +310,10 @@ static int player_get(lua_State *L) lua_pushinteger(L, plr->tripwireReboundDelay); else if (fastcmp(field,"itemroulette")) lua_pushinteger(L, plr->itemroulette); + else if (fastcmp(field,"itemblink")) + lua_pushinteger(L, plr->itemblink); + else if (fastcmp(field,"itemblinkmode")) + lua_pushinteger(L, plr->itemblinkmode); else if (fastcmp(field,"roulettetype")) lua_pushinteger(L, plr->roulettetype); else if (fastcmp(field,"itemtype")) @@ -725,6 +729,10 @@ static int player_set(lua_State *L) plr->tripwireReboundDelay = luaL_checkinteger(L, 3); else if (fastcmp(field,"itemroulette")) plr->itemroulette = luaL_checkinteger(L, 3); + else if (fastcmp(field,"itemblink")) + plr->itemblink = luaL_checkinteger(L, 3); + else if (fastcmp(field,"itemblinkmode")) + plr->itemblinkmode = luaL_checkinteger(L, 3); else if (fastcmp(field,"roulettetype")) plr->roulettetype = luaL_checkinteger(L, 3); else if (fastcmp(field,"itemtype")) @@ -1274,10 +1282,10 @@ static int kartstuff_get(lua_State *L) // v1.0.2+ vars case k_itemblink: - lua_pushinteger(L, plr->karthud[khud_itemblink]); + lua_pushinteger(L, plr->itemblink); return 1; case k_itemblinkmode: - lua_pushinteger(L, plr->karthud[khud_itemblinkmode]); + lua_pushinteger(L, plr->itemblinkmode); return 1; case k_getsparks: lua_pushinteger(L, (plr->pflags & PF_GETSPARKS)); @@ -1537,10 +1545,10 @@ static int kartstuff_set(lua_State *L) // v1.0.2+ vars case k_itemblink: - plr->karthud[khud_itemblink] = i; + plr->itemblink = i; break; case k_itemblinkmode: - plr->karthud[khud_itemblinkmode] = i; + plr->itemblinkmode = i; break; case k_getsparks: if (i > 0) diff --git a/src/p_inter.c b/src/p_inter.c index 97a852b42..e208e4d7f 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1708,8 +1708,8 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget else player->itemamount = max(1, target->movecount); } - player->karthud[khud_itemblink] = TICRATE; - player->karthud[khud_itemblinkmode] = 0; + player->itemblink = TICRATE; + player->itemblinkmode = 0; player->itemroulette = 0; player->roulettetype = 0; if (P_IsDisplayPlayer(player)) diff --git a/src/p_saveg.c b/src/p_saveg.c index 86f2b8125..09b7f7f61 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -285,6 +285,8 @@ static void P_NetArchivePlayers(savebuffer_t *save) WRITEUINT8(save->p, players[i].tripwireReboundDelay); WRITEUINT16(save->p, players[i].itemroulette); + WRITEUINT16(save->p, players[i].itemblink); + WRITEUINT16(save->p, players[i].itemblinkmode); WRITEUINT8(save->p, players[i].roulettetype); WRITESINT8(save->p, players[i].itemtype); @@ -594,6 +596,8 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) players[i].tripwireReboundDelay = READUINT8(save->p); players[i].itemroulette = READUINT16(save->p); + players[i].itemblink = READUINT16(save->p); + players[i].itemblinkmode = READUINT16(save->p); players[i].roulettetype = READUINT8(save->p); players[i].itemtype = READSINT8(save->p); From 7fe7bd6ec0bfebd3a9414945de6c3cade988f14a Mon Sep 17 00:00:00 2001 From: NepDisk Date: Sat, 22 Mar 2025 10:47:18 -0400 Subject: [PATCH 04/45] Fix client dedicrash due to my stupidity --- src/k_hud.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_hud.c b/src/k_hud.c index 9da03988b..58138044b 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -3000,7 +3000,7 @@ static void K_drawKartMinimapNametag(fixed_t objx, fixed_t objy, INT32 hudx, INT if (encoremode) amnumxpos = -amnumxpos; - skin = ((skin_t*)players->mo->skin)-skins; + skin = ((skin_t*)player->mo->skin)-skins; amxpos = amnumxpos + ((hudx + (SHORT(minimapinfo.minimap_pic->width)-SHORT(faceprefix[skin][FACE_MINIMAP]->width))/2)<height)-SHORT(faceprefix[skin][FACE_MINIMAP]->height))/2)< Date: Mon, 14 Aug 2023 01:08:33 -0700 Subject: [PATCH 05/45] Move software shearing conditions from R_SetupFreelook into G_FinalClipAimingPitch --- src/g_game.c | 29 +++++++++++++++++++++++++++++ src/g_game.h | 1 + src/r_fps.c | 21 +-------------------- 3 files changed, 31 insertions(+), 20 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 3f119241a..64842f7ff 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -68,6 +68,10 @@ #include "discord.h" #endif +#ifdef HWRENDER +#include "hardware/hw_main.h" // for cv_glshearing +#endif + gameaction_t gameaction; gamestate_t gamestate = GS_NULL; UINT8 ultimatemode = false; @@ -763,6 +767,31 @@ INT16 G_SoftwareClipAimingPitch(INT32 *aiming) return (INT16)((*aiming)>>16); } +void G_FinalClipAimingPitch(INT32 *aiming, player_t *player, boolean skybox) +{ +#ifndef HWRENDER + (void)player; + (void)skybox; +#endif + + // clip it in the case we are looking a hardware 90 degrees full aiming + // (lmps, network and use F12...) + if (rendermode == render_soft +#ifdef HWRENDER + || (rendermode == render_opengl + && (cv_glshearing.value == 1 + || (cv_glshearing.value == 2 && R_IsViewpointThirdPerson(player, skybox)))) +#endif + ) + { + G_SoftwareClipAimingPitch(aiming); + } + else + { + G_ClipAimingPitch(aiming); + } +} + // returns true if event's axis is within the deadzone for the given player boolean G_AxisInDeadzone(UINT8 p, event_t *ev) { diff --git a/src/g_game.h b/src/g_game.h index 951a9741f..005cdb70b 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -113,6 +113,7 @@ ticcmd_t *G_MoveTiccmd(ticcmd_t* dest, const ticcmd_t* src, const size_t n); // clip the console player aiming to the view INT32 G_ClipAimingPitch(INT32 *aiming); INT16 G_SoftwareClipAimingPitch(INT32 *aiming); +void G_FinalClipAimingPitch(INT32 *aiming, player_t *player, boolean skybox); extern angle_t localangle[MAXSPLITSCREENPLAYERS]; extern INT32 localaiming[MAXSPLITSCREENPLAYERS]; // should be an angle_t but signed diff --git a/src/r_fps.c b/src/r_fps.c index a41fe2909..11f1b8378 100644 --- a/src/r_fps.c +++ b/src/r_fps.c @@ -23,9 +23,6 @@ #include "r_state.h" #include "z_zone.h" #include "console.h" // con_startup_loadprogress -#ifdef HWRENDER -#include "hardware/hw_main.h" // for cv_glshearing -#endif static CV_PossibleValue_t fpscap_cons_t[] = { #ifdef DEVELOP @@ -118,23 +115,7 @@ static vector3_t *R_LerpVector3(const vector3_t *from, const vector3_t *to, fixe // 18/08/18: (No it's actually 16*viewheight, thanks Jimita for finding this out) static void R_SetupFreelook(player_t *player, boolean skybox) { -#ifndef HWRENDER - (void)player; - (void)skybox; -#endif - - // clip it in the case we are looking a hardware 90 degrees full aiming - // (lmps, network and use F12...) - if (rendermode == render_soft -#ifdef HWRENDER - || (rendermode == render_opengl - && (cv_glshearing.value == 1 - || (cv_glshearing.value == 2 && R_IsViewpointThirdPerson(player, skybox)))) -#endif - ) - { - G_SoftwareClipAimingPitch((INT32 *)&aimingangle); - } + G_FinalClipAimingPitch((INT32 *)&aimingangle, player, skybox); centeryfrac = (viewheight/2)< Date: Sat, 22 Mar 2025 11:28:47 -0400 Subject: [PATCH 06/45] Remove advancedemo https://git.do.srb2.org/KartKrew/RingRacers/-/commit/0230b57aa8971c8e5448d3065348ac5cd24267b5 --- src/d_clisrv.c | 8 -------- src/d_main.cpp | 11 ----------- src/d_main.h | 5 ----- src/g_demo.c | 11 ++++++++--- 4 files changed, 8 insertions(+), 27 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 9b3eedf7c..e9a0abf9d 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -5601,14 +5601,6 @@ boolean TryRunTics(tic_t realtics) if (ticking) { - if (advancedemo) - { - if (timedemo_quit) - COM_ImmedExecute("quit"); - else - D_StartTitle(); - } - else { boolean tickInterp = true; diff --git a/src/d_main.cpp b/src/d_main.cpp index 686d18bd1..00b179585 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -130,7 +130,6 @@ INT32 postimgparam[MAXSPLITSCREENPLAYERS]; boolean sound_disabled = false; boolean digital_disabled = false; -boolean advancedemo; #ifdef DEBUGFILE INT32 debugload = 0; #endif @@ -972,15 +971,6 @@ void D_SRB2Loop(void) } } -// -// D_AdvanceDemo -// Called after each demo or intro demosequence finishes -// -void D_AdvanceDemo(void) -{ - advancedemo = true; -} - // ========================================================================= // D_SRB2Main // ========================================================================= @@ -1053,7 +1043,6 @@ void D_StartTitle(void) //demosequence = -1; G_SetGametype(GT_RACE); // SRB2kart paused = false; - advancedemo = false; F_InitMenuPresValues(); // clear cmd building stuff diff --git a/src/d_main.h b/src/d_main.h index 9e24c4cad..f317ab08f 100644 --- a/src/d_main.h +++ b/src/d_main.h @@ -20,9 +20,6 @@ #ifdef __cplusplus extern "C" { #endif - -extern boolean advancedemo; - // make sure not to write back the config until it's been correctly loaded extern tic_t rendergametic; @@ -39,7 +36,6 @@ void D_SRB2Loop(void) FUNCNORETURN; // D_SRB2Main() // Not a globally visible function, just included for source reference, // calls all startup code, parses command line options. -// If not overrided by user input, calls D_AdvanceDemo. // void D_SRB2Main(void); @@ -56,7 +52,6 @@ const char *D_Home(void); // // BASE LEVEL // -void D_AdvanceDemo(void); void D_StartTitle(void); #ifdef __cplusplus diff --git a/src/g_demo.c b/src/g_demo.c index e0301e14d..bd15f3d88 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -3963,7 +3963,10 @@ static void G_StopTimingDemo(void) if (restorecv_vidwait != cv_vidwait.value) CV_SetValue(&cv_vidwait, restorecv_vidwait); - D_AdvanceDemo(); + if (timedemo_quit) + COM_ImmedExecute("quit"); + else + D_StartTitle(); } // reset engine variable set for the demos @@ -4021,10 +4024,12 @@ boolean G_CheckDemoStatus(void) { G_StopDemo(); - if (modeattacking) + if (timedemo_quit) + COM_ImmedExecute("quit"); + else if (modeattacking) M_EndModeAttackRun(); else - D_AdvanceDemo(); + D_StartTitle(); } return true; From 0d6fa2178cf9db0fc976590969eb986f5587043d Mon Sep 17 00:00:00 2001 From: NepDisk Date: Sat, 22 Mar 2025 11:32:18 -0400 Subject: [PATCH 07/45] Use stringl for netreplay wadlist --- src/g_demo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/g_demo.c b/src/g_demo.c index bd15f3d88..df21c0ce0 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -2147,7 +2147,7 @@ void G_BeginRecording(void) if (wadfiles[i]->important) { nameonly(( filename = va("%s", wadfiles[i]->filename) )); - WRITESTRINGN(demobuf.p, filename, MAX_WADPATH); + WRITESTRINGL(demobuf.p, filename, MAX_WADPATH); WRITEMEM(demobuf.p, wadfiles[i]->md5sum, 16); WRITEUINT8(demobuf.p, wadfiles[i]->compatmode); From 259bc5dd97bb294ae9fdc2758fb445869f26db31 Mon Sep 17 00:00:00 2001 From: NepDisk Date: Sat, 22 Mar 2025 13:19:59 -0400 Subject: [PATCH 08/45] Fix small p_tick.c mistakes --- src/p_tick.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/p_tick.c b/src/p_tick.c index 6da044264..9ea3ea380 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -905,15 +905,17 @@ void P_Ticker(boolean run) } } - K_UpdateDirector(); - - // Always move the camera. - P_RunChaseCameras(); - - LUA_HOOK(PostThinkFrame); + if (gamestate == GS_LEVEL) + { + // Move the camera during levels. + K_UpdateDirector(); + P_RunChaseCameras(); + } if (run) { + LUA_HOOK(PostThinkFrame); + R_UpdateLevelInterpolators(); // Hack: ensure newview is assigned every tic. From bbd22ad1afc7dd5f7e000fa553cc0f173b37f3f8 Mon Sep 17 00:00:00 2001 From: NepDisk Date: Sat, 22 Mar 2025 14:46:07 -0400 Subject: [PATCH 09/45] Hack to fix R_IsViewpointThirdPerson crashing due to invalid player ref Basically to explain due to R_InterpolateView being called here it can possibly pass an invalid player ref to R_IsViewpointThirdPerson since the player mobj hasn't been created yet. Thanks to GenericHeroGuy for helping me figure this out. --- src/st_stuff.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/st_stuff.c b/src/st_stuff.c index d108a1c9c..c9e9777b8 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -918,7 +918,10 @@ void ST_Drawer(void) stplyr = &players[displayplayers[i]]; stplyrnum = i; R_SetViewContext(VIEWCONTEXT_PLAYER1 + i); - R_InterpolateView(rendertimefrac); // to assist with object tracking + + // HACK: This can possibly crash the game in R_IsViewpointThirdPerson during first two tics if the player object doesn't exist. + if (stplyr->mo) + R_InterpolateView(rendertimefrac); // to assist with object tracking ST_overlayDrawer(); } From 7270ae178d1483f5b6e2e620f5a60a078b942b56 Mon Sep 17 00:00:00 2001 From: NepDisk Date: Sat, 22 Mar 2025 16:57:35 -0400 Subject: [PATCH 10/45] Support for MF2_SHADOW --- src/deh_tables.c | 2 +- src/lua_mobjlib.c | 7 +++++++ src/p_mobj.h | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/deh_tables.c b/src/deh_tables.c index c0f42c9e7..e82ded972 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -165,7 +165,7 @@ const char *const MOBJFLAG_LIST[] = { // \tMF2_(\S+).*// (.+) --> \t"\1", // \2 const char *const MOBJFLAG2_LIST[] = { "AXIS", // It's a NiGHTS axis! (For faster checking) - "\x01", // free: 1<<1 (name un-matchable) + "SHADOW", // alias for RF_GHOSTLY "DONTRESPAWN", // Don't respawn this object! "DONTDRAW", // alias for RF_DONTDRAW "AUTOMATIC", // Thrown ring has automatic properties diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c index 6d826e3bb..2b0a0fc46 100644 --- a/src/lua_mobjlib.c +++ b/src/lua_mobjlib.c @@ -379,6 +379,8 @@ static int mobj_get(lua_State *L) UINT32 flags2 = mo->flags2; if (lua_compatmode && (mo->renderflags & RF_DONTDRAW) == RF_DONTDRAW) flags2 |= MF2_DONTDRAW; + if (lua_compatmode && (mo->renderflags & RF_GHOSTLY) == RF_GHOSTLY) + flags2 |= MF2_SHADOW; lua_pushinteger(L, flags2); break; } @@ -818,6 +820,11 @@ static int mobj_set(lua_State *L) mo->renderflags |= RF_DONTDRAW; else mo->renderflags &= ~RF_DONTDRAW; + + if (flags2 & MF2_SHADOW) + mo->renderflags |= RF_GHOSTLY; + else + mo->renderflags &= ~RF_GHOSTLYMASK; } mo->flags2 = flags2; break; diff --git a/src/p_mobj.h b/src/p_mobj.h index 7244ce061..109ea99bc 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -173,7 +173,7 @@ typedef enum typedef enum { MF2_AXIS = 1, // It's a NiGHTS axis! (For faster checking) - // free: 1<<1 + MF2_SHADOW = 1<<3, // DO NOT USE: for lua compatibility only MF2_DONTRESPAWN = 1<<2, // Don't respawn this object! MF2_DONTDRAW = 1<<3, // DO NOT USE: for lua compatibility only MF2_AUTOMATIC = 1<<4, // Thrown ring has automatic properties From 247b5662755fe361853a7dd39c27baf4c2cae4ea Mon Sep 17 00:00:00 2001 From: NepDisk Date: Sat, 22 Mar 2025 17:08:20 -0400 Subject: [PATCH 11/45] Toggle for singleplayer itembreaker --- src/d_netcmd.c | 20 ++++++++++++++++++++ src/d_netcmd.h | 1 + src/k_battle.c | 3 +++ src/k_kart.c | 1 + src/m_menu.c | 7 ++++--- 5 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index d08112fe6..798b7bcd7 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -150,6 +150,7 @@ static void KartEncore_OnChange(void); static void KartComeback_OnChange(void); static void KartEliminateLast_OnChange(void); static void KartRings_OnChange(void); +static void KartItemBreaker_OnChange(void); static void Schedule_OnChange(void); @@ -435,6 +436,8 @@ consvar_t cv_karteliminatelast = CVAR_INIT ("karteliminatelast", "Yes", CV_NETVA // Toggles for new features consvar_t cv_kartrings = CVAR_INIT ("kartrings", "No", CV_NETVAR|CV_CHEAT|CV_CALL|CV_NOINIT, CV_YesNo, KartRings_OnChange); +consvar_t cv_kartitembreaker = CVAR_INIT ("kartitembreaker", "No", CV_NETVAR|CV_CHEAT|CV_CALL|CV_NOINIT, CV_YesNo, KartItemBreaker_OnChange); + consvar_t cv_kartwalltransfer = CVAR_INIT ("BG_forcewalltransfer", "Off", CV_NETVAR|CV_CHEAT, CV_OnOff, NULL); consvar_t cv_kartusepwrlv = CVAR_INIT ("kartusepwrlv", "Yes", CV_NETVAR|CV_CHEAT, CV_YesNo, NULL); @@ -6903,6 +6906,23 @@ static void KartRings_OnChange(void) } } +static void KartItemBreaker_OnChange(void) +{ + if (K_CanChangeRules() == false) + { + return; + } + + if (cv_kartitembreaker.value) + { + CONS_Printf(M_GetText("Singleplayer Item Breaker will be turned \"On\" Next Round.\n")); + } + else + { + CONS_Printf(M_GetText("Singleplayer Item Breaker will be turned \"Off\" Next Round.\n")); + } +} + static void Schedule_OnChange(void) { size_t i; diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 285d4b19c..b9ca59fd7 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -96,6 +96,7 @@ extern consvar_t cv_kartbot; extern consvar_t cv_karteliminatelast; extern consvar_t cv_kartusepwrlv; extern consvar_t cv_kartrings; +extern consvar_t cv_kartitembreaker; extern consvar_t cv_kartwalltransfer; extern consvar_t cv_kartpurpledrift; extern consvar_t cv_kartbumpspark; diff --git a/src/k_battle.c b/src/k_battle.c index 482bf3b74..e164b08ba 100644 --- a/src/k_battle.c +++ b/src/k_battle.c @@ -491,6 +491,9 @@ void K_BattleInit(void) { UINT8 n = 0; + if (!cv_kartitembreaker.value) + goto afteritembreaker; + for (i = 0; i < MAXPLAYERS; i++) { if (!playeringame[i] || players[i].spectator) diff --git a/src/k_kart.c b/src/k_kart.c index 2275721c5..c63032e77 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -251,6 +251,7 @@ void K_RegisterKartStuff(void) CV_RegisterVar(&cv_lessflicker); CV_RegisterVar(&cv_kartrings); + CV_RegisterVar(&cv_kartitembreaker); CV_RegisterVar(&cv_newspeedometer); CV_RegisterVar(&cv_showinput); diff --git a/src/m_menu.c b/src/m_menu.c index b8a81a92d..db17339ca 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -1517,9 +1517,10 @@ static menuitem_t OP_GameOptionsMenu[] = static menuitem_t OP_BlanKartGameOptionsMenu[] = { {IT_STRING | IT_CVAR, NULL, "Rings", {.cvar = &cv_kartrings}, 10}, - {IT_STRING | IT_CVAR, NULL, "Purple Drift", {.cvar = &cv_kartpurpledrift}, 20}, - {IT_STRING | IT_CVAR, NULL, "Bump Spark", {.cvar = &cv_kartbumpspark}, 30}, - {IT_STRING | IT_CVAR, NULL, "Bump Spring", {.cvar = &cv_kartbumpspring}, 40}, + {IT_STRING | IT_CVAR, NULL, "Item Breaker", {.cvar = &cv_kartitembreaker}, 20}, + {IT_STRING | IT_CVAR, NULL, "Purple Drift", {.cvar = &cv_kartpurpledrift}, 30}, + {IT_STRING | IT_CVAR, NULL, "Bump Spark", {.cvar = &cv_kartbumpspark}, 40}, + {IT_STRING | IT_CVAR, NULL, "Bump Spring", {.cvar = &cv_kartbumpspring}, 50}, }; From f40771dd979e0925c42d53ba15a47a4a8d1f962d Mon Sep 17 00:00:00 2001 From: NepDisk Date: Sun, 23 Mar 2025 14:12:31 -0400 Subject: [PATCH 12/45] bot and respawn improvements Bot respawn incrementer is based on RR's general antigrief incrementer with edits and additions --- src/d_player.h | 1 + src/d_ticcmd.h | 1 + src/g_demo.c | 10 +++++++ src/g_game.c | 2 ++ src/k_bot.cpp | 65 ++++++++++++++++++++++++++++++++++++++++++++ src/k_bot.h | 2 +- src/k_kart.c | 74 +++++++++++++++++++++++++++++++++++++++++--------- src/k_kart.h | 1 + src/p_saveg.c | 2 ++ src/p_user.c | 14 ++-------- 10 files changed, 146 insertions(+), 26 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index 8e5f57f5f..9b7f6b4d1 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -425,6 +425,7 @@ struct botvars_t tic_t itemconfirm; // When high enough, they will use their item SINT8 turnconfirm; // Confirm turn direction + UINT32 respawnconfirm; // Confirm when respawn is needed. }; diff --git a/src/d_ticcmd.h b/src/d_ticcmd.h index a4d0263ae..0ed322b5a 100644 --- a/src/d_ticcmd.h +++ b/src/d_ticcmd.h @@ -78,6 +78,7 @@ struct ticcmd_t { SINT8 turnconfirm; SINT8 itemconfirm; + UINT8 respawnconfirm; } bot; } ATTRPACK; diff --git a/src/g_demo.c b/src/g_demo.c index df21c0ce0..3cfada007 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -147,6 +147,7 @@ demoghost *ghosts = NULL; #define ZT_BOT_TURN 0x0001 #define ZT_BOT_ITEM 0x0002 +#define ZT_BOT_RESPAWN 0x0004 #define DEMOMARKER 0x80 // demobuf.end @@ -579,6 +580,8 @@ void G_ReadDemoTiccmd(ticcmd_t *cmd, INT32 playernum) oldcmd[playernum].bot.turnconfirm = READSINT8(demobuf.p); if (botziptic & ZT_BOT_ITEM) oldcmd[playernum].bot.itemconfirm = READSINT8(demobuf.p); + if (botziptic & ZT_BOT_RESPAWN) + oldcmd[playernum].bot.itemconfirm = READUINT32(demobuf.p); } G_CopyTiccmd(cmd, &oldcmd[playernum], 1); @@ -695,6 +698,13 @@ void G_WriteDemoTiccmd(ticcmd_t *cmd, INT32 playernum) botziptic |= ZT_BOT_ITEM; } + if (cmd->bot.respawnconfirm != oldcmd[playernum].bot.respawnconfirm) + { + WRITESINT8(demobuf.p, cmd->bot.respawnconfirm); + oldcmd[playernum].bot.respawnconfirm = cmd->bot.respawnconfirm; + botziptic |= ZT_BOT_RESPAWN; + } + WRITEUINT16(botziptic_p, botziptic); } diff --git a/src/g_game.c b/src/g_game.c index 64842f7ff..c4b9af24d 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1262,6 +1262,8 @@ ticcmd_t *G_MoveTiccmd(ticcmd_t* dest, const ticcmd_t* src, const size_t n) if (dest[i].flags & TICCMD_BOT) { dest[i].bot.itemconfirm = src[i].bot.itemconfirm; + dest[i].bot.turnconfirm = src[i].bot.turnconfirm; + dest[i].bot.respawnconfirm = src[i].bot.respawnconfirm; } } return dest; diff --git a/src/k_bot.cpp b/src/k_bot.cpp index 804b17e8c..5b14d08e1 100644 --- a/src/k_bot.cpp +++ b/src/k_bot.cpp @@ -1327,6 +1327,25 @@ static void K_BuildBotTiccmdNormal(player_t *player, ticcmd_t *cmd) return; } + if (player->botvars.respawnconfirm >= BOTRESPAWNCONFIRM) + { + // We want to respawn. Simply hold brake and stop here! + cmd->buttons &= ~BT_ACCELERATE|BT_DRIFT|BT_ATTACK; + if (player->speed > 0) + { + cmd->buttons |= (BT_BRAKE); + } + + if ((player->speed < 10*FRACUNIT)) + { + cmd->bot.respawnconfirm = 1; + } + return; + } + else + { + cmd->bot.respawnconfirm = 0; + } destangle = player->mo->angle; boolean forcedDir = false; @@ -1488,6 +1507,38 @@ void K_BuildBotTiccmd( } } +static void K_IncrementBotRespawn(player_t *player, UINT32 *respawn, const UINT32 respawnmax) +{ + const fixed_t requireDist = (12*player->mo->scale) / FRACUNIT; + INT32 progress = player->distancetofinishprev - player->distancetofinish; + boolean exceptions = ( + (leveltime < starttime) + || player->flashing != 0 + || player->spinouttimer != 0 + || player->airtime > 3*TICRATE/2 + || (player->justbumped > 0 && player->justbumped < bumptime-1) + ); + + if (!exceptions && (progress < requireDist)) + { + if (*respawn < respawnmax) + { + // Making no progress, start counting against you. + *respawn = *respawn + 1; + if (progress < -requireDist && *respawn < respawnmax) + { + // Making NEGATIVE progress? Start counting even harder. + *respawn = *respawn + 1; + } + } + } + else if (*respawn > 0) + { + // Playing normally. + *respawn = *respawn - 1; + } +} + /*-------------------------------------------------- void K_UpdateBotGameplayVars(player_t *player); @@ -1505,5 +1556,19 @@ void K_UpdateBotGameplayVars(player_t *player) player->botvars.turnconfirm += player->cmd.bot.turnconfirm; + // Is a bot not making any progress? Kill it and respawn at next waypoint. + K_IncrementBotRespawn(player, &player->botvars.respawnconfirm, BOTRESPAWNCONFIRM); + + if ((player->cmd.bot.respawnconfirm > 0) && (player->botvars.respawnconfirm >= BOTRESPAWNCONFIRM)) + { + // Now a clean function! Neat, eh? + K_SetRespawnAtNextWaypoint(player); + + // WHAT ARE YOU DOING??? RACE ALREADY! + P_DamageMobj(player->mo, NULL, NULL, 1, DMG_INSTAKILL); + + player->botvars.respawnconfirm = 0; + } + K_UpdateBotGameplayVarsItemUsage(player); } diff --git a/src/k_bot.h b/src/k_bot.h index 267b13450..7a8691bba 100644 --- a/src/k_bot.h +++ b/src/k_bot.h @@ -40,7 +40,7 @@ extern "C" { #define BOTSPINDASHCONFIRM (4*TICRATE) // How many tics without being able to make progress before we'll let you respawn. -#define BOTRESPAWNCONFIRM (5*TICRATE) +#define BOTRESPAWNCONFIRM (4*TICRATE) // How long it takes for a Lv.1 bot to decide to pick an item. #define BOT_ITEM_DECISION_TIME (2*TICRATE) diff --git a/src/k_kart.c b/src/k_kart.c index c63032e77..9f3004642 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -8304,6 +8304,60 @@ static void K_FudgeRespawn(player_t *player, const waypoint_t *const waypoint) player->starposty += FixedMul(16, FINESINE(from)); } +void K_SetRespawnAtNextWaypoint(player_t * player) +{ + 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); + + // Safety :P + if (!safewaypoint || !currentwaypoint) + { + // Better safe then sorry. + return; + } + + player->pflags |= PF_TRUSTWAYPOINTS; + player->starposttime = player->realtime; + player->starpostz = (safewaypoint->spawnpoint->z + 15) >> 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; +} + +static boolean K_MobjIsOnLine(mobj_t *const mobj) +{ + const fixed_t x = mobj->x; + const fixed_t y = mobj->y; + + line_t *line = P_FindNearestLine(x, y, + mobj->subsector->sector, -1); + + vertex_t point; + + if (line != NULL) + { + P_ClosestPointOnLine(x, y, line, &point); + + if (x == point.x && y == point.y) + return true; + } + + return false; +} + +static void K_MobjFudgeRespawn(player_t *player, const mobj_t *const mobj) +{ + const angle_t from = R_PointToAngle2(mobj->x, mobj->y, + player->mo->x, player->mo->y) >> ANGLETOFINESHIFT; + + player->starpostx += FixedMul(25, FINECOSINE(from)); + player->starposty += FixedMul(25, FINESINE(from)); +} + /*-------------------------------------------------- static void K_UpdatePlayerWaypoints(player_t *const player) @@ -8401,22 +8455,16 @@ static void K_UpdatePlayerWaypoints(player_t *const player) // Then do x and y player->starpostx = player->mo->x >> FRACBITS; player->starposty = player->mo->y >> FRACBITS; + + if (K_MobjIsOnLine(player->mo)) + { + K_MobjFudgeRespawn(player, player->mo); + } } 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 = respawnangle; - - // Then do x and y - player->starpostx = safewaypoint->x >> FRACBITS; - player->starposty = safewaypoint->y >> FRACBITS; - + // Now a clean function! Neat, eh? + K_SetRespawnAtNextWaypoint(player); } if (player->nextwaypoint->onaline) diff --git a/src/k_kart.h b/src/k_kart.h index 134ca1bda..01294e277 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -131,6 +131,7 @@ player_t *K_FindJawzTarget(mobj_t *actor, player_t *source); INT32 K_GetKartRingPower(player_t *player, boolean boosted); size_t K_NextRespawnWaypointIndex(waypoint_t *waypoint); boolean K_CheckPlayersRespawnColliding(INT32 playernum, fixed_t x, fixed_t y); +void K_SetRespawnAtNextWaypoint(player_t * player); INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue); INT32 K_GetKartDriftSparkValue(player_t *player); INT32 K_GetKartDriftSparkValueForStage(player_t *player, UINT8 stage); diff --git a/src/p_saveg.c b/src/p_saveg.c index 09b7f7f61..582a7a68f 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -367,6 +367,7 @@ static void P_NetArchivePlayers(savebuffer_t *save) WRITEUINT32(save->p, players[i].botvars.itemdelay); WRITEUINT32(save->p, players[i].botvars.itemconfirm); WRITESINT8(save->p, players[i].botvars.turnconfirm); + WRITEUINT32(save->p, players[i].botvars.respawnconfirm); WRITEFIXED(save->p, players[i].outrun); WRITEUINT8(save->p, players[i].outruntime); @@ -678,6 +679,7 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) players[i].botvars.itemdelay = READUINT32(save->p); players[i].botvars.itemconfirm = READUINT32(save->p); players[i].botvars.turnconfirm = READSINT8(save->p); + players[i].botvars.respawnconfirm = READUINT32(save->p); players[i].outrun = READFIXED(save->p); players[i].outruntime = READUINT8(save->p); diff --git a/src/p_user.c b/src/p_user.c index c7d77af15..ad2ce9c9d 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -2539,18 +2539,8 @@ static void P_DeathThink(player_t *player) // 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; + // Now a clean function! Neat, eh? + K_SetRespawnAtNextWaypoint(player); } K_KartPlayerHUDUpdate(player); From fe2fc49ac1b5de1ac3f117ab8add053ef5bb0bed Mon Sep 17 00:00:00 2001 From: NepDisk Date: Sun, 23 Mar 2025 15:33:11 -0400 Subject: [PATCH 13/45] fix copypaste errors in new bot respawn ticccmd code --- src/d_ticcmd.h | 2 +- src/g_demo.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/d_ticcmd.h b/src/d_ticcmd.h index 0ed322b5a..48fd66b9d 100644 --- a/src/d_ticcmd.h +++ b/src/d_ticcmd.h @@ -78,7 +78,7 @@ struct ticcmd_t { SINT8 turnconfirm; SINT8 itemconfirm; - UINT8 respawnconfirm; + SINT8 respawnconfirm; } bot; } ATTRPACK; diff --git a/src/g_demo.c b/src/g_demo.c index 3cfada007..3646bde41 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -581,7 +581,7 @@ void G_ReadDemoTiccmd(ticcmd_t *cmd, INT32 playernum) if (botziptic & ZT_BOT_ITEM) oldcmd[playernum].bot.itemconfirm = READSINT8(demobuf.p); if (botziptic & ZT_BOT_RESPAWN) - oldcmd[playernum].bot.itemconfirm = READUINT32(demobuf.p); + oldcmd[playernum].bot.respawnconfirm = READSINT8(demobuf.p); } G_CopyTiccmd(cmd, &oldcmd[playernum], 1); From 071fa97191a323939c852ff3a5e1244920d2c8bc Mon Sep 17 00:00:00 2001 From: NepDisk Date: Sun, 23 Mar 2025 18:59:03 -0400 Subject: [PATCH 14/45] Repair ring award special --- src/p_spec.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/p_spec.c b/src/p_spec.c index 5d2c1a782..f4bce7b19 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -4061,6 +4061,7 @@ boolean P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, cha case 460: // Award rings { + INT16 rings = args[0]; INT32 delay = args[1]; if ( @@ -4069,13 +4070,9 @@ boolean P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, cha && (delay <= 0 || !(leveltime % delay)) // Timing ) { - // Don't award rings your rings are locked - if (mo->player->pflags & PF_RINGLOCK) - return false; - - if (delay <= 0 || !(leveltime % delay)) + if (rings > 0) { - // Don't award rings while your rings are locked + // Don't award rings while your rings are locked. if (mo->player->pflags & PF_RINGLOCK) return false; @@ -4084,11 +4081,19 @@ boolean P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, cha } else { + // args[2]: cap rings to -20 instead of 0 + SINT8 baseline = (args[2] ? -20 : 0); + // Don't push you below baseline - if (mo->player->rings < 0) + if (mo->player->rings <= baseline) return false; - mo->player->rings--; + rings = -(rings); + + if (rings > (mo->player->rings - baseline)) + rings = (mo->player->rings - baseline); + + mo->player->rings -= rings; S_StartSound(mo, sfx_antiri); } } From 7b54760030bedb3942dd3806d0d1a834f6f86471 Mon Sep 17 00:00:00 2001 From: NepDisk Date: Sun, 23 Mar 2025 20:01:55 -0400 Subject: [PATCH 15/45] Changes for Spee ring meter --- src/k_hud.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/k_hud.c b/src/k_hud.c index 58138044b..66667fc02 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -2167,14 +2167,15 @@ static void K_drawRingMeter(void) if (stplyr->rings) { SINT8 ringcount = stplyr->rings; - SINT8 barcolors[4] = {66,83,65}; + UINT8 barcolors[5] = {66,83,114,65}; boolean indebt = false; if (stplyr->rings < 0) { barcolors[0] = 35; barcolors[1] = 33; - barcolors[2] = 37; + barcolors[2] = 32; + barcolors[3] = 37; ringcount = -ringcount; indebt = true; } @@ -2185,7 +2186,8 @@ static void K_drawRingMeter(void) V_DrawFill(LAPS_X+17+(2*i), LAPS_Y-10 + ringoffsety, 1, 3, barcolors[0]|splitflags); V_DrawFill(LAPS_X+17+(2*i), LAPS_Y-9 + ringoffsety, 1, 2, barcolors[1]|splitflags); - V_DrawFill(LAPS_X+17+(2*i), LAPS_Y-6 + ringoffsety, 1, 1, barcolors[2]|splitflags); + V_DrawFill(LAPS_X+17+(2*i), LAPS_Y-7 + ringoffsety, 1, 1, barcolors[2]|splitflags); + V_DrawFill(LAPS_X+17+(2*i), LAPS_Y-6 + ringoffsety, 1, 1, barcolors[3]|splitflags); } } } From f845a3aca509338757c2a578bdd31ae6d8a654a6 Mon Sep 17 00:00:00 2001 From: NepDisk Date: Sun, 23 Mar 2025 21:09:54 -0400 Subject: [PATCH 16/45] Adjust bar colors for spee ringbar and fix 11 offset to look better --- src/k_hud.c | 47 ++++++++++++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/src/k_hud.c b/src/k_hud.c index 66667fc02..c6c4641aa 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -2093,17 +2093,18 @@ static void K_drawRingMeter(void) UINT8 *ringmap = NULL; boolean colorring = false; INT32 splitflags = V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_HUDTRANS|V_SPLITSCREEN; + SINT8 ringcount = stplyr->rings; - rn[0] = ((abs(stplyr->rings) / 10) % 10); - rn[1] = (abs(stplyr->rings) % 10); + rn[0] = ((abs(ringcount) / 10) % 10); + rn[1] = (abs(ringcount) % 10); - if (stplyr->rings <= 0 && (leveltime/5 & 1)) // In debt + if (ringcount <= 0 && (leveltime/5 & 1)) // In debt { ringmap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_CRIMSON, GTC_CACHE); colorring = true; } - else if (stplyr->rings >= 20) // Maxed out + else if (ringcount >= 20) // Maxed out ringmap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_YELLOW, GTC_CACHE); if (r_splitscreen > 1) @@ -2136,7 +2137,7 @@ static void K_drawRingMeter(void) V_DrawMappedPatch(fr, fy-10, V_HUDTRANS|splitflags, kp_ringsplitscreen, (colorring ? ringmap : NULL)); - if (stplyr->rings < 0) // Draw the minus for ring debt + if (ringcount < 0) // Draw the minus for ring debt V_DrawMappedPatch(fr+7, fy-8, V_HUDTRANS|splitflags, kp_ringdebtminussmall, ringmap); V_DrawMappedPatch(fr+11, fy-10, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[rn[0]], ringmap); @@ -2160,34 +2161,46 @@ static void K_drawRingMeter(void) V_DrawMappedPatch(LAPS_X-5, LAPS_Y-11 + ringoffsety, V_HUDTRANS|splitflags, kp_ringdebtminus, ringmap); } - V_DrawMappedPatch(LAPS_X+2, LAPS_Y-11 + ringoffsety, V_HUDTRANS|splitflags, kp_facenum[rn[0]], ringmap); - V_DrawMappedPatch(LAPS_X+8, LAPS_Y-11 + ringoffsety, V_HUDTRANS|splitflags, kp_facenum[rn[1]], ringmap); + if (stplyr->rings < 0) + { + // Invert the ring count + ringcount = -ringcount; + } + + if (rn[1] == 1 && ringcount == 11) + V_DrawMappedPatch(LAPS_X+2, LAPS_Y-11 + ringoffsety, V_HUDTRANS|splitflags, kp_facenum[rn[0]], ringmap); + else + V_DrawMappedPatch(LAPS_X+2, LAPS_Y-11 + ringoffsety, V_HUDTRANS|splitflags, kp_facenum[rn[0]], ringmap); + + if (rn[1] == 1 && ringcount == 11) + V_DrawMappedPatch(LAPS_X+7, LAPS_Y-11 + ringoffsety, V_HUDTRANS|splitflags, kp_facenum[rn[1]], ringmap); + else + V_DrawMappedPatch(LAPS_X+8, LAPS_Y-11 + ringoffsety, V_HUDTRANS|splitflags, kp_facenum[rn[1]], ringmap); // Draw the fillbars if (stplyr->rings) { - SINT8 ringcount = stplyr->rings; - UINT8 barcolors[5] = {66,83,114,65}; + UINT8 barcolors[5] = {66,72,2,68}; boolean indebt = false; if (stplyr->rings < 0) { - barcolors[0] = 35; - barcolors[1] = 33; + barcolors[0] = 38; + barcolors[1] = 36; barcolors[2] = 32; - barcolors[3] = 37; - ringcount = -ringcount; + barcolors[3] = 40; indebt = true; } + if (!indebt || (indebt && (leveltime/5 & 1))) { for (i = 0; i != ringcount; i++) { - V_DrawFill(LAPS_X+17+(2*i), LAPS_Y-10 + ringoffsety, 1, 3, barcolors[0]|splitflags); - V_DrawFill(LAPS_X+17+(2*i), LAPS_Y-9 + ringoffsety, 1, 2, barcolors[1]|splitflags); - V_DrawFill(LAPS_X+17+(2*i), LAPS_Y-7 + ringoffsety, 1, 1, barcolors[2]|splitflags); - V_DrawFill(LAPS_X+17+(2*i), LAPS_Y-6 + ringoffsety, 1, 1, barcolors[3]|splitflags); + V_DrawFill(LAPS_X+17+(2*i), LAPS_Y-10 + ringoffsety, 1, 1, barcolors[0]|splitflags); + V_DrawFill(LAPS_X+17+(2*i), LAPS_Y-9 + ringoffsety, 1, 4, barcolors[1]|splitflags); + V_DrawFill(LAPS_X+17+(2*i), LAPS_Y-8 + ringoffsety, 1, 1, barcolors[2]|splitflags); + V_DrawFill(LAPS_X+17+(2*i), LAPS_Y-7 + ringoffsety, 1, 1, barcolors[3]|splitflags); } } } From 2876e2adc6b38bd2df505aa54cd58f33d8608968 Mon Sep 17 00:00:00 2001 From: NepDisk Date: Sun, 23 Mar 2025 22:30:36 -0400 Subject: [PATCH 17/45] Properly fix shearing crash without the ugly hack --- src/r_main.cpp | 8 +++++++- src/st_stuff.c | 5 +---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/r_main.cpp b/src/r_main.cpp index f80181fc4..8adb6ab5e 100644 --- a/src/r_main.cpp +++ b/src/r_main.cpp @@ -1438,7 +1438,13 @@ boolean R_ViewpointHasChasecam(player_t *player) boolean R_IsViewpointThirdPerson(player_t *player, boolean skybox) { - boolean chasecam = R_ViewpointHasChasecam(player); + boolean chasecam = false; + + // Prevent game crash if player is ever invalid. + if (!player) + return false; + + chasecam = R_ViewpointHasChasecam(player); // cut-away view stuff if (player->awayviewtics || skybox) diff --git a/src/st_stuff.c b/src/st_stuff.c index c9e9777b8..d108a1c9c 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -918,10 +918,7 @@ void ST_Drawer(void) stplyr = &players[displayplayers[i]]; stplyrnum = i; R_SetViewContext(VIEWCONTEXT_PLAYER1 + i); - - // HACK: This can possibly crash the game in R_IsViewpointThirdPerson during first two tics if the player object doesn't exist. - if (stplyr->mo) - R_InterpolateView(rendertimefrac); // to assist with object tracking + R_InterpolateView(rendertimefrac); // to assist with object tracking ST_overlayDrawer(); } From cf09ac0023e65d984a0601a82528aef68086af71 Mon Sep 17 00:00:00 2001 From: NepDisk Date: Mon, 24 Mar 2025 09:26:50 -0400 Subject: [PATCH 18/45] Check for removed mobj for loop --- src/p_mobj.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index 32406521c..799bcf73c 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -12317,7 +12317,9 @@ static void P_SpawnItemRow(mapthing_t *mthing, mobjtype_t *itemtypes, UINT8 numi loopanchor->spawnpoint = NULL; - Obj_LinkLoopAnchor(loopanchor, loopcenter, mthing->args[0]); + if (!P_MobjWasRemoved(loopanchor)) + Obj_LinkLoopAnchor(loopanchor, loopcenter, mthing->args[0]); + } for (r = 0; r < numitems; r++) From af09ba91ec701b6d25d24b5c9d9011402ab9de3c Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 25 Mar 2023 20:05:38 -0700 Subject: [PATCH 19/45] Completely rewrite party management code Replaces g_splitscreen.c with g_party.cpp. Simplifies party management functions. Moves externs out of already bloated doomstat.h and g_game.h into g_party.h. Cuts down on globals spam. --- src/Sourcefile | 2 +- src/d_clisrv.c | 14 +-- src/d_netcmd.c | 52 ++++------- src/doomstat.h | 12 --- src/g_game.c | 5 +- src/g_game.h | 4 - src/g_party.cpp | 53 ++++++----- src/g_party.h | 84 ++++++++++++++++++ src/g_splitscreen.c | 210 -------------------------------------------- src/k_hud.c | 1 + src/p_saveg.c | 75 ++++++++++++---- src/p_user.c | 23 ++--- 12 files changed, 209 insertions(+), 326 deletions(-) create mode 100644 src/g_party.h delete mode 100644 src/g_splitscreen.c diff --git a/src/Sourcefile b/src/Sourcefile index ceb12918b..c3d4bca7e 100644 --- a/src/Sourcefile +++ b/src/Sourcefile @@ -15,7 +15,7 @@ f_wipe.c g_demo.c g_game.c g_input.c -g_splitscreen.c +g_party.cpp am_map.c command.c console.c diff --git a/src/d_clisrv.c b/src/d_clisrv.c index e9a0abf9d..b541c8c59 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -58,6 +58,8 @@ #include "doomstat.h" #include "s_sound.h" // sfx_syfail #include "r_fps.h" +#include "m_cond.h" // netUnlocked +#include "g_party.h" // cl loading screen #include "v_video.h" @@ -2525,6 +2527,7 @@ void CL_ClearPlayer(INT32 playernum) splitscreen_invitations[playernum] = -1; playerconsole[playernum] = playernum; + G_DestroyParty(playernum); // Wipe the struct. memset(&players[playernum], 0, sizeof (player_t)); @@ -2575,7 +2578,7 @@ void CL_RemovePlayer(INT32 playernum, kickreason_t reason) displayplayers[0] = consoleplayer; } - G_RemovePartyMember(playernum); + G_LeaveParty(playernum); // Reset player data CL_ClearPlayer(playernum); @@ -3436,9 +3439,9 @@ void SV_ResetServer(void) Schedule_Clear(); Automate_Clear(); K_ClearClientPowerLevels(); + G_ObliterateParties(); memset(splitscreen_invitations, -1, sizeof splitscreen_invitations); - memset(splitscreen_partied, 0, sizeof splitscreen_partied); memset(player_name_changes, 0, sizeof player_name_changes); mynode = 0; @@ -3525,6 +3528,7 @@ void D_QuitNetGame(void) Schedule_Clear(); Automate_Clear(); K_ClearClientPowerLevels(); + G_ObliterateParties(); DEBFILE("===========================================================================\n" " Log finish\n" @@ -3603,7 +3607,6 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum) displayplayers[i] = newplayernum; g_localplayers[i] = newplayernum; } - splitscreen_partied[newplayernum] = true; DEBFILE("spawning me\n"); } @@ -3617,10 +3620,7 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum) players[newplayernum].bot = false; playerconsole[newplayernum] = console; - splitscreen_original_party_size[console] = - ++splitscreen_party_size[console]; - splitscreen_original_party[console][splitscreenplayer] = - splitscreen_party[console][splitscreenplayer] = newplayernum; + G_BuildLocalSplitscreenParty(newplayernum); if (netgame) { diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 798b7bcd7..3b914bfc3 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -64,6 +64,7 @@ #include "doomstat.h" #include "deh_tables.h" #include "m_perfstats.h" +#include "g_party.h" #define CV_RESTRICT CV_NETVAR @@ -1492,14 +1493,12 @@ static void ForceAllSkins(INT32 forcedskin) } static const char * -VaguePartyDescription (int playernum, int *party_sizes, int default_color) +VaguePartyDescription (int playernum, int size, int default_color) { static char party_description [1 + MAXPLAYERNAME + 1 + sizeof " and x others"]; const char *name; - int size; name = player_names[playernum]; - size = party_sizes[playernum]; /* less than check for the dumb compiler because I KNOW it'll complain about "writing x bytes into an area of y bytes"!!! @@ -1914,7 +1913,7 @@ static void Got_PartyInvite(UINT8 **cp,INT32 playernum) HU_AddChatText(va( "\x82*You have been invited to join %s.", VaguePartyDescription( - playernum, splitscreen_party_size, '\x82') + playernum, G_PartySize(playernum), '\x82') ), true); } } @@ -1923,8 +1922,6 @@ static void Got_PartyInvite(UINT8 **cp,INT32 playernum) static void Got_AcceptPartyInvite(UINT8 **cp,INT32 playernum) { int invitation; - int old_party_size; - int views; (void)cp; @@ -1940,12 +1937,12 @@ static void Got_AcceptPartyInvite(UINT8 **cp,INT32 playernum) if (invitation >= 0) { - if (splitscreen_partied[invitation]) + if (G_IsPartyLocal(invitation)) { HU_AddChatText(va( "\x82*%s joined your party!", VaguePartyDescription( - playernum, splitscreen_original_party_size, '\x82') + playernum, G_LocalSplitscreenPartySize(playernum), '\x82') ), true); } else if (playernum == consoleplayer) @@ -1953,18 +1950,11 @@ static void Got_AcceptPartyInvite(UINT8 **cp,INT32 playernum) HU_AddChatText(va( "\x82*You joined %s's party!", VaguePartyDescription( - invitation, splitscreen_party_size, '\x82') + invitation, G_PartySize(invitation), '\x82') ), true); } - old_party_size = splitscreen_party_size[invitation]; - views = splitscreen_original_party_size[playernum]; - - if (( old_party_size + views ) <= MAXSPLITSCREENPLAYERS) - { - G_RemovePartyMember(playernum); - G_AddPartyMember(invitation, playernum); - } + G_JoinParty(invitation, playernum); splitscreen_invitations[playernum] = -1; } @@ -2022,21 +2012,16 @@ static void Got_LeaveParty(UINT8 **cp,INT32 playernum) splitscreen_invitations[playernum] = -1; - if (splitscreen_party_size[playernum] > - splitscreen_original_party_size[playernum]) + if (G_IsPartyLocal(playernum) && playernum != consoleplayer) { - if (splitscreen_partied[playernum] && playernum != consoleplayer) - { - HU_AddChatText(va( - "\x85*%s left your party.", - VaguePartyDescription( - playernum, splitscreen_original_party_size, '\x85') - ), true); - } - - G_RemovePartyMember(playernum); - G_ResetSplitscreen(playernum); + HU_AddChatText(va( + "\x85*%s left your party.", + VaguePartyDescription( + playernum, G_LocalSplitscreenPartySize(playernum), '\x85') + ), true); } + + G_LeaveParty(playernum); } void D_SendPlayerConfig(UINT8 n) @@ -2354,8 +2339,7 @@ Command_Invite_f (void) "That player has already been invited to join another party.\n"); } - if (( splitscreen_party_size[consoleplayer] + - splitscreen_original_party_size[invitee] ) > MAXSPLITSCREENPLAYERS) + if ((G_PartySize(consoleplayer) + G_LocalSplitscreenPartySize(invitee)) > MAXSPLITSCREENPLAYERS) { CONS_Alert(CONS_WARNING, "That player joined with too many " @@ -2365,7 +2349,7 @@ Command_Invite_f (void) CONS_Printf( "Inviting %s...\n", VaguePartyDescription( - invitee, splitscreen_original_party_size, '\x80') + invitee, G_LocalSplitscreenPartySize(invitee), '\x80') ); buffer[0] = invitee; @@ -2408,7 +2392,7 @@ Command_CancelInvite_f (void) CONS_Printf( "Rescinding invite to %s...\n", VaguePartyDescription( - invitee, splitscreen_original_party_size, '\x80') + invitee, G_LocalSplitscreenPartySize(invitee), '\x80') ); buffer[0] = invitee; diff --git a/src/doomstat.h b/src/doomstat.h index 77c954a25..ff3ef68bb 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -195,18 +195,6 @@ extern INT32 displayplayers[MAXSPLITSCREENPLAYERS]; /* g_localplayers[0] = consoleplayer */ extern INT32 g_localplayers[MAXSPLITSCREENPLAYERS]; -/* spitscreen players sync */ -extern INT32 splitscreen_original_party_size[MAXPLAYERS]; -extern INT32 splitscreen_original_party[MAXPLAYERS][MAXSPLITSCREENPLAYERS]; - -/* parties */ -extern INT32 splitscreen_invitations[MAXPLAYERS]; -extern INT32 splitscreen_party_size[MAXPLAYERS]; -extern INT32 splitscreen_party[MAXPLAYERS][MAXSPLITSCREENPLAYERS]; - -/* the only local one */ -extern boolean splitscreen_partied[MAXPLAYERS]; - extern char * titlemap; extern boolean hidetitlepics; extern char * bootmap; //bootmap for loading a map on startup diff --git a/src/g_game.c b/src/g_game.c index c4b9af24d..19117bbb2 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -63,6 +63,7 @@ #include "doomstat.h" #include "acs/interface.h" #include "k_director.h" +#include "g_party.h" #ifdef HAVE_DISCORDRPC #include "discord.h" @@ -2936,12 +2937,12 @@ void G_DoReborn(INT32 playernum) void G_AddPlayer(INT32 playernum, INT32 console) { CL_ClearPlayer(playernum); - //G_DestroyParty(playernum); + G_DestroyParty(playernum); playeringame[playernum] = true; playerconsole[playernum] = console; - //G_BuildLocalSplitscreenParty(playernum); + G_BuildLocalSplitscreenParty(playernum); player_t *newplayer = &players[playernum]; diff --git a/src/g_game.h b/src/g_game.h index 005cdb70b..8bcab9b66 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -224,10 +224,6 @@ void G_ResetViews(void); void G_ResetView(UINT8 viewnum, INT32 playernum, boolean onlyactive); void G_AdjustView(UINT8 viewnum, INT32 offset, boolean onlyactive); -void G_AddPartyMember (INT32 party_member, INT32 new_party_member); -void G_RemovePartyMember (INT32 party_member); -void G_ResetSplitscreen (INT32 playernum); - void G_AddPlayer(INT32 playernum, INT32 console); void G_SetExitGameFlag(void); diff --git a/src/g_party.cpp b/src/g_party.cpp index 1c82a2d1d..a5dd79270 100644 --- a/src/g_party.cpp +++ b/src/g_party.cpp @@ -1,6 +1,7 @@ // DR. ROBOTNIK'S RING RACERS //----------------------------------------------------------------------------- -// Copyright (C) 2023 by James Robert Roman +// Copyright (C) 2025 by James Robert Roman +// Copyright (C) 2025 by Kart Krew // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -19,7 +20,7 @@ #include "d_clisrv.h" // playerconsole #include "doomdef.h" // MAXPLAYERS #include "doomstat.h" // consoleplayer -#include "g_game.h" // localangle +#include "g_game.h" // G_FixCamera #include "g_party.h" #include "g_state.h" #include "p_local.h" @@ -101,7 +102,7 @@ public: bool local() const { // consoleplayer is not valid yet. - if (!addedtogame) + if (!addedtogame && !demo.playback) { return false; } @@ -128,28 +129,15 @@ public: return; } - // Rendering stuff is not valid outside of levels. - if (!G_GamestateUsesLevel()) - { - return; - } - for (std::size_t i = 0; i < size(); ++i) { - const playernum_t player = at(i); + displayplayers[i] = at(i); - displayplayers[i] = player; - - // The order of displayplayers can change, which - // would make localangle invalid now. - localangle[i] = players[player].angleturn; - - P_ResetCamera(&players[player], &camera[i]); - - // Make sure the viewport doesn't interpolate at - // all into its new position -- just snap - // instantly into place. - R_ResetViewInterpolation(1 + i); + // Camera is not valid outside of levels. + if (G_GamestateUsesLevel()) + { + G_FixCamera(1 + i); + } } r_splitscreen = size() - 1; @@ -186,6 +174,11 @@ public: // consoleplayer. Party& operator [](Party::Console console) { return pool_[console]; } + // Clears a single player's local party. This method + // accesses the playernum directly, instead of the + // consoleplayer. + void reset(playernum_t player) { pool_[player] = {}; } + protected: std::array pool_; } @@ -271,7 +264,7 @@ void G_ObliterateParties(void) void G_DestroyParty(UINT8 player) { - local_party[player] = {}; + local_party.reset(player); final_party[player] = {}; } @@ -324,3 +317,17 @@ UINT8 G_PartyPosition(UINT8 player) return party.find(player) - party.begin(); } + +UINT8 G_LocalSplitscreenPartyPosition(UINT8 player) +{ + const Party& party = local_party[player]; + + return party.find(player) - party.begin(); +} + +UINT8 G_LocalSplitscreenPartyMember(UINT8 player, UINT8 index) +{ + SRB2_ASSERT(index < local_party[player].size()); + + return local_party[player][index]; +} diff --git a/src/g_party.h b/src/g_party.h new file mode 100644 index 000000000..b5fd5c2b0 --- /dev/null +++ b/src/g_party.h @@ -0,0 +1,84 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2025 by James Robert Roman +// Copyright (C) 2025 by Kart Krew +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- + +#ifndef __G_PARTY_H__ +#define __G_PARTY_H__ + +#include "doomdef.h" // MAXPLAYERS + +#ifdef __cplusplus +extern "C" { +#endif + +// +// Functions +// + +// Frees all party resources. +void G_ObliterateParties(void); + +// Wipes all party data for this player slot. +void G_DestroyParty(UINT8 player); + +// Adds player to their local party. +void G_BuildLocalSplitscreenParty(UINT8 player); + +// Join guest's entire local party to the host. All checks are +// performed, so this is a no-op if the parties are already +// joined, or if either party is too big for the other, etc. +// +// Resets viewports for all players involved. +void G_JoinParty(UINT8 host, UINT8 guest); + +// Removes guest from an online party and restores their +// initial local party. +void G_LeaveParty(UINT8 guest); + +// Size of the player's initial local party. +UINT8 G_LocalSplitscreenPartySize(UINT8 player); + +// Ultimate size of this player's party. Includes any joined +// parties, else the same as G_LocalSplitscreenPartySize. +UINT8 G_PartySize(UINT8 player); + +// True if this player is a member of the consoleplayer's +// party. +boolean G_IsPartyLocal(UINT8 player); + +// Returns the player slot present at a certain position +// within this player's party. Do not call this function with +// an index beyond G_PartySize() - 1. +UINT8 G_PartyMember(UINT8 player, UINT8 index); + +// C array access to the same data as G_PartyMember. +const UINT8 *G_PartyArray(UINT8 player); + +// Suitable index to G_PartyMember and G_PartyArray. +UINT8 G_PartyPosition(UINT8 player); + +// +UINT8 G_LocalSplitscreenPartyPosition(UINT8 player); + +// +UINT8 G_LocalSplitscreenPartyMember(UINT8 player, UINT8 index); + +// +// Globals +// + +// Whether this player has been invited to join anyone's party +// and who invited them. -1 if no invitation. +extern INT32 splitscreen_invitations[MAXPLAYERS]; + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // __G_PARTY_H__ diff --git a/src/g_splitscreen.c b/src/g_splitscreen.c deleted file mode 100644 index e510a474c..000000000 --- a/src/g_splitscreen.c +++ /dev/null @@ -1,210 +0,0 @@ -// SONIC ROBO BLAST 2 KART -//----------------------------------------------------------------------------- -// Copyright (C) 2020 by James R. -// -// This program is free software distributed under the -// terms of the GNU General Public License, version 2. -// See the 'LICENSE' file for more details. -//----------------------------------------------------------------------------- -/// \file g_splitscreen.c -/// \brief some splitscreen stuff - -#include "doomdef.h" -#include "g_game.h" -#include "p_local.h" -#include "r_local.h" -#include "doomstat.h" - -INT32 splitscreen_original_party_size[MAXPLAYERS]; -INT32 splitscreen_original_party[MAXPLAYERS][MAXSPLITSCREENPLAYERS]; - -INT32 splitscreen_invitations[MAXPLAYERS]; -INT32 splitscreen_party_size[MAXPLAYERS]; -INT32 splitscreen_party[MAXPLAYERS][MAXSPLITSCREENPLAYERS]; - -boolean splitscreen_partied[MAXPLAYERS]; - -void -G_ResetSplitscreen (INT32 playernum) -{ - INT32 old_displayplayers[MAXSPLITSCREENPLAYERS]; - - INT32 i; - - splitscreen_party_size[playernum] = - splitscreen_original_party_size[playernum]; - - memcpy(splitscreen_party[playernum], splitscreen_original_party[playernum], - sizeof splitscreen_party[playernum]); - - if (playernum == consoleplayer) - { - memset(splitscreen_partied, 0, sizeof splitscreen_partied); - splitscreen_partied[consoleplayer] = true; - - memcpy(old_displayplayers, displayplayers, sizeof old_displayplayers); - - /* easier to just rebuild displayplayers with local players */ - for (i = 0; i <= splitscreen; ++i) - { - displayplayers[i] = g_localplayers[i]; - P_ResetCamera(&players[displayplayers[i]], &camera[i]); - } - - while (i < MAXSPLITSCREENPLAYERS) - { - displayplayers[i] = consoleplayer; - i++; - } - - r_splitscreen = splitscreen; - - R_ExecuteSetViewSize(); - } -} - -void -G_RemovePartyMember (INT32 playernum) -{ - INT32 old_party[MAXSPLITSCREENPLAYERS]; - INT32 new_party[MAXSPLITSCREENPLAYERS]; - - INT32 old_party_size; - INT32 before; - INT32 after; - INT32 views; - - INT32 i; - INT32 n; - - old_party_size = splitscreen_party_size[playernum]; - - for (i = 0; i < old_party_size; ++i) - { - /* exploit that splitscreen players keep order */ - if (splitscreen_party[playernum][i] == playernum) - { - before = i; - - views = splitscreen_original_party_size[playernum]; - after = ( before + views ); - - memcpy(old_party, splitscreen_party[playernum], sizeof old_party); - memcpy(new_party, old_party, before * sizeof *old_party); - - memcpy(&new_party[before], &old_party[after], - ( old_party_size - after ) * sizeof *new_party); - - views = ( old_party_size - views ); - - for (i = 0; i < old_party_size; ++i) - { - n = old_party[i]; - if (n != playernum && playerconsole[n] == n) - { - splitscreen_party_size[n] = views; - memcpy(splitscreen_party[n], new_party, - sizeof splitscreen_party[n]); - } - } - - /* don't want to remove yourself from your own screen! */ - if (playernum != consoleplayer && splitscreen_partied[playernum]) - { - splitscreen_partied[playernum] = false; - - for (i = 0; i < views; ++i) - { - displayplayers[i] = new_party[i]; - P_ResetCamera(&players[displayplayers[i]], &camera[i]); - } - while (i < MAXSPLITSCREENPLAYERS) - { - displayplayers[i] = displayplayers[0]; - - i++; - } - - r_splitscreen = ( views - 1 ); - - R_ExecuteSetViewSize(); - } - - break; - } - } -} - -void -G_AddPartyMember (INT32 invitation, INT32 playernum) -{ - INT32 * party; - INT32 *add_party; - - INT32 old_party_size; - INT32 new_party_size; - - INT32 views; - - INT32 i; - INT32 n; - - views = splitscreen_original_party_size[playernum]; - - old_party_size = splitscreen_party_size[invitation]; - new_party_size = ( old_party_size + views ); - - party = splitscreen_party[invitation]; - add_party = splitscreen_original_party[playernum]; - - for (i = 0; i < old_party_size; ++i) - { - n = party[i]; - if (playerconsole[n] == n) - { - splitscreen_party_size[n] = new_party_size; - memcpy(&splitscreen_party[n][old_party_size], add_party, - views * sizeof *splitscreen_party[n]); - } - } - - splitscreen_party_size[playernum] = new_party_size; - memcpy(splitscreen_party[playernum], party, - sizeof splitscreen_party[playernum]); - - /* in my party or adding me? */ - if (splitscreen_partied[invitation]) - { - splitscreen_partied[playernum] = true; - - for (i = old_party_size; i < new_party_size; ++i) - { - displayplayers[i] = party[i]; - P_ResetCamera(&players[displayplayers[i]], &camera[i]); - } - - r_splitscreen += views; - - R_ExecuteSetViewSize(); - } - else if (playernum == consoleplayer) - { - for (i = 0; i < new_party_size; ++i) - { - splitscreen_partied[playerconsole[party[i]]] = true; - - displayplayers[i] = party[i]; - P_ResetCamera(&players[displayplayers[i]], &camera[i]); - } - while (i < MAXSPLITSCREENPLAYERS) - { - displayplayers[i] = displayplayers[0]; - - i++; - } - - r_splitscreen = ( new_party_size - 1 ); - - R_ExecuteSetViewSize(); - } -} diff --git a/src/k_hud.c b/src/k_hud.c index c6c4641aa..f2ad6f0c3 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -38,6 +38,7 @@ #include "r_things.h" #include "r_fps.h" #include "m_random.h" +#include "g_party.h" #define NUMPOSNUMS 10 #define NUMPOSFRAMES 7 // White, three blues, three reds diff --git a/src/p_saveg.c b/src/p_saveg.c index 582a7a68f..9dbcb6e0a 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -42,6 +42,7 @@ #include "k_pwrlv.h" #include "k_terrain.h" #include "acs/interface.h" +#include "g_party.h" #include @@ -51,6 +52,7 @@ savedata_t savedata; // being sent and received #define ARCHIVEBLOCK_MISC 0x7FEEDEED #define ARCHIVEBLOCK_PLAYERS 0x7F448008 +#define ARCHIVEBLOCK_PARTIES 0x7F87AF0C #define ARCHIVEBLOCK_WORLD 0x7F8C08C0 #define ARCHIVEBLOCK_POBJS 0x7F928546 #define ARCHIVEBLOCK_THINKERS 0x7F37037C @@ -121,14 +123,6 @@ static void P_NetArchivePlayers(savebuffer_t *save) WRITEUINT8(save->p, playerconsole[i]); WRITEINT32(save->p, splitscreen_invitations[i]); - WRITEINT32(save->p, splitscreen_party_size[i]); - WRITEINT32(save->p, splitscreen_original_party_size[i]); - - for (j = 0; j < MAXSPLITSCREENPLAYERS; ++j) - { - WRITEINT32(save->p, splitscreen_party[i][j]); - WRITEINT32(save->p, splitscreen_original_party[i][j]); - } WRITEANGLE(save->p, players[i].angleturn); WRITEANGLE(save->p, players[i].aiming); @@ -440,14 +434,6 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) playerconsole[i] = READUINT8(save->p); splitscreen_invitations[i] = READINT32(save->p); - splitscreen_party_size[i] = READINT32(save->p); - splitscreen_original_party_size[i] = READINT32(save->p); - - for (j = 0; j < MAXSPLITSCREENPLAYERS; ++j) - { - splitscreen_party[i][j] = READINT32(save->p); - splitscreen_original_party[i][j] = READINT32(save->p); - } players[i].angleturn = READANGLE(save->p); players[i].aiming = READANGLE(save->p); @@ -722,6 +708,59 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) TracyCZoneEnd(__zone); } +static void P_NetArchiveParties(savebuffer_t *save) +{ + INT32 i, k; + UINT8 partySize; + + WRITEUINT32(save->p, ARCHIVEBLOCK_PARTIES); + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + continue; + + partySize = G_PartySize(i); + + WRITEUINT8(save->p, partySize); + + for (k = 0; k < partySize; ++k) + { + WRITEUINT8(save->p, G_PartyMember(i, k)); + } + } +} + +static void P_NetUnArchiveParties(savebuffer_t *save) +{ + INT32 i, k; + UINT8 partySize; + + if (READUINT32(save->p) != ARCHIVEBLOCK_PARTIES) + I_Error("Bad $$$.sav at archive block Parties"); + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + continue; + + G_BuildLocalSplitscreenParty(i); + } + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + continue; + + partySize = READUINT8(save->p); + + for (k = 0; k < partySize; ++k) + { + G_JoinParty(i, READUINT8(save->p)); + } + } +} + /// /// Colormaps /// @@ -5404,6 +5443,8 @@ void P_SaveNetGame(savebuffer_t *save, boolean resending) } P_NetArchivePlayers(save); + P_NetArchiveParties(save); + if (gamestate == GS_LEVEL) { P_NetArchiveWorld(save); @@ -5452,6 +5493,8 @@ boolean P_LoadNetGame(savebuffer_t *save, boolean reloading) if (!P_NetUnArchiveMisc(save,reloading)) return false; P_NetUnArchivePlayers(save); + P_NetUnArchiveParties(save); + if (gamestate == GS_LEVEL) { P_NetUnArchiveWorld(save); diff --git a/src/p_user.c b/src/p_user.c index ad2ce9c9d..4bf556f67 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -60,6 +60,7 @@ #include "k_terrain.h" // K_SpawnSplashForMobj #include "k_color.h" #include "k_follower.h" +#include "g_party.h" #include "acs/interface.h" @@ -717,7 +718,7 @@ boolean P_EndingMusic(player_t *player) if (r_splitscreen) { - INT32 *localplayertable = (splitscreen_partied[consoleplayer] ? splitscreen_party[consoleplayer] : g_localplayers); + const UINT8 *localplayertable = G_PartyArray(consoleplayer); if (!((players[localplayertable[0]].exiting || (players[localplayertable[0]].pflags & PF_NOCONTEST)) || (players[localplayertable[1]].exiting || (players[localplayertable[1]].pflags & PF_NOCONTEST)) @@ -816,7 +817,7 @@ void P_RestoreMusic(player_t *player) if (r_splitscreen) { INT32 bestlocaltimer = 1; - INT32 *localplayertable = (splitscreen_partied[consoleplayer] ? splitscreen_party[consoleplayer] : g_localplayers); + const UINT8 *localplayertable = G_PartyArray(consoleplayer); #define setbests(p) \ if (players[p].playerstate == PST_LIVE) \ @@ -1107,8 +1108,6 @@ boolean P_IsMachineLocalPlayer(player_t *player) // boolean P_IsLocalPlayer(player_t *player) { - UINT8 i; - if (player == NULL) { return false; @@ -1118,18 +1117,8 @@ boolean P_IsLocalPlayer(player_t *player) if (demo.playback) return false; - // parties - treat everyone as if it's couch co-op - if (splitscreen_partied[consoleplayer]) - { - for (i = 0; i < splitscreen_party_size[consoleplayer]; i++) - { - if (splitscreen_party[consoleplayer][i] == (player-players)) - return true; - } - return false; - } - - return P_IsMachineLocalPlayer(player); + // handles both online parties and local players (no need to call P_IsMachineLocalPlayer here) + return G_IsPartyLocal(player-players); } // @@ -3548,7 +3537,7 @@ boolean P_SpectatorJoinGame(player_t *player) // Reset away view (some code referenced from Got_Teamchange) { UINT8 i = 0; - INT32 *localplayertable = (splitscreen_partied[consoleplayer] ? splitscreen_party[consoleplayer] : g_localplayers); + const UINT8 *localplayertable = G_PartyArray(consoleplayer); for (i = 0; i < r_splitscreen; i++) { From 37c960a14a2176999f5c44fd5d4766398ffc584d Mon Sep 17 00:00:00 2001 From: NepDisk Date: Mon, 24 Mar 2025 10:24:26 -0400 Subject: [PATCH 20/45] Various fixes from RR --- src/d_netcmd.c | 100 +++++++++++++++++++------------------------------ src/g_demo.c | 31 +++++++++++++++ src/g_demo.h | 5 +++ src/g_game.c | 80 ++++++++++++++++++++++++++------------- src/g_game.h | 1 + src/p_inter.c | 5 +++ 6 files changed, 136 insertions(+), 86 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 3b914bfc3..2cd39ead3 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2269,13 +2269,6 @@ static void Command_SetViews_f(void) UINT8 splits; UINT8 newsplits; - if (!( demo.playback && multiplayer )) - { - CONS_Alert(CONS_NOTICE, - "You must be viewing a multiplayer replay to use this.\n"); - return; - } - if (COM_Argc() != 2) { CONS_Printf("setviews : set the number of split screens\n"); @@ -2286,12 +2279,33 @@ static void Command_SetViews_f(void) newsplits = atoi(COM_Argv(1)); newsplits = min(max(newsplits, 1), 4); - if (newsplits > splits) + + if (newsplits > splits && demo.playback && multiplayer) + { G_AdjustView(newsplits, 0, true); + } else { - r_splitscreen = newsplits-1; - R_ExecuteSetViewSize(); + // Even if the splits go beyond the real number of + // splitscreen players, displayplayers was filled + // with duplicates of P1 (see Got_AddPlayer). + if (demo.playback) + { + G_SyncDemoParty(consoleplayer, newsplits-1); + } + else + { + r_splitscreen = newsplits-1; + R_ExecuteSetViewSize(); + } + + // If promoting (outside of replays), make sure the + // camera is in the correct position. + UINT8 i; + for (i = splits + 1; i <= newsplits; ++i) + { + G_FixCamera(i); + } } } @@ -2308,7 +2322,7 @@ Command_Invite_f (void) return; } - if (r_splitscreen >= MAXSPLITSCREENPLAYERS) + if (G_PartySize(consoleplayer) >= MAXSPLITSCREENPLAYERS) { CONS_Alert(CONS_WARNING, "Your party is full!\n"); return; @@ -2327,9 +2341,9 @@ Command_Invite_f (void) return; } - if (invitee == consoleplayer) + if (G_IsPartyLocal(invitee)) { - CONS_Alert(CONS_WARNING, "You cannot invite yourself! Bruh!\n"); + CONS_Alert(CONS_WARNING, "That player is already a member of your party.\n"); return; } @@ -2337,6 +2351,7 @@ Command_Invite_f (void) { CONS_Alert(CONS_WARNING, "That player has already been invited to join another party.\n"); + return; } if ((G_PartySize(consoleplayer) + G_LocalSplitscreenPartySize(invitee)) > MAXSPLITSCREENPLAYERS) @@ -2344,6 +2359,7 @@ Command_Invite_f (void) CONS_Alert(CONS_WARNING, "That player joined with too many " "splitscreen players for your party.\n"); + return; } CONS_Printf( @@ -2387,6 +2403,7 @@ Command_CancelInvite_f (void) { CONS_Alert(CONS_WARNING, "You have not invited this player!\n"); + return; } CONS_Printf( @@ -2432,7 +2449,7 @@ Command_RejectInvite_f (void) static void Command_LeaveParty_f (void) { - if (r_splitscreen > splitscreen) + if (G_PartySize(consoleplayer) > G_LocalSplitscreenPartySize(consoleplayer)) { CONS_Printf("\x85Leaving party...\n"); @@ -3647,24 +3664,6 @@ void P_SetPlayerSpectator(INT32 playernum) players[playernum].pflags &= ~PF_WANTSTOJOIN; players[playernum].playerstate = PST_REBORN; - - /*if (cv_spectatormusic.value && (players[displayplayers[0]].spectator == true) && !r_splitscreen) - { - if (P_UseContinuousLevelMusic()) - { - if (!stricmp(Music_Song("level_nosync"), cv_spectatormusiclump.string)) - { - // Do not reset music if it is the same - Music_BatchExempt("level_nosync"); - } - Music_Remap("level_nosync", cv_spectatormusiclump.string); - } - else - { - Music_Remap("level", cv_spectatormusiclump.string); - } - }*/ - } //todo: This and the other teamchange functions are getting too long and messy. Needs cleaning. @@ -3742,24 +3741,19 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum) //Safety first! // (not respawning spectators here...) - if (!players[playernum].spectator) + wasspectator = (players[playernum].spectator == true); + + if (!wasspectator) { - if (players[playernum].mo) + if (gamestate == GS_LEVEL && players[playernum].mo) { - P_DamageMobj(players[playernum].mo, NULL, NULL, 1, - (NetPacket.packet.newteam ? DMG_INSTAKILL : DMG_SPECTATOR)); - } - //else - if (!NetPacket.packet.newteam) - { - players[playernum].playerstate = PST_REBORN; + // The following will call P_SetPlayerSpectator if successful + P_DamageMobj(players[playernum].mo, NULL, NULL, 1, DMG_SPECTATOR); } + //...but because the above could return early under some contexts, we try again here + P_SetPlayerSpectator(playernum); } - else - wasspectator = true; - - players[playernum].pflags &= ~PF_WANTSTOJOIN; //Now that we've done our error checking and killed the player //if necessary, put the player on the correct team/status. @@ -3809,22 +3803,6 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum) else if (NetPacket.packet.newteam == 0 && !wasspectator) HU_AddChatText(va("\x82*%s became a spectator.", player_names[playernum]), false); // "entered the game" text was moved to P_SpectatorJoinGame - // Reset away view (some code referenced from P_SpectatorJoinGame) - { - UINT8 i = 0; - INT32 *localplayertable = (splitscreen_partied[consoleplayer] ? splitscreen_party[consoleplayer] : g_localplayers); - - for (i = 0; i < r_splitscreen; i++) - { - if (localplayertable[i] == playernum) - { - LUA_HookViewpointSwitch(players+playernum, players+playernum, true); - displayplayers[i] = playernum; - break; - } - } - } - /*if (G_GametypeHasTeams()) { if (NetPacket.packet.newteam) diff --git a/src/g_demo.c b/src/g_demo.c index 3646bde41..3dfcb51e8 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -53,6 +53,7 @@ #include "k_color.h" #include "k_follower.h" #include "k_grandprix.h" +#include "g_party.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); @@ -4186,3 +4187,33 @@ boolean G_DemoTitleResponder(event_t *ev) return true; } + +void G_SyncDemoParty(INT32 rem, INT32 newsplitscreen) +{ + int r_splitscreen_copy = r_splitscreen; + INT32 displayplayers_copy[MAXSPLITSCREENPLAYERS]; + memcpy(displayplayers_copy, displayplayers, sizeof displayplayers); + + // If we switch away from someone's view, that player + // should be removed from the party. + // However, it is valid to have the player on multiple + // viewports. + + // Remove this player + G_LeaveParty(rem); + + // And reset the rest of the party + for (int i = 0; i <= r_splitscreen_copy; ++i) + G_LeaveParty(displayplayers_copy[i]); + + // Restore the party, without the removed player, and + // with the order matching displayplayers + for (int i = 0; i <= newsplitscreen; ++i) + G_JoinParty(consoleplayer, displayplayers_copy[i]); + + // memcpy displayplayers back to preserve duplicates + // (G_JoinParty will not create duplicates itself) + r_splitscreen = newsplitscreen; + memcpy(displayplayers, displayplayers_copy, sizeof displayplayers); + R_ExecuteSetViewSize(); +} diff --git a/src/g_demo.h b/src/g_demo.h index 8a73145ad..78dd0d217 100644 --- a/src/g_demo.h +++ b/src/g_demo.h @@ -123,6 +123,9 @@ extern UINT8 demo_writerng; #define DXD_COLOR 0x10 // color changed #define DXD_FOLLOWER 0x20 // follower was changed #define DXD_RESPAWN 0x40 // "respawn" command in console + +#define DXD_ADDPLAYER (DXD_JOINDATA|DXD_PLAYSTATE|DXD_COLOR|DXD_NAME|DXD_SKIN|DXD_FOLLOWER) + #define DXD_WEAPONPREF 0x80 // netsynced playsim settings were changed #define DXD_PST_PLAYING 0x01 @@ -197,6 +200,8 @@ void G_SaveDemo(void); boolean G_DemoTitleResponder(event_t *ev); +void G_SyncDemoParty(INT32 rem, INT32 newsplitscreen); + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/g_game.c b/src/g_game.c index 19117bbb2..4b9ba9424 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1805,19 +1805,12 @@ INT32 G_CountPlayersPotentiallyViewable(boolean active) return total; } -// -// G_ResetView -// Correct a viewpoint to playernum or the next available, wraps forward. -// Also promotes splitscreen up to available viewable players. -// An out of range playernum is corrected. -// void G_ResetView(UINT8 viewnum, INT32 playernum, boolean onlyactive) { UINT8 splits; UINT8 viewd; INT32 *displayplayerp; - camera_t *camerap; INT32 olddisplayplayer; INT32 playersviewable; @@ -1843,33 +1836,70 @@ void G_ResetView(UINT8 viewnum, INT32 playernum, boolean onlyactive) /* Check if anyone is available to view. */ if (( playernum = G_FindView(playernum, viewnum, onlyactive, playernum < olddisplayplayer) ) == -1) - return; + { + if (G_PartySize(consoleplayer) < viewnum) + { + return; + } + + /* Fall back on true self */ + playernum = G_PartyMember(consoleplayer, viewnum - 1); + } + + // Call ViewpointSwitch hooks here. + // The viewpoint was forcibly changed. + LUA_HookViewpointSwitch(&players[g_localplayers[viewnum - 1]], &players[playernum], true); /* Focus our target view first so that we don't take its player. */ (*displayplayerp) = playernum; - if ((*displayplayerp) != olddisplayplayer) - { - camerap = &camera[viewnum-1]; - P_ResetCamera(&players[(*displayplayerp)], camerap); - - R_ResetViewInterpolation(viewnum); - } + /* If a viewpoint changes, reset the camera to clear uninitialized memory. */ if (viewnum > splits) { - for (viewd = splits+1; viewd < viewnum; ++viewd) + for (viewd = splits+1; viewd <= viewnum; ++viewd) { - displayplayerp = (&displayplayers[viewd-1]); - camerap = &camera[viewd]; - - (*displayplayerp) = G_FindView(0, viewd, onlyactive, false); - - P_ResetCamera(&players[(*displayplayerp)], camerap); + G_FixCamera(viewd); + } + } + else + { + if ((*displayplayerp) != olddisplayplayer) + { + G_FixCamera(viewnum); } } - if (viewnum == 1 && demo.playback) - consoleplayer = displayplayers[0]; + if (demo.playback) + { + if (viewnum == 1) + consoleplayer = displayplayers[0]; + + G_SyncDemoParty(olddisplayplayer, r_splitscreen); + } + + // change statusbar also if playing back demo + if (demo.quitafterplaying) + ST_changeDemoView(); +} + +// +// G_FixCamera +// Reset camera position, angle and interpolation on a view +// after changing state. +// +void G_FixCamera(UINT8 view) +{ + player_t *player = &players[displayplayers[view - 1]]; + + // The order of displayplayers can change, which would + // invalidate localangle. + localangle[view - 1] = player->angleturn; + + P_ResetCamera(player, &camera[view - 1]); + + // Make sure the viewport doesn't interpolate at all into + // its new position -- just snap instantly into place. + R_ResetViewInterpolation(view); } // @@ -2949,7 +2979,7 @@ void G_AddPlayer(INT32 playernum, INT32 console) newplayer->playerstate = PST_REBORN; newplayer->jointime = 0; - demo_extradata[playernum] |= DXD_PLAYSTATE|DXD_COLOR|DXD_NAME|DXD_SKIN|DXD_FOLLOWER; // Set everything + demo_extradata[playernum] |= DXD_ADDPLAYER; // Set everything } void G_BeginLevelExit(void) diff --git a/src/g_game.h b/src/g_game.h index 8bcab9b66..65abb025d 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -220,6 +220,7 @@ boolean G_CanView(INT32 playernum, UINT8 viewnum, boolean onlyactive); INT32 G_FindView(INT32 startview, UINT8 viewnum, boolean onlyactive, boolean reverse); INT32 G_CountPlayersPotentiallyViewable(boolean active); +void G_FixCamera(UINT8 view); void G_ResetViews(void); void G_ResetView(UINT8 viewnum, INT32 playernum, boolean onlyactive); void G_AdjustView(UINT8 viewnum, INT32 offset, boolean onlyactive); diff --git a/src/p_inter.c b/src/p_inter.c index e208e4d7f..48bc0812d 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1981,6 +1981,11 @@ static boolean P_KillPlayer(player_t *player, mobj_t *inflictor, mobj_t *source, (void)source; (void)inflictor; + if (type == DMG_SPECTATOR && (G_GametypeHasTeams() || G_GametypeHasSpectators())) + { + P_SetPlayerSpectator(player-players); + } + if (player->exiting) { player->mo->destscale = 1; From fd8917092e6aee19f899e029f8109fe25de82c6f Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 1 Mar 2024 06:00:40 -0800 Subject: [PATCH 21/45] Improve displayplayers command - Tabulate data - Show party members --- src/d_netcmd.c | 100 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 75 insertions(+), 25 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 2cd39ead3..faa9b4e62 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -5724,35 +5724,85 @@ static void Got_Cheat(UINT8 **cp, INT32 playernum) } } +static const char *displayplayer_compose_col(int playernum) +{ + return va("\x84(%d) \x83%s\x80", playernum, player_names[playernum]); +} + +static int displayplayer_col_len(const char *text) +{ + int n = strlen(text); + int k = n; + int i; + for (i = 0; i < n; ++i) + { + if (!isprint(text[i])) + k--; + } + return k; +} + +static void displayplayer_calc_col(int *col, const char *text) +{ + if (text && text[0] != ' ') + { + int n = displayplayer_col_len(text); + if (*col < n) + *col = n; + } +} + +static void displayplayer_print_col(int *col, const char *text) +{ + if (text) + { + if (*col) + { + int n = *col - displayplayer_col_len(text); + CONS_Printf("%s%*s ", text, n, ""); + } + } + else + CONS_Printf("\n"); +} + +static void displayplayer_iter_table(int table[5], void(*col_cb)(int*,const char*)) +{ + int i; + + col_cb(&table[0], ""); + for (i = 0; i < 4; ++i) + col_cb(&table[1 + i], va(" %d", i)); + col_cb(NULL, NULL); + + col_cb(&table[0], "g_local"); + for (i = 0; i <= splitscreen; ++i) + col_cb(&table[1 + i], displayplayer_compose_col(g_localplayers[i])); + col_cb(NULL, NULL); + + col_cb(&table[0], "display"); + for (i = 0; i <= r_splitscreen; ++i) + col_cb(&table[1 + i], displayplayer_compose_col(displayplayers[i])); + col_cb(NULL, NULL); + + col_cb(&table[0], "local party"); + for (i = 0; i < G_LocalSplitscreenPartySize(consoleplayer); ++i) + col_cb(&table[1 + i], displayplayer_compose_col(G_LocalSplitscreenPartyMember(consoleplayer, i))); + col_cb(NULL, NULL); + + col_cb(&table[0], "final party"); + for (i = 0; i < G_PartySize(consoleplayer); ++i) + col_cb(&table[1 + i], displayplayer_compose_col(G_PartyMember(consoleplayer, i))); + col_cb(NULL, NULL); +} + /** Prints the number of displayplayers[0]. - * - * \todo Possibly remove this; it was useful for debugging at one point. */ static void Command_Displayplayer_f(void) { - int playernum; - int i; - for (i = 0; i <= splitscreen; ++i) - { - playernum = g_localplayers[i]; - CONS_Printf( - "local player %d: \x84(%d) \x83%s\x80\n", - i, - playernum, - player_names[playernum] - ); - } - CONS_Printf("\x83----------------------------------------\x80\n"); - for (i = 0; i <= r_splitscreen; ++i) - { - playernum = displayplayers[i]; - CONS_Printf( - "display player %d: \x84(%d) \x83%s\x80\n", - i, - playernum, - player_names[playernum] - ); - } + int table[5] = {0}; + displayplayer_iter_table(table, displayplayer_calc_col); + displayplayer_iter_table(table, displayplayer_print_col); } /** Quits a game and returns to the title screen. From 616941841c3b48bc1d4c3a35bf07faeebce43ab3 Mon Sep 17 00:00:00 2001 From: NepDisk Date: Mon, 24 Mar 2025 11:16:33 -0400 Subject: [PATCH 22/45] Fix being able to reverse in the air --- src/k_kart.c | 8 +++----- src/k_kart.h | 2 +- src/p_user.c | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 9f3004642..70ea946f4 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3598,9 +3598,6 @@ fixed_t K_GetNewSpeed(player_t *player) const fixed_t p_speed = K_GetKartSpeed(player, true, true); fixed_t p_accel = K_GetKartAccel(player); fixed_t newspeed, oldspeed, finalspeed; - boolean onground = (P_IsObjectOnGround(player->mo) || (player->pogospring)); - - if (!onground) return 0; // If the player isn't on the ground, there is no change in speed if (K_PlayerUsesBotMovement(player) == true && player->botvars.rubberband > 0) { @@ -3635,12 +3632,13 @@ fixed_t K_GetNewSpeed(player_t *player) return finalspeed; } -fixed_t K_3dKartMovement(player_t *player) +fixed_t K_3dKartMovement(player_t *player, boolean onground) { fixed_t finalspeed = K_GetNewSpeed(player); - SINT8 forwardmove = K_GetForwardMove(player); + if (!onground) return 0; // If the player isn't on the ground, there is no change in speed + // forwardmove is: // 50 while accelerating, // 25 while clutching, diff --git a/src/k_kart.h b/src/k_kart.h index 01294e277..c4d9bae67 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -169,7 +169,7 @@ boolean K_KartKickstart(player_t *player); UINT16 K_GetKartButtons(player_t *player); SINT8 K_GetForwardMove(player_t *player); fixed_t K_GetNewSpeed(player_t *player); -fixed_t K_3dKartMovement(player_t *player); +fixed_t K_3dKartMovement(player_t *player, boolean onground); SINT8 K_Sliptiding(player_t *player); void K_MoveKartPlayer(player_t *player, boolean onground); void K_CheckSpectateStatus(boolean considermapreset); diff --git a/src/p_user.c b/src/p_user.c index 4bf556f67..8ebfd00cd 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -1829,7 +1829,7 @@ static void P_3dMovement(player_t *player) // Forward movement if (!((player->exiting || mapreset) || (P_PlayerInPain(player) && !onground))) { - movepushforward = K_3dKartMovement(player); + movepushforward = K_3dKartMovement(player, onground); // allow very small movement while in air for gameplay if (!onground) From 2c36b047dd934fec2e27de3de05d213a25c64b4a Mon Sep 17 00:00:00 2001 From: Oni Date: Wed, 4 Oct 2023 05:28:45 +0000 Subject: [PATCH 23/45] Merge branch 'fix-splitscreen-hud-tracking-bleed' into 'master' K_drawKartNameTags: crop HUD tracking to splitscreen viewports See merge request KartKrew/Kart!1542 --- src/k_hud.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/k_hud.c b/src/k_hud.c index f2ad6f0c3..ee91c3fe6 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -2713,6 +2713,31 @@ static void K_drawKartNameTags(void) return; } + // Crop within splitscreen bounds + switch (r_splitscreen) + { + case 1: + V_SetClipRect( + 0, + cnum == 1 ? (BASEVIDHEIGHT / 2) * FRACUNIT : 0, + BASEVIDWIDTH * FRACUNIT, + (BASEVIDHEIGHT / 2) * FRACUNIT, + 0 + ); + break; + + case 2: + case 3: + V_SetClipRect( + cnum & 1 ? (BASEVIDWIDTH / 2) * FRACUNIT : 0, + cnum > 1 ? (BASEVIDHEIGHT / 2) * FRACUNIT : 0, + (BASEVIDWIDTH / 2) * FRACUNIT, + (BASEVIDHEIGHT / 2) * FRACUNIT, + 0 + ); + break; + } + c.x = viewx; c.y = viewy; c.z = viewz; @@ -2942,6 +2967,8 @@ static void K_drawKartNameTags(void) } } } + + V_ClearClipRect(); } static void K_drawKartMinimapIcon(fixed_t objx, fixed_t objy, INT32 hudx, INT32 hudy, INT32 flags, patch_t *icon, UINT8 *colormap) From 36ad821c798da4db9db710fe55177dfc1a9b3447 Mon Sep 17 00:00:00 2001 From: "James R." Date: Mon, 11 Sep 2023 02:45:33 -0700 Subject: [PATCH 24/45] K_drawKartMinimap: fix splitscreen player icons --- src/k_hud.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_hud.c b/src/k_hud.c index ee91c3fe6..649bd4a1c 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -3390,7 +3390,7 @@ static void K_drawKartMinimap(void) if (localplayers[i] == -1) continue; // this doesn't interest us - if ((players[i].hyudorotimer > 0) && (leveltime & 1)) + if ((players[localplayers[i]].hyudorotimer > 0) && (leveltime & 1)) continue; mobj = players[localplayers[i]].mo; From 04744338a62a87e03ed0c07f1b2648ce69d2d703 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 26 Feb 2023 21:39:37 -0800 Subject: [PATCH 25/45] Director: skip splitscreen players when switching This makes sure director only tries switching to players who aren't splitscreen players. --- src/k_director.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/k_director.c b/src/k_director.c index 6ba86b0d9..8d05fcaf6 100644 --- a/src/k_director.c +++ b/src/k_director.c @@ -270,8 +270,20 @@ void K_UpdateDirector(void) target = directorinfo.sortedplayers[targetposition]; + // stop here since we're already viewing this player + if (*displayplayerp == target) + { + break; + } + + // if this is a splitscreen player, try next pair + if (P_IsDisplayPlayer(&players[target])) + { + continue; + } + // if we're certain the back half of the pair is actually in this position, try to switch - if (*displayplayerp != target && !players[target].positiondelay) + if (!players[target].positiondelay) { K_DirectorSwitch(target, false); } From 591a909eeac708f1a1a26fdf03fe62553ca18806 Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 14 Mar 2024 00:06:52 -0700 Subject: [PATCH 26/45] Software: krangle directional lighting in 3P/4P splitscreen --- src/r_things.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/r_things.cpp b/src/r_things.cpp index a4eb35b19..cb6ac9e53 100644 --- a/src/r_things.cpp +++ b/src/r_things.cpp @@ -2310,6 +2310,14 @@ static void R_ProjectSprite(mobj_t *thing) ? interp.angle + (ang >= ANGLE_180 ? -ANGLE_90 : ANGLE_90) : R_PointToAngle(interp.x, interp.y)); + // Krangle contrast in 3P/4P because scalelight + // scales differently depending on the screen + // width (which is halved in 3P/4P). + if (r_splitscreen > 1) + { + extralight *= 2; + } + // Less change in contrast in dark sectors extralight = FixedMul(extralight, std::min(std::max(0, lightnum), LIGHTLEVELS - 1) * FRACUNIT / (LIGHTLEVELS - 1)); From ad290b3604c0be5fa53c7153d6b3273d28340839 Mon Sep 17 00:00:00 2001 From: NepDisk Date: Mon, 24 Mar 2025 12:44:14 -0400 Subject: [PATCH 27/45] add back removed comments for G_ResetView --- src/g_game.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/g_game.c b/src/g_game.c index 4b9ba9424..afff8e8dd 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1805,6 +1805,12 @@ INT32 G_CountPlayersPotentiallyViewable(boolean active) return total; } +// +// G_ResetView +// Correct a viewpoint to playernum or the next available, wraps forward. +// Also promotes splitscreen up to available viewable players. +// An out of range playernum is corrected. +// void G_ResetView(UINT8 viewnum, INT32 playernum, boolean onlyactive) { UINT8 splits; From a5be688b82074d5cd7b933f8f15f2ced43607072 Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 1 Mar 2024 06:05:52 -0800 Subject: [PATCH 28/45] Replays: keep party in sync with current viewpoints - More and more parts of the game rely on parties - Parties are assumed to match the displayplayers - This fixes A/B/C/D nametags --- src/g_demo.c | 1 + src/m_menu.c | 14 +++++++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/g_demo.c b/src/g_demo.c index 3dfcb51e8..41529ad52 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -40,6 +40,7 @@ #include "lua_hook.h" #include "md5.h" // demo checksums #include "p_saveg.h" // savebuffer_t +#include "g_party.h" // SRB2Kart #include "d_netfil.h" // nameonly diff --git a/src/m_menu.c b/src/m_menu.c index db17339ca..485272f47 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -6208,12 +6208,10 @@ static void M_PlaybackAdvance(INT32 choice) paused = true; } - static void M_PlaybackSetViews(INT32 choice) { - if (demo.freecam) - return; // not here. + return; // not here. if (choice > 0) { @@ -6222,8 +6220,14 @@ static void M_PlaybackSetViews(INT32 choice) } else if (r_splitscreen) { - r_splitscreen--; - R_ExecuteSetViewSize(); + if (choice == 0) + { + G_SyncDemoParty(displayplayers[r_splitscreen], r_splitscreen - 1); + } + else + { + G_SyncDemoParty(consoleplayer, 0); + } } } From ceed76cc732482ef6a9c071a1d6c762f5437e2c9 Mon Sep 17 00:00:00 2001 From: NepDisk Date: Mon, 24 Mar 2025 14:24:03 -0400 Subject: [PATCH 29/45] Repair parties for blankart --- src/d_clisrv.c | 3 --- src/g_game.c | 6 ++++-- src/p_user.c | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index b541c8c59..8f7cc84f6 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -3619,9 +3619,6 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum) players[newplayernum].splitscreenindex = splitscreenplayer; players[newplayernum].bot = false; - playerconsole[newplayernum] = console; - G_BuildLocalSplitscreenParty(newplayernum); - if (netgame) { char joinmsg[256]; diff --git a/src/g_game.c b/src/g_game.c index afff8e8dd..ed7bd846d 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -950,15 +950,17 @@ angle_t localangle[MAXSPLITSCREENPLAYERS]; // This brings back the camera prediction that was lost. static void G_DoAnglePrediction(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer, player_t *player) { + UINT8 viewnum = G_PartyPosition(g_localplayers[ssplayer-1]); + if (player->mo) cmd->angle = K_GetKartTurnValue(player, cmd->turning); cmd->angle *= realtics; if (P_CanPlayerTurn(player, cmd)) - localangle[ssplayer-1] += (cmd->angle<angle<angle = (INT16)(localangle[ssplayer-1] >> TICCMD_REDUCE); + cmd->angle = (INT16)(localangle[viewnum] >> TICCMD_REDUCE); } void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) diff --git a/src/p_user.c b/src/p_user.c index 8ebfd00cd..37aaff733 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -2913,7 +2913,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall focusangle = player->cmd.angle << TICCMD_REDUCE; focusaiming = 0; } - else if (P_IsLocalPlayer(player)) + else if (P_IsMachineLocalPlayer(player)) { focusangle = localangle[num]; focusaiming = localaiming[num]; From 35a7f420af8836f2e3bfdb9884fa1c2392f1be90 Mon Sep 17 00:00:00 2001 From: NepDisk Date: Mon, 24 Mar 2025 18:54:58 -0400 Subject: [PATCH 30/45] Fix spectate button, allow respawning local players 2-4, allow respawn to be binded to button --- src/d_netcmd.c | 50 +++++++++++++++++++++++++++++++++++++++++--------- src/g_game.c | 40 ++++++++++++++++++++++++++++++++++++++++ src/g_input.c | 3 ++- src/g_input.h | 1 + src/m_menu.c | 1 + 5 files changed, 85 insertions(+), 10 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index faa9b4e62..48ef54b13 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -179,7 +179,11 @@ static void Command_ListWADS_f(void); static void Command_ListDoomednums_f(void); static void Command_RunSOC(void); static void Command_Pause(void); + static void Command_Respawn(void); +static void Command_Respawn2(void); +static void Command_Respawn3(void); +static void Command_Respawn4(void); static void Command_Version_f(void); #ifdef UPDATE_ALERT @@ -740,7 +744,11 @@ void D_RegisterServerCommands(void) COM_AddCommand("runsoc", Command_RunSOC); COM_AddCommand("pause", Command_Pause); + COM_AddCommand("respawn", Command_Respawn); + COM_AddCommand("respawn2", Command_Respawn2); + COM_AddCommand("respawn3", Command_Respawn3); + COM_AddCommand("respawn4", Command_Respawn4); COM_AddCommand("gametype", Command_ShowGametype_f); COM_AddCommand("version", Command_Version_f); @@ -3317,34 +3325,58 @@ static void Got_Pause(UINT8 **cp, INT32 playernum) } // Command for stuck characters in netgames, griefing, etc. -static void Command_Respawn(void) +static void HandleRespawnCommand(UINT8 localplayer) { UINT8 buf[4]; UINT8 *cp = buf; - - if (!(gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_VOTING)) { CONS_Printf(M_GetText("You must be in a level to use this.\n")); return; } - if (players[consoleplayer].mo && !P_IsObjectOnGround(players[consoleplayer].mo)) // KART: Nice try, but no, you won't be cheesing spb anymore. + if (players[g_localplayers[localplayer]].mo && !P_IsObjectOnGround(players[g_localplayers[localplayer]].mo)) // KART: Nice try, but no, you won't be cheesing spb anymore. { CONS_Printf(M_GetText("You must be on the floor to use this.\n")); return; } - // todo: this probably isnt necessary anymore with v2 - if (players[consoleplayer].mo && (P_PlayerInPain(&players[consoleplayer]) || spbplace == players[consoleplayer].position)) // KART: Nice try, but no, you won't be cheesing spb anymore (x2) + if (players[g_localplayers[localplayer]].mo && (P_PlayerInPain(&players[g_localplayers[localplayer]]) || spbplace == players[g_localplayers[localplayer]].position)) // KART: Nice try, but no, you won't be cheesing spb anymore (x2) { CONS_Printf(M_GetText("Nice try.\n")); return; } - WRITEINT32(cp, consoleplayer); - SendNetXCmd(XD_RESPAWN, &buf, 4); + if (localplayer != 0 && !g_localplayers[localplayer]) + { + CONS_Printf(M_GetText("There is no player to respawn.\n")); + return; + } + + WRITEINT32(cp, g_localplayers[localplayer]); + SendNetXCmdForPlayer(localplayer, XD_RESPAWN, &buf, sizeof(buf)); +} + +// Command for stuck characters in netgames, griefing, etc. +static void Command_Respawn(void) +{ + HandleRespawnCommand(0); +} + +static void Command_Respawn2(void) +{ + HandleRespawnCommand(1); +} + +static void Command_Respawn3(void) +{ + HandleRespawnCommand(2); +} + +static void Command_Respawn4(void) +{ + HandleRespawnCommand(3); } static void Got_Respawn(UINT8 **cp, INT32 playernum) @@ -3366,7 +3398,7 @@ static void Got_Respawn(UINT8 **cp, INT32 playernum) if (!P_IsObjectOnGround(players[respawnplayer].mo)) return; - P_DamageMobj(players[respawnplayer].mo, NULL, NULL, 1,DMG_INSTAKILL); + P_DamageMobj(players[respawnplayer].mo, NULL, NULL, 1, DMG_INSTAKILL); demo_extradata[playernum] |= DXD_RESPAWN; } } diff --git a/src/g_game.c b/src/g_game.c index ed7bd846d..765736886 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1450,6 +1450,8 @@ boolean G_IsTitleCardAvailable(void) INT32 pausedelay = 0; boolean pausebreakkey = false; static INT32 camtoggledelay[MAXSPLITSCREENPLAYERS]; +static INT32 spectatedelay[MAXSPLITSCREENPLAYERS]; +static INT32 respawndelay[MAXSPLITSCREENPLAYERS]; // // G_Responder @@ -1678,6 +1680,40 @@ boolean G_Responder(event_t *ev) CV_SetValue(&cv_chasecam[i], cv_chasecam[i].value ? 0 : 1); } } + + if (G_ControlBoundToKey(i, gc_spectate, ev->data1, false)) + { + if (!spectatedelay[i]) + { + char *commandname = va("changeteam"); + + if (i > 0) + { + // Add one for command names. + commandname = va("changeteam%d", i+1); + } + + spectatedelay[i] = NEWTICRATE / 7; + COM_ImmedExecute(va("%s spectator", commandname)); + } + } + + if (G_ControlBoundToKey(i, gc_respawn, ev->data1, false)) + { + if (!respawndelay[i]) + { + char *commandname = va("respawn"); + + if (i > 0) + { + // Add one for command names. + commandname = va("respawn%d", i+1); + } + + respawndelay[i] = NEWTICRATE / 4; + COM_ImmedExecute(commandname); + } + } } return true; @@ -2157,6 +2193,10 @@ void G_Ticker(boolean run) { if (camtoggledelay[i]) camtoggledelay[i]--; + if (spectatedelay[i]) + spectatedelay[i]--; + if (respawndelay[i]) + respawndelay[i]--; } if (gametic % NAMECHANGERATE == 0) diff --git a/src/g_input.c b/src/g_input.c index 459571a22..4418f5ded 100644 --- a/src/g_input.c +++ b/src/g_input.c @@ -405,6 +405,7 @@ static const char *gamecontrolname[num_gamecontrols] = "custom1", "custom2", "custom3", + "respawn", }; #define NUMKEYNAMES (sizeof (keynames)/sizeof (keyname_t)) @@ -681,7 +682,7 @@ static void setcontrol(UINT8 player) namectrl = COM_Argv(1); for (numctrl = 0; - numctrl < num_gamecontrols && stricmp(namectrl, gamecontrolname[numctrl]); + numctrl < num_gamecontrols && gamecontrolname[numctrl] && stricmp(namectrl, gamecontrolname[numctrl]); numctrl++) { ; } diff --git a/src/g_input.h b/src/g_input.h index a5601486b..a06221e46 100644 --- a/src/g_input.h +++ b/src/g_input.h @@ -85,6 +85,7 @@ typedef enum gc_custom1, // Lua scriptable gc_custom2, // Lua scriptable gc_custom3, // Lua scriptable + gc_respawn, num_gamecontrols } gamecontrols_e; diff --git a/src/m_menu.c b/src/m_menu.c index 485272f47..fb8d93807 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -1200,6 +1200,7 @@ static menuitem_t OP_AllControlsMenu[] = //{IT_CONTROL, NULL, "Team Chat", M_ChangeControl, gc_teamkey }, {IT_CONTROL, NULL, "Show Rankings", {.routine = M_ChangeControl}, gc_scores }, {IT_CONTROL, NULL, "Change Viewpoint", {.routine = M_ChangeControl}, gc_viewpoint }, + {IT_CONTROL, NULL, "Respawn", {.routine = M_ChangeControl}, gc_respawn }, {IT_CONTROL, NULL, "Reset Camera", {.routine = M_ChangeControl}, gc_camreset }, {IT_CONTROL, NULL, "Toggle Chasecam", {.routine = M_ChangeControl}, gc_camtoggle }, {IT_CONTROL, NULL, "Pause", {.routine = M_ChangeControl}, gc_pause }, From 6511baabaf8eafa6bc4ae265bf390f638a3960bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustaf=20Alh=C3=A4ll?= Date: Wed, 22 Nov 2023 19:41:35 +0100 Subject: [PATCH 31/45] Fix LAN discovery for SRB2 servers --- src/i_tcp.c | 97 ++++++++++++++++++++++++----------------------------- 1 file changed, 43 insertions(+), 54 deletions(-) diff --git a/src/i_tcp.c b/src/i_tcp.c index d7b5b0993..c86c1146a 100644 --- a/src/i_tcp.c +++ b/src/i_tcp.c @@ -165,7 +165,9 @@ typedef union #define ERRSOCKET (-1) #endif -// define socklen_t in Windows if it is not already defined +#define IPV6_MULTICAST_ADDRESS "ff15::57e1:1a12" + +// define socklen_t in DOS/Windows if it is not already defined #ifdef USE_WINSOCK1 typedef int socklen_t; #endif @@ -717,6 +719,7 @@ static inline ssize_t SOCK_SendToAddr(SOCKET_TYPE socket, mysockaddr_t *sockaddr socklen_t d6 = (socklen_t)sizeof(struct sockaddr_in6); #endif socklen_t d, da = (socklen_t)sizeof(mysockaddr_t); + ssize_t status; switch (sockaddr->any.sa_family) { @@ -727,7 +730,12 @@ static inline ssize_t SOCK_SendToAddr(SOCKET_TYPE socket, mysockaddr_t *sockaddr default: d = da; break; } - return sendto(socket, (char *)&doomcom->data, doomcom->datalength, 0, &sockaddr->any, d); + status = sendto(socket, (char *)&doomcom->data, doomcom->datalength, 0, &sockaddr->any, d); + if (status == -1) + { + CONS_Alert(CONS_WARNING, "Unable to send packet to %s: %s\n", SOCK_AddrToStr(sockaddr), strerror(errno)); + } + return status; } #define ALLOWEDERROR(x) ((x) == ECONNREFUSED || (x) == EWOULDBLOCK || (x) == EHOSTUNREACH || (x) == ENETUNREACH) @@ -880,6 +888,24 @@ static SOCKET_TYPE UDP_Bind(int family, struct sockaddr *addr, socklen_t addrlen return (SOCKET_TYPE)ERRSOCKET; } +#ifdef HAVE_IPV6 + if (family == AF_INET6) + { + // we need to set all of this *after* binding to an address! + if (memcmp(&straddr.ip6.sin6_addr, &in6addr_any, sizeof(in6addr_any)) == 0) //IN6_ARE_ADDR_EQUAL + { + struct ipv6_mreq maddr; + + inet_pton(AF_INET6, IPV6_MULTICAST_ADDRESS, &maddr.ipv6mr_multiaddr); + maddr.ipv6mr_interface = 0; + if (setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP, &maddr, sizeof(maddr)) != 0) + { + CONS_Alert(CONS_WARNING, M_GetText("Could not register multicast address\n")); + } + } + } +#endif + #ifdef FIONBIO // make it non blocking opt = true; @@ -1053,65 +1079,28 @@ static boolean UDP_Socket(void) // ip + udp packetheaderlength = 20 + 8; // for stats - hints.ai_family = AF_INET; - gaie = I_getaddrinfo("127.0.0.1", "0", &hints, &ai); - if (gaie == 0) - { - runp = ai; - while (runp != NULL && s < MAXNETNODES+1) - { - memcpy(&clientaddress[s], runp->ai_addr, runp->ai_addrlen); - s++; - runp = runp->ai_next; - } - I_freeaddrinfo(ai); - } - else - { - clientaddress[s].any.sa_family = AF_INET; - clientaddress[s].ip4.sin_port = htons(0); - clientaddress[s].ip4.sin_addr.s_addr = htonl(INADDR_LOOPBACK); //GetLocalAddress(); // my own ip - s++; - } + clientaddress[s].any.sa_family = AF_INET; + clientaddress[s].ip4.sin_port = htons(0); + clientaddress[s].ip4.sin_addr.s_addr = htonl(INADDR_LOOPBACK); //GetLocalAddress(); // my own ip + s++; s = 0; // setup broadcast adress to BROADCASTADDR entry - gaie = I_getaddrinfo("255.255.255.255", "0", &hints, &ai); - if (gaie == 0) - { - runp = ai; - while (runp != NULL && s < MAXNETNODES+1) - { - memcpy(&broadcastaddress[s], runp->ai_addr, runp->ai_addrlen); - s++; - runp = runp->ai_next; - } - I_freeaddrinfo(ai); - } - else - { - broadcastaddress[s].any.sa_family = AF_INET; - broadcastaddress[s].ip4.sin_port = htons(0); - broadcastaddress[s].ip4.sin_addr.s_addr = htonl(INADDR_BROADCAST); - s++; - } + broadcastaddress[s].any.sa_family = AF_INET; + broadcastaddress[s].ip4.sin_port = htons(atoi(DEFAULTPORT)); + broadcastaddress[s].ip4.sin_addr.s_addr = htonl(INADDR_BROADCAST); + s++; + #ifdef HAVE_IPV6 if (b_ipv6) { - hints.ai_family = AF_INET6; - gaie = I_getaddrinfo("ff02::1", "0", &hints, &ai); - if (gaie == 0) - { - runp = ai; - while (runp != NULL && s < MAXNETNODES+1) - { - memcpy(&broadcastaddress[s], runp->ai_addr, runp->ai_addrlen); - s++; - runp = runp->ai_next; - } - I_freeaddrinfo(ai); - } + broadcastaddress[s].any.sa_family = AF_INET6; + broadcastaddress[s].ip6.sin6_port = htons(atoi(DEFAULTPORT)); + broadcastaddress[s].ip6.sin6_flowinfo = 0; + inet_pton(AF_INET6, IPV6_MULTICAST_ADDRESS, &broadcastaddress[s].ip6.sin6_addr); + broadcastaddress[s].ip6.sin6_scope_id = 0; + s++; } #endif From ba4718883c1aa0bf7320b91e919aba96b92de3d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustaf=20Alh=C3=A4ll?= Date: Wed, 22 Nov 2023 20:26:55 +0100 Subject: [PATCH 32/45] Fix Windows build --- src/i_tcp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i_tcp.c b/src/i_tcp.c index c86c1146a..5a348dcb6 100644 --- a/src/i_tcp.c +++ b/src/i_tcp.c @@ -898,7 +898,7 @@ static SOCKET_TYPE UDP_Bind(int family, struct sockaddr *addr, socklen_t addrlen inet_pton(AF_INET6, IPV6_MULTICAST_ADDRESS, &maddr.ipv6mr_multiaddr); maddr.ipv6mr_interface = 0; - if (setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP, &maddr, sizeof(maddr)) != 0) + if (setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP, (const char *)&maddr, sizeof(maddr)) != 0) { CONS_Alert(CONS_WARNING, M_GetText("Could not register multicast address\n")); } From 58e5cb079eabd5015685c3e0fecd584e1608d49c Mon Sep 17 00:00:00 2001 From: NepDisk Date: Mon, 24 Mar 2025 22:12:00 -0400 Subject: [PATCH 33/45] Prevent dedi from ending up on titlescreen Thanks Indev! --- src/d_netcmd.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 48ef54b13..a1f2dcc87 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -5844,6 +5844,12 @@ void Command_ExitGame_f(void) { INT32 i; + if (dedicated) + { + CONS_Printf("This command cannot be used on dedicated server\n"); + return; + } + LUA_HookBool(false, HOOK(GameQuit)); D_QuitNetGame(); From 2a0b0d4bae873c1a5c89f92db2983cf49a61db6f Mon Sep 17 00:00:00 2001 From: NepDisk Date: Mon, 24 Mar 2025 22:35:18 -0400 Subject: [PATCH 34/45] Remove ASM --- README.md | 1 - SRB2.cbp | 18 - SRB2_common.props | 3 - Srb2.dev | 52 +- appveyor.yml | 8 - cmake/Modules/CMakeASM_YASMInformation.cmake | 46 - .../CMakeDetermineASM_YASMCompiler.cmake | 27 - cmake/Modules/CMakeTestASM_YASMCompiler.cmake | 23 - debian-template/README.source | 1 - src/Android.mk | 2 +- src/CMakeLists.txt | 35 - src/Makefile | 23 - src/Makefile.d/features.mk | 157 +- src/Makefile.d/nix.mk | 5 - src/Makefile.d/platform.mk | 1 - src/Makefile.d/sdl.mk | 7 - src/Makefile.d/win32.mk | 4 +- src/Sourcefile | 1 - src/d_netcmd.c | 5 - src/m_fixed.h | 1 - src/p5prof.h | 278 --- src/screen.c | 4 - src/sdl/CMakeLists.txt | 17 - src/sdl/MakeCYG.cfg | 1 - src/sdl/Srb2SDL-vc9.vcproj | 4 +- src/sdl/Srb2SDL.dsp | 4 +- src/sdl/i_main.c | 34 - src/sdl12/Srb2SDL-vc10.vcxproj | 4 +- src/sdl12/Srb2SDL-vc9.vcproj | 4 +- src/sdl12/Srb2SDL.dsp | 4 +- src/sdl12/i_main.c | 28 +- src/tmap.nas | 957 ---------- src/tmap.s | 1587 ----------------- src/tmap_asm.s | 322 ---- src/tmap_mmx.nas | 674 ------- src/tmap_vc.nas | 48 - src/v_video.c | 10 - src/win32ce/win_main.c | 11 +- tools/anglechk.c | 364 ++++ 39 files changed, 454 insertions(+), 4321 deletions(-) delete mode 100644 cmake/Modules/CMakeASM_YASMInformation.cmake delete mode 100644 cmake/Modules/CMakeDetermineASM_YASMCompiler.cmake delete mode 100644 cmake/Modules/CMakeTestASM_YASMCompiler.cmake delete mode 100644 src/p5prof.h delete mode 100644 src/tmap.nas delete mode 100644 src/tmap.s delete mode 100644 src/tmap_asm.s delete mode 100644 src/tmap_mmx.nas delete mode 100644 src/tmap_vc.nas create mode 100644 tools/anglechk.c diff --git a/README.md b/README.md index 7fe87ca71..982ba4056 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,6 @@ [SRB2Kart](https://srb2.org/mods/) is a kart racing mod based on the 3D Sonic the Hedgehog fangame [Sonic Robo Blast 2](https://srb2.org/), based on a modified version of [Doom Legacy](http://doomlegacy.sourceforge.net/). ## Dependencies -- NASM (x86 builds only) - SDL2 (Linux/OS X only) - SDL2-Mixer (Linux/OS X only) - libupnp (Linux/OS X only) diff --git a/SRB2.cbp b/SRB2.cbp index acdc61c7f..b4cf543db 100644 --- a/SRB2.cbp +++ b/SRB2.cbp @@ -1996,24 +1996,6 @@ HW3SOUND for 3D hardware sound support