From d66278dfebe57e467472273dd801231ac351ddea Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Thu, 1 May 2025 22:03:57 -0400 Subject: [PATCH 01/32] Fail gracefully on Hamachi/Radmin/etc --- src/d_clisrv.c | 68 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 53 insertions(+), 15 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 2ec42c4de..33e9dceaa 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -123,6 +123,11 @@ static tic_t freezetimeout[MAXNETNODES]; // Until when can this node freeze the // If higher than cv_joindelay * 2 (3 joins in a short timespan), joins are temporarily disabled. static tic_t joindelay = 0; +// Set when the server requests a signature with an IP mismatch. +// This is a common issue on Hamachi, Radmin, and other VPN software that does noncompliant IP remap horeseshit. +// When ths is set, provide only blank signatures. If this is set while joining, the server will degrade us to a GUEST. +static boolean forceGuest = false; + UINT16 pingmeasurecount = 1; UINT32 realpingtable[MAXPLAYERS]; //the base table of ping where an average will be sent to everyone. UINT32 playerpingtable[MAXPLAYERS]; //table of player latency values. @@ -276,9 +281,26 @@ shouldsign_t ShouldSignChallenge(uint8_t *message) if ((max(now, then) - min(now, then)) > 60*15) return SIGN_BADTIME; + // ____ _____ ___ ____ _ + // / ___|_ _/ _ \| _ \| | + // \___ \ | || | | | |_) | | + // ___) || || |_| | __/|_| + // |____/ |_| \___/|_| (_) + // ========================= + // SIGN_BADIP MUST BE CHECKED LAST, AND RETURN ONLY IF ALL OTHER CHECKS HAVE ALREADY SUCCEEDED. + // We allow IP failures through for compatibility with shitty VPNs and fucked-beyond-belief home networks. + // If this is checked before other sign-safety conditons, bad actors can INTENTIONALLY SKIP CHECKS. if (realIP != claimedIP && I_IsExternalAddress(&realIP)) return SIGN_BADIP; - +#ifdef DEVELOP + if (cv_badip.value) + { + CV_AddValue(&cv_badip, -1); + CONS_Alert(CONS_WARNING, "cv_badip enabled, intentionally failing checks\n"); + return SIGN_BADIP; + } +#endif + // Do NOT return SIGN_BADIP before doing all other available checks. return SIGN_OK; } @@ -951,17 +973,19 @@ static boolean CL_SendJoin(void) // Don't leak old signatures from prior sessions. memset(&netbuffer->u.clientcfg.challengeResponse, 0, sizeof(((clientconfig_pak *)0)->challengeResponse)); + forceGuest = false; + if (client && netgame) { shouldsign_t safe = ShouldSignChallenge(awaitingChallenge); - if (safe != SIGN_OK) + if (safe == SIGN_BADIP) { - if (safe == SIGN_BADIP) - { - I_Error("External server IP didn't match the message it sent."); - } - else if (safe == SIGN_BADTIME) + forceGuest = true; + } + else if (safe != SIGN_OK) + { + if (safe == SIGN_BADTIME) { I_Error("External server sent a message with an unusual timestamp.\nMake sure your system time is set correctly."); } @@ -978,7 +1002,7 @@ static boolean CL_SendJoin(void) uint8_t signature[SIGNATURELENGTH]; profile_t *localProfile = PR_GetLocalPlayerProfile(i); - if (PR_IsLocalPlayerGuest(i)) // GUESTS don't have keys + if (PR_IsLocalPlayerGuest(i) || forceGuest) // GUESTS don't have keys { memset(signature, 0, sizeof(signature)); } @@ -1450,6 +1474,9 @@ static void CL_LoadReceivedSavegame(boolean reloading) } } } + + if (forceGuest) + HU_AddChatText("\x85* This server uses a strange network configuration (VPN?). You have joined as a GUEST.", true); } static void CL_ReloadReceivedSavegame(void) @@ -4436,8 +4463,19 @@ static void HandleConnect(SINT8 node) if (netgame && sigcheck != 0) { - SV_SendRefuse(node, M_GetText("Signature verification failed.")); - return; + uint8_t allZero[SIGNATURELENGTH]; + memset(allZero, 0, SIGNATURELENGTH); + if (memcmp(netbuffer->u.clientcfg.challengeResponse[i], allZero, SIGNATURELENGTH) == 0) + { + // The connecting client didn't even try to sign this, probably due to an IP mismatch. + // Let them in as a guest. + memset(lastReceivedKey[node][i], 0, PUBKEYLENGTH); + } + else + { + SV_SendRefuse(node, M_GetText("Signature verification failed.")); + return; + } } } @@ -5864,11 +5902,11 @@ static void HandlePacketFromPlayer(SINT8 node) memcpy(lastChallengeAll, netbuffer->u.challengeall.secret, sizeof(lastChallengeAll)); shouldsign_t safe = ShouldSignChallenge(lastChallengeAll); - if (safe != SIGN_OK) + if (safe == SIGN_BADIP) + forceGuest = true; + else if (safe != SIGN_OK) { - if (safe == SIGN_BADIP) - HandleSigfail("External server sent the wrong IP"); - else if (safe == SIGN_BADTIME) + if (safe == SIGN_BADTIME) HandleSigfail("Bad timestamp - is your time set correctly?"); else HandleSigfail("Unknown auth error - contact a developer"); @@ -5893,7 +5931,7 @@ static void HandlePacketFromPlayer(SINT8 node) { uint8_t signature[SIGNATURELENGTH]; profile_t *localProfile = PR_GetLocalPlayerProfile(challengeplayers); - if (!PR_IsLocalPlayerGuest(challengeplayers)) // GUESTS don't have keys + if (!PR_IsLocalPlayerGuest(challengeplayers) && !forceGuest) // GUESTS don't have keys { crypto_eddsa_sign(signature, localProfile->secret_key, lastChallengeAll, sizeof(lastChallengeAll)); From 90c28dc2d955b7f319d9b2995ff2a58257cccb97 Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Mon, 5 May 2025 20:36:00 -0400 Subject: [PATCH 02/32] Spindash button, Strict Fastfall profile option --- src/cvars.cpp | 8 ++++++++ src/d_netcmd.c | 11 +++++++++-- src/d_player.h | 1 + src/d_ticcmd.h | 7 ++++--- src/deh_tables.c | 1 + src/g_build_ticcmd.cpp | 2 +- src/g_demo.cpp | 8 ++++++++ src/g_game.h | 1 + src/hud/input-display.cpp | 2 +- src/k_kart.c | 4 ++++ src/k_menu.h | 1 + src/k_profiles.cpp | 5 +++++ src/k_profiles.h | 3 +++ src/menus/options-profiles-1.c | 2 ++ src/menus/options-profiles-edit-1.c | 1 + src/menus/options-profiles-edit-accessibility.cpp | 5 ++++- 16 files changed, 54 insertions(+), 8 deletions(-) diff --git a/src/cvars.cpp b/src/cvars.cpp index 173ba956a..2ea673fdc 100644 --- a/src/cvars.cpp +++ b/src/cvars.cpp @@ -974,6 +974,7 @@ consvar_t cv_dummymenuplayer = MenuDummy("dummymenuplayer", "P1").onchange(Dummy consvar_t cv_dummyprofileautoroulette = MenuDummy("dummyprofileautoroulette", "Off").on_off(); consvar_t cv_dummyprofilefov = MenuDummy("dummyprofilefov", "100").min_max(70, 110); consvar_t cv_dummyprofilelitesteer = MenuDummy("dummyprofilelitesteer", "Off").on_off(); +consvar_t cv_dummyprofilestrictfastfall = MenuDummy("dummprofilestrictfastfall", "Off").on_off(); consvar_t cv_dummyprofiledescriptiveinput = Player("dummyprofiledescriptiveinput", "Modern").values(descriptiveinput_cons_t); consvar_t cv_dummyprofileautoring = MenuDummy("dummyprofileautoring", "Off").on_off(); consvar_t cv_dummyprofilekickstart = MenuDummy("dummyprofilekickstart", "Off").on_off(); @@ -1085,6 +1086,13 @@ consvar_t cv_litesteer[MAXSPLITSCREENPLAYERS] = { Player("litesteer4", "Off").on_off().onchange(weaponPrefChange4), }; +consvar_t cv_strictfastfall[MAXSPLITSCREENPLAYERS] = { + Player("strictfastfall", "Off").on_off().onchange(weaponPrefChange), + Player("strictfastfall2", "Off").on_off().onchange(weaponPrefChange2), + Player("strictfastfall3", "Off").on_off().onchange(weaponPrefChange3), + Player("strictfastfall4", "Off").on_off().onchange(weaponPrefChange4), +}; + consvar_t cv_autoring[MAXSPLITSCREENPLAYERS] = { Player("autoring", "Off").on_off().onchange(weaponPrefChange), Player("autoring2", "Off").on_off().onchange(weaponPrefChange2), diff --git a/src/d_netcmd.c b/src/d_netcmd.c index b10215e50..b902cadb3 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1176,7 +1176,8 @@ enum { WP_ANALOGSTICK = 1<<3, WP_AUTORING = 1<<4, WP_SELFMUTE = 1<<5, - WP_SELFDEAFEN = 1<<6 + WP_SELFDEAFEN = 1<<6, + WP_STRICTFASTFALL = 1<<7, }; void WeaponPref_Send(UINT8 ssplayer) @@ -1207,6 +1208,9 @@ void WeaponPref_Send(UINT8 ssplayer) prefs |= WP_SELFDEAFEN; } + if (cv_strictfastfall[ssplayer].value) + prefs |= WP_STRICTFASTFALL; + UINT8 buf[2]; buf[0] = prefs; buf[1] = cv_mindelay.value; @@ -1246,7 +1250,7 @@ size_t WeaponPref_Parse(const UINT8 *bufstart, INT32 playernum) UINT8 prefs = READUINT8(p); player->pflags &= ~(PF_KICKSTARTACCEL|PF_SHRINKME|PF_AUTOROULETTE|PF_AUTORING); - player->pflags2 &= ~(PF2_SELFMUTE | PF2_SELFDEAFEN); + player->pflags2 &= ~(PF2_SELFMUTE | PF2_SELFDEAFEN | PF2_STRICTFASTFALL); if (prefs & WP_KICKSTARTACCEL) player->pflags |= PF_KICKSTARTACCEL; @@ -1271,6 +1275,9 @@ size_t WeaponPref_Parse(const UINT8 *bufstart, INT32 playernum) if (prefs & WP_SELFDEAFEN) player->pflags2 |= PF2_SELFDEAFEN; + if (prefs & WP_STRICTFASTFALL) + player->pflags2 |= PF2_STRICTFASTFALL; + if (leveltime < 2) { // BAD HACK: No other place I tried to slot this in diff --git a/src/d_player.h b/src/d_player.h index 7f286400c..aecf3caaf 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -138,6 +138,7 @@ typedef enum PF2_SELFDEAFEN = 1<<2, PF2_SERVERMUTE = 1<<3, PF2_SERVERDEAFEN = 1<<4, + PF2_STRICTFASTFALL = 1<<5, } pflags2_t; typedef enum diff --git a/src/d_ticcmd.h b/src/d_ticcmd.h index 88ad11c6e..f0c5f4f28 100644 --- a/src/d_ticcmd.h +++ b/src/d_ticcmd.h @@ -34,11 +34,12 @@ typedef enum BT_LOOKBACK = 1<<5, // Look Backward BT_RESPAWN = 1<<6, // Respawn BT_VOTE = 1<<7, // Vote + BT_SPINDASH = 1<<8, // Spindash - BT_EBRAKEMASK = (BT_ACCELERATE|BT_BRAKE), - BT_SPINDASHMASK = (BT_ACCELERATE|BT_BRAKE|BT_DRIFT), + BT_EBRAKEMASK = (BT_ACCELERATE|BT_BRAKE), + BT_SPINDASHMASK = (BT_ACCELERATE|BT_BRAKE|BT_DRIFT), - // free: 1<<8 to 1<<12 + // free: 1<<9 to 1<<12 // Lua garbage, replace with freeslottable buttons some day BT_LUAA = 1<<13, diff --git a/src/deh_tables.c b/src/deh_tables.c index 44195a340..a6c2719cf 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -5005,6 +5005,7 @@ struct int_const_s const INT_CONST[] = { {"BT_LOOKBACK",BT_LOOKBACK}, {"BT_RESPAWN",BT_RESPAWN}, {"BT_VOTE",BT_VOTE}, + {"BT_SPINDASH",BT_SPINDASH}, // Real button now, but triggers the macro same as always. {"BT_EBRAKEMASK",BT_EBRAKEMASK}, // Macro button {"BT_SPINDASHMASK",BT_SPINDASHMASK}, // Macro button {"BT_LUAA",BT_LUAA}, // Lua customizable diff --git a/src/g_build_ticcmd.cpp b/src/g_build_ticcmd.cpp index df036eab8..b8d45e3dc 100644 --- a/src/g_build_ticcmd.cpp +++ b/src/g_build_ticcmd.cpp @@ -400,7 +400,7 @@ class TiccmdBuilder }; map(gc_drift, BT_DRIFT); // drift - map(gc_spindash, BT_SPINDASHMASK); // C + map(gc_spindash, BT_SPINDASH|BT_SPINDASHMASK); // C map(gc_item, BT_ATTACK); // fire map(gc_lookback, BT_LOOKBACK); // rear view diff --git a/src/g_demo.cpp b/src/g_demo.cpp index 1d36dcb7e..88828fee6 100644 --- a/src/g_demo.cpp +++ b/src/g_demo.cpp @@ -207,6 +207,7 @@ boolean G_CompatLevel(UINT16 level) #define DEMO_BOT 0x08 #define DEMO_AUTOROULETTE 0x10 #define DEMO_AUTORING 0x20 +#define DEMO_STRICTFASTFALL 0x40 // For demos #define ZT_FWD 0x0001 @@ -2336,6 +2337,8 @@ void G_BeginRecording(void) i |= DEMO_SPECTATOR; if (player->pflags & PF_KICKSTARTACCEL) i |= DEMO_KICKSTART; + if (player->pflags & PF2_STRICTFASTFALL) + i |= DEMO_STRICTFASTFALL; if (player->pflags & PF_AUTOROULETTE) i |= DEMO_AUTOROULETTE; if (player->pflags & PF_AUTORING) @@ -3453,6 +3456,11 @@ void G_DoPlayDemoEx(const char *defdemoname, lumpnum_t deflumpnum) else players[p].pflags &= ~PF_KICKSTARTACCEL; + if (flags & DEMO_STRICTFASTFALL) + players[p].pflags |= PF2_STRICTFASTFALL; + else + players[p].pflags &= ~PF2_STRICTFASTFALL; + if (flags & DEMO_AUTOROULETTE) players[p].pflags |= PF_AUTOROULETTE; else diff --git a/src/g_game.h b/src/g_game.h index f19f819da..14ee6494a 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -100,6 +100,7 @@ extern consvar_t cv_pauseifunfocused; extern consvar_t cv_kickstartaccel[MAXSPLITSCREENPLAYERS]; extern consvar_t cv_autoroulette[MAXSPLITSCREENPLAYERS]; extern consvar_t cv_litesteer[MAXSPLITSCREENPLAYERS]; +extern consvar_t cv_strictfastfall[MAXSPLITSCREENPLAYERS]; extern consvar_t cv_autoring[MAXSPLITSCREENPLAYERS]; extern consvar_t cv_shrinkme[MAXSPLITSCREENPLAYERS]; diff --git a/src/hud/input-display.cpp b/src/hud/input-display.cpp index fbfd03ee2..c3dab0a53 100644 --- a/src/hud/input-display.cpp +++ b/src/hud/input-display.cpp @@ -111,7 +111,7 @@ void K_DrawInputDisplay(float x, float y, INT32 flags, char mode, UINT8 pid, boo box.patch(gfx("PAD{}", analog ? "N" : dpad_suffix(dpad))); box.patch(but('A', gc_a, BT_ACCELERATE)); box.patch(but('B', gc_b, BT_LOOKBACK)); - box.patch(but('C', gc_c, BT_SPINDASHMASK)); + box.patch(but('C', gc_c, BT_SPINDASH)); box.patch(but('X', gc_x, BT_BRAKE)); box.patch(but('Y', gc_y, BT_RESPAWN)); box.patch(but('Z', gc_z, BT_VOTE)); diff --git a/src/k_kart.c b/src/k_kart.c index 5099b6747..e464b08c6 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -12614,6 +12614,10 @@ static void K_KartSpindash(player_t *player) if (player->pflags & PF_NOFASTFALL) return; + if (player->pflags2 & PF2_STRICTFASTFALL) + if (!(player->cmd.buttons & BT_SPINDASH)) + return; + if (player->fastfall == 0) { // Factors 3D momentum. diff --git a/src/k_menu.h b/src/k_menu.h index bd2973ff1..21cef5fa5 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -1130,6 +1130,7 @@ extern consvar_t cv_dummyprofileplayername; extern consvar_t cv_dummyprofilekickstart; extern consvar_t cv_dummyprofileautoroulette; extern consvar_t cv_dummyprofilelitesteer; +extern consvar_t cv_dummyprofilestrictfastfall; extern consvar_t cv_dummyprofiledescriptiveinput; extern consvar_t cv_dummyprofileautoring; extern consvar_t cv_dummyprofilerumble; diff --git a/src/k_profiles.cpp b/src/k_profiles.cpp index 8ccab2229..06febe5c7 100644 --- a/src/k_profiles.cpp +++ b/src/k_profiles.cpp @@ -85,6 +85,7 @@ profile_t* PR_MakeProfile( newprofile->kickstartaccel = false; newprofile->autoroulette = false; newprofile->litesteer = false; + newprofile->strictfastfall = false; newprofile->descriptiveinput = 1; newprofile->autoring = false; newprofile->rumble = true; @@ -108,6 +109,7 @@ profile_t* PR_MakeProfileFromPlayer(const char *prname, const char *pname, const newprofile->kickstartaccel = cv_kickstartaccel[pnum].value; newprofile->autoroulette = cv_autoroulette[pnum].value; newprofile->litesteer = cv_litesteer[pnum].value; + newprofile->strictfastfall = cv_strictfastfall[pnum].value; newprofile->descriptiveinput = cv_descriptiveinput[pnum].value; newprofile->autoring = cv_autoring[pnum].value; newprofile->rumble = cv_rumble[pnum].value; @@ -305,6 +307,7 @@ void PR_SaveProfiles(void) jsonprof.preferences.kickstartaccel = cprof->kickstartaccel; jsonprof.preferences.autoroulette = cprof->autoroulette; jsonprof.preferences.litesteer = cprof->litesteer; + jsonprof.preferences.strictfastfall = cprof->strictfastfall; jsonprof.preferences.descriptiveinput = cprof->descriptiveinput; jsonprof.preferences.autoring = cprof->autoring; jsonprof.preferences.rumble = cprof->rumble; @@ -493,6 +496,7 @@ void PR_LoadProfiles(void) newprof->kickstartaccel = jsprof.preferences.kickstartaccel; newprof->autoroulette = jsprof.preferences.autoroulette; newprof->litesteer = jsprof.preferences.litesteer; + newprof->strictfastfall = jsprof.preferences.strictfastfall; newprof->descriptiveinput = jsprof.preferences.descriptiveinput; newprof->autoring = jsprof.preferences.autoring; newprof->rumble = jsprof.preferences.rumble; @@ -597,6 +601,7 @@ static void PR_ApplyProfile_Settings(profile_t *p, UINT8 playernum) CV_StealthSetValue(&cv_kickstartaccel[playernum], p->kickstartaccel); CV_StealthSetValue(&cv_autoroulette[playernum], p->autoroulette); CV_StealthSetValue(&cv_litesteer[playernum], p->litesteer); + CV_StealthSetValue(&cv_strictfastfall[playernum], p->strictfastfall); CV_StealthSetValue(&cv_descriptiveinput[playernum], p->descriptiveinput); CV_StealthSetValue(&cv_autoring[playernum], p->autoring); CV_StealthSetValue(&cv_rumble[playernum], p->rumble); diff --git a/src/k_profiles.h b/src/k_profiles.h index 4c71ee074..9572bd973 100644 --- a/src/k_profiles.h +++ b/src/k_profiles.h @@ -46,6 +46,7 @@ struct ProfilePreferencesJson bool kickstartaccel; bool autoroulette; bool litesteer; + bool strictfastfall; uint8_t descriptiveinput; bool autoring; bool rumble; @@ -56,6 +57,7 @@ struct ProfilePreferencesJson kickstartaccel, autoroulette, litesteer, + strictfastfall, descriptiveinput, autoring, rumble, @@ -164,6 +166,7 @@ struct profile_t boolean kickstartaccel; // cv_kickstartaccel boolean autoroulette; // cv_autoroulette boolean litesteer; // cv_litesteer + boolean strictfastfall; // cv_strictfastfall UINT8 descriptiveinput; // cv_descriptiveinput boolean autoring; // cv_autoring boolean rumble; // cv_rumble diff --git a/src/menus/options-profiles-1.c b/src/menus/options-profiles-1.c index 0fc2a0de8..8f61f01cd 100644 --- a/src/menus/options-profiles-1.c +++ b/src/menus/options-profiles-1.c @@ -102,6 +102,7 @@ void M_StartEditProfile(INT32 c) CV_StealthSetValue(&cv_dummyprofilekickstart, optionsmenu.profile->kickstartaccel); CV_StealthSetValue(&cv_dummyprofileautoroulette, optionsmenu.profile->autoroulette); CV_StealthSetValue(&cv_dummyprofilelitesteer, optionsmenu.profile->litesteer); + CV_StealthSetValue(&cv_dummyprofilestrictfastfall, optionsmenu.profile->strictfastfall); CV_StealthSetValue(&cv_dummyprofiledescriptiveinput, optionsmenu.profile->descriptiveinput); CV_StealthSetValue(&cv_dummyprofileautoring, optionsmenu.profile->autoring); CV_StealthSetValue(&cv_dummyprofilerumble, optionsmenu.profile->rumble); @@ -114,6 +115,7 @@ void M_StartEditProfile(INT32 c) CV_StealthSetValue(&cv_dummyprofilekickstart, 0); // off CV_StealthSetValue(&cv_dummyprofileautoroulette, 0); // off CV_StealthSetValue(&cv_dummyprofilelitesteer, 1); // on + CV_StealthSetValue(&cv_dummyprofilestrictfastfall, 0); // off CV_StealthSetValue(&cv_dummyprofiledescriptiveinput, 1); // Modern CV_StealthSetValue(&cv_dummyprofileautoring, 0); // on CV_StealthSetValue(&cv_dummyprofilerumble, 1); // on diff --git a/src/menus/options-profiles-edit-1.c b/src/menus/options-profiles-edit-1.c index f432c24ef..fa5eb661c 100644 --- a/src/menus/options-profiles-edit-1.c +++ b/src/menus/options-profiles-edit-1.c @@ -98,6 +98,7 @@ static void M_ProfileEditApply(void) optionsmenu.profile->kickstartaccel = cv_dummyprofilekickstart.value; optionsmenu.profile->autoroulette = cv_dummyprofileautoroulette.value; optionsmenu.profile->litesteer = cv_dummyprofilelitesteer.value; + optionsmenu.profile->strictfastfall = cv_dummyprofilestrictfastfall.value; optionsmenu.profile->descriptiveinput = cv_dummyprofiledescriptiveinput.value; optionsmenu.profile->autoring = cv_dummyprofileautoring.value; optionsmenu.profile->rumble = cv_dummyprofilerumble.value; diff --git a/src/menus/options-profiles-edit-accessibility.cpp b/src/menus/options-profiles-edit-accessibility.cpp index bbb09f97a..d347b3ca4 100644 --- a/src/menus/options-profiles-edit-accessibility.cpp +++ b/src/menus/options-profiles-edit-accessibility.cpp @@ -114,6 +114,9 @@ menuitem_t OPTIONS_ProfileAccessibility[] = { {IT_STRING | IT_CVAR, "Lite Steer", "Hold DOWN on d-pad/keyboard for shallow turns.", NULL, {.cvar = &cv_dummyprofilelitesteer}, 0, 0}, + {IT_STRING | IT_CVAR, "Strict Fastfall", "Fastfall only with the Spindash button.", + NULL, {.cvar = &cv_dummyprofilestrictfastfall}, 0, 0}, + {IT_STRING | IT_CVAR, "Field of View", "Higher FOV lets you see more.", NULL, {.cvar = &cv_dummyprofilefov}, 0, 0}, @@ -144,7 +147,7 @@ menu_t OPTIONS_ProfileAccessibilityDef = { &OPTIONS_EditProfileDef, 0, OPTIONS_ProfileAccessibility, - 145, 41, + 145, 31, SKINCOLOR_ULTRAMARINE, 0, MBF_DRAWBGWHILEPLAYING, "FILE", From 654f25f6843476561772c95c5dcd50d444ab6d9c Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Sun, 11 May 2025 17:58:44 -0700 Subject: [PATCH 03/32] Strict Fastfall: review fixups --- src/d_netcmd.c | 9 +++++++++ src/g_game.c | 2 +- src/k_kart.c | 8 ++++---- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index b902cadb3..054504912 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1178,6 +1178,12 @@ enum { WP_SELFMUTE = 1<<5, WP_SELFDEAFEN = 1<<6, WP_STRICTFASTFALL = 1<<7, + // WARNING: STUPID LEGACY TIMEWASTER AHEAD + // IF YOU ARE ADDING OR MODIFYING WEAPONPREFS, YOU MUST + // PRESERVE THEM IN G_PlayerReborn -- OTHERWISE THEY + // WILL MYSTERIOUSLY VANISH AFTER ONE RACE + // + // HOURS LOST TO G_PlayerReborn: UNCOUNTABLE }; void WeaponPref_Send(UINT8 ssplayer) @@ -1239,6 +1245,9 @@ void WeaponPref_Save(UINT8 **cp, INT32 playernum) if (player->pflags & PF_AUTORING) prefs |= WP_AUTORING; + if (player->pflags & PF2_STRICTFASTFALL) + prefs |= WP_STRICTFASTFALL; + WRITEUINT8(*cp, prefs); } diff --git a/src/g_game.c b/src/g_game.c index 392d6f72b..90261ce90 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2349,7 +2349,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) xtralife = players[player].xtralife; pflags = (players[player].pflags & (PF_WANTSTOJOIN|PF_KICKSTARTACCEL|PF_SHRINKME|PF_SHRINKACTIVE|PF_AUTOROULETTE|PF_ANALOGSTICK|PF_AUTORING)); - pflags2 = (players[player].pflags2 & (PF2_SELFMUTE | PF2_SELFDEAFEN | PF2_SERVERMUTE | PF2_SERVERDEAFEN)); + pflags2 = (players[player].pflags2 & (PF2_SELFMUTE | PF2_SELFDEAFEN | PF2_SERVERMUTE | PF2_SERVERDEAFEN | PF2_STRICTFASTFALL)); // SRB2kart memcpy(&itemRoulette, &players[player].itemRoulette, sizeof (itemRoulette)); diff --git a/src/k_kart.c b/src/k_kart.c index e464b08c6..d9cda547c 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -12614,12 +12614,12 @@ static void K_KartSpindash(player_t *player) if (player->pflags & PF_NOFASTFALL) return; - if (player->pflags2 & PF2_STRICTFASTFALL) - if (!(player->cmd.buttons & BT_SPINDASH)) - return; - if (player->fastfall == 0) { + if (player->pflags2 & PF2_STRICTFASTFALL) + if (!(player->cmd.buttons & BT_SPINDASH)) + return; + // Factors 3D momentum. player->fastfallBase = FixedHypot(player->speed, player->mo->momz); } From 4bc768eb43be0e1053a5a14c116fc8c5df9b2804 Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Mon, 12 May 2025 13:43:16 -0400 Subject: [PATCH 04/32] "devexec" cheat --- src/f_finale.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/f_finale.c b/src/f_finale.c index a53d5414a..fcb5821a8 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -931,6 +931,12 @@ void F_IntroTicker(void) return; } + if (skiptype == 6) // Dev Exec + { + ResetSkipSequences(); + COM_ImmedExecute("exec devexec.cfg"); + } + if (doskip && disclaimerskippable) { if (dc_state == DISCLAIMER_FINAL) { @@ -1021,19 +1027,21 @@ static void AdvanceSkipSequences(UINT8 input) UINT8 s2cheat[] = {1, 1, 1}; UINT8 s3cheat[] = {2, 2, 2}; UINT8 s3kcheat[] = {3, 3, 3}; - UINT8 spincheat[] = {4, 4, 4}; + UINT8 spincheat[] = {1, 3, 1}; + UINT8 devcheat[] = {4, 4, 4}; #else UINT8 s2cheat[] = {1, 1, 1, 3, 3, 3, 1}; UINT8 s3cheat[] = {1, 1, 3, 3, 1, 1, 1, 1}; UINT8 s3kcheat[] = {4, 4, 4, 2, 2, 2, 1, 1, 1}; UINT8 spincheat[] = {1, 2, 3, 4, 3, 2, 1}; + UINT8 devcheat[] = {4, 4, 4}; #endif UINT8 nicetry[] = {1, 1, 3, 3, 4, 2, 4, 2}; - #define NUMCHEATSPLUSONE 5 + #define NUMCHEATSPLUSONE 6 - UINT8 *cheats[NUMCHEATSPLUSONE] = {s2cheat, s3cheat, s3kcheat, nicetry, spincheat}; - UINT8 cheatlengths[NUMCHEATSPLUSONE] = {sizeof(s2cheat), sizeof(s3cheat), sizeof(s3kcheat), sizeof(nicetry), sizeof(spincheat)}; + UINT8 *cheats[NUMCHEATSPLUSONE] = {s2cheat, s3cheat, s3kcheat, nicetry, spincheat, devcheat}; + UINT8 cheatlengths[NUMCHEATSPLUSONE] = {sizeof(s2cheat), sizeof(s3cheat), sizeof(s3kcheat), sizeof(nicetry), sizeof(spincheat), sizeof(devcheat)}; for (UINT8 i = 0; i < NUMCHEATSPLUSONE; i++) // for each cheat... { From 9ef0109eb8a580b0718d58ae1446496add56a5f2 Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Mon, 12 May 2025 20:30:11 -0400 Subject: [PATCH 05/32] Rework Last Chance --- src/p_map.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/p_map.c b/src/p_map.c index c4e831118..1289dc71f 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -35,6 +35,7 @@ #include "k_terrain.h" #include "k_objects.h" #include "k_boss.h" +#include "k_hitlag.h" // K_AddHitlag #include "r_splats.h" @@ -4120,10 +4121,24 @@ static void P_BouncePlayerMove(mobj_t *mo, TryMoveResult_t *result) // Combo avoidance! if (mo->player && P_PlayerInPain(mo->player) && gametyperules & GTR_BUMPERS && mo->health == 1) { - K_StumblePlayer(mo->player); - K_BumperInflate(mo->player); - mo->player->tumbleBounces = TUMBLEBOUNCES; - mo->hitlag = max(mo->hitlag, 6); + P_ResetPlayer(mo->player); + mo->player->spinouttimer = 0; + mo->player->wipeoutslow = 0; + mo->player->tumbleBounces = 0; + + K_AddHitLag(mo, 3, false); + + // "I dunno man, just fuckin' do it" - jart + S_StartSound(mo, sfx_mbs45); + S_StartSound(mo, sfx_mbs45); + S_StartSound(mo, sfx_mbs45); + S_StartSound(mo, sfx_mbs45); + S_StartSound(mo, sfx_mbv84); + + if (mo->eflags & MFE_VERTICALFLIP) + mo->momz -= 40*mo->scale; + else + mo->momz += 40*mo->scale; } mo->momx = tmxmove; From 22817ac60b0291e07de472717dafc68866d4425b Mon Sep 17 00:00:00 2001 From: Lach Date: Wed, 14 May 2025 01:39:42 +1000 Subject: [PATCH 06/32] Add stun mechanic & Flybot767 --- src/d_player.h | 2 + src/deh_tables.c | 5 ++ src/info.c | 32 +++++++++ src/info.h | 8 +++ src/k_kart.c | 27 +++++++ src/k_objects.h | 5 ++ src/lua_playerlib.c | 8 +++ src/objects/CMakeLists.txt | 1 + src/objects/flybot767.c | 142 +++++++++++++++++++++++++++++++++++++ src/p_inter.c | 48 +++++++++---- src/p_local.h | 6 ++ src/p_mobj.c | 6 ++ src/p_saveg.cpp | 4 ++ 13 files changed, 281 insertions(+), 13 deletions(-) create mode 100644 src/objects/flybot767.c diff --git a/src/d_player.h b/src/d_player.h index 64547b348..0c2e0e19b 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -724,6 +724,8 @@ struct player_t UINT8 noEbrakeMagnet; // Briefly disable 2.2 responsive ebrake if you're bumped by another player. UINT8 tumbleBounces; UINT16 tumbleHeight; // In *mobjscaled* fracunits, or mfu, not raw fu + UINT16 stunned; // Number of tics during which rings cannot be picked up + UINT8 stunnedCombo; // Number of hits sustained while stunned, reduces consecutive stun penalties UINT8 justDI; // Turn-lockout timer to briefly prevent unintended turning after DI, resets when actionable or no input boolean flipDI; // Bananas flip the DI direction. Was a bug, but it made bananas much more interesting. diff --git a/src/deh_tables.c b/src/deh_tables.c index 44195a340..773b47b90 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -3081,6 +3081,9 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi "S_BADNIK_EXPLOSION_SHOCKWAVE2", "S_BADNIK_EXPLOSION1", "S_BADNIK_EXPLOSION2", + + // Flybot767 (stun) + "S_FLYBOT767", }; // RegEx to generate this from info.h: ^\tMT_([^,]+), --> \t"MT_\1", @@ -3973,6 +3976,8 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t "MT_PULLUPHOOK", "MT_AMPS", + + "MT_FLYBOT767", }; const char *const MOBJFLAG_LIST[] = { diff --git a/src/info.c b/src/info.c index 902575c82..faed52546 100644 --- a/src/info.c +++ b/src/info.c @@ -771,6 +771,9 @@ char sprnames[NUMSPRITES + 1][5] = "DIEM", // smoke "DIEN", // explosion + // Flybot767 (stun) + "STUN", + // Pulley "HCCH", "HCHK", @@ -3632,6 +3635,9 @@ state_t states[NUMSTATES] = {SPR_NULL, 0, 1, {A_PlaySound}, sfx_s3k3d, 1, S_BATTLEBUMPER_EXBLAST1}, // S_BADNIK_EXPLOSION_SHOCKWAVE2 {SPR_NULL, 0, 1, {NULL}, 0, 0, S_BADNIK_EXPLOSION2}, // S_BADNIK_EXPLOSION1 {SPR_WIPD, FF_FULLBRIGHT|FF_RANDOMANIM|FF_ANIMATE, 30, {NULL}, 9, 3, S_NULL}, // S_BADNIK_EXPLOSION2 + + // Flybot767 (stun) + {SPR_STUN, FF_FULLBRIGHT|FF_ANIMATE, -1, {NULL}, 4, 4, S_NULL}, // S_FLYBOT767 }; mobjinfo_t mobjinfo[NUMMOBJTYPES] = @@ -22320,6 +22326,32 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_NOCLIP|MF_NOCLIPTHING, // flags S_NULL // raisestate }, + { // MT_FLYBOT767 + -1, // doomednum + S_FLYBOT767, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 0, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_pop, // deathsound + 4*FRACUNIT, // speed + 32*FRACUNIT, // radius + 15*FRACUNIT, // height + 0, // dispoffset + 0, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_NOCLIP|MF_NOCLIPTHING, // flags + S_NULL // raisestate + }, }; diff --git a/src/info.h b/src/info.h index 0345073b7..a525bfd50 100644 --- a/src/info.h +++ b/src/info.h @@ -1306,6 +1306,9 @@ typedef enum sprite SPR_DIEM, // smoke SPR_DIEN, // explosion + // Flybot767 (stun) + SPR_STUN, + // Pulley SPR_HCCH, SPR_HCHK, @@ -4117,6 +4120,9 @@ typedef enum state S_BADNIK_EXPLOSION1, S_BADNIK_EXPLOSION2, + // Flybot767 (stun) + S_FLYBOT767, + S_FIRSTFREESLOT, S_LASTFREESLOT = S_FIRSTFREESLOT + NUMSTATEFREESLOTS - 1, NUMSTATES @@ -5032,6 +5038,8 @@ typedef enum mobj_type MT_AMPS, + MT_FLYBOT767, + MT_FIRSTFREESLOT, MT_LASTFREESLOT = MT_FIRSTFREESLOT + NUMMOBJFREESLOTS - 1, NUMMOBJTYPES diff --git a/src/k_kart.c b/src/k_kart.c index 5173fa3eb..0716ab7e5 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -9377,6 +9377,33 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) if (player->ringdelay) player->ringdelay--; + if ((player->stunned > 0) + && (player->respawn.state == RESPAWNST_NONE) + && !P_PlayerInPain(player) + && P_IsObjectOnGround(player->mo) + ) + { + // MEGA FUCKING HACK BECAUSE P_SAVEG MOBJS ARE FULL + // Would updating player_saveflags to 32 bits have any negative consequences? + // For now, player->stunned 16th bit is a flag to determine whether the flybots were spawned + + // timer counts down at triple speed while spindashing + player->stunned = (player->stunned & 0x8000) | max(0, (player->stunned & 0x7FFF) - (player->spindash ? 3 : 1)); + + // when timer reaches 0, reset the flag and stun combo counter + if ((player->stunned & 0x7FFF) == 0) + { + player->stunned = 0; + player->stunnedCombo = 0; + } + // otherwise if the flybots aren't spawned, spawn them now! + else if ((player->stunned & 0x8000) == 0) + { + player->stunned |= 0x8000; + Obj_SpawnFlybotsForPlayer(player); + } + } + if (player->trickpanel == TRICKSTATE_READY) { if (!player->throwdir && !cmd->turning) diff --git a/src/k_objects.h b/src/k_objects.h index c661e47d2..0d75c222a 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -435,6 +435,11 @@ boolean Obj_DestroyKart(mobj_t *kart); void Obj_DestroyedKartParticleThink(mobj_t *part); void Obj_DestroyedKartParticleLanding(mobj_t *part); +/* Flybot767 (stun) */ +void Obj_SpawnFlybotsForPlayer(player_t *player); +void Obj_FlybotThink(mobj_t *flybot); +void Obj_FlybotDeath(mobj_t *flybot); + /* Pulley */ void Obj_PulleyThink(mobj_t *root); void Obj_PulleyHookTouch(mobj_t *special, mobj_t *toucher); diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 62eca4611..f4a53be32 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -260,6 +260,10 @@ static int player_get(lua_State *L) lua_pushinteger(L, plr->tumbleBounces); else if (fastcmp(field,"tumbleheight")) lua_pushinteger(L, plr->tumbleHeight); + else if (fastcmp(field,"stunned")) + lua_pushinteger(L, plr->stunned); + else if (fastcmp(field,"stunnedcombo")) + lua_pushinteger(L, plr->stunnedCombo); else if (fastcmp(field,"justdi")) lua_pushinteger(L, plr->justDI); else if (fastcmp(field,"flipdi")) @@ -872,6 +876,10 @@ static int player_set(lua_State *L) plr->tumbleBounces = luaL_checkinteger(L, 3); else if (fastcmp(field,"tumbleheight")) plr->tumbleHeight = luaL_checkinteger(L, 3); + else if (fastcmp(field,"stunned")) + plr->stunned = luaL_checkinteger(L, 3); + else if (fastcmp(field,"stunnedcombo")) + plr->stunnedCombo = luaL_checkinteger(L, 3); else if (fastcmp(field,"justdi")) plr->justDI = luaL_checkinteger(L, 3); else if (fastcmp(field,"flipdi")) diff --git a/src/objects/CMakeLists.txt b/src/objects/CMakeLists.txt index 593d5b8f6..35ee3ad6c 100644 --- a/src/objects/CMakeLists.txt +++ b/src/objects/CMakeLists.txt @@ -60,6 +60,7 @@ target_sources(SRB2SDL2 PRIVATE pulley.cpp amps.c ballhog.cpp + flybot767.c ) add_subdirectory(versus) diff --git a/src/objects/flybot767.c b/src/objects/flybot767.c new file mode 100644 index 000000000..790276387 --- /dev/null +++ b/src/objects/flybot767.c @@ -0,0 +1,142 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2025 by Lachlan "Lach" Wright +// 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. +//----------------------------------------------------------------------------- +/// \file flybot767.c +/// \brief Flybot767 object code. + +#include "../p_local.h" +#include "../k_kart.h" +#include "../k_objects.h" +#include "../s_sound.h" +#include "../m_easing.h" + +#define FLYBOT_QUANTITY 2 +#define FLYBOT_VERTICAL_OFFSET (16 * FRACUNIT) +#define FLYBOT_BOB_AMPLITUDE (16 * FRACUNIT) +#define FLYBOT_BOB_FREQUENCY (ANG15) +#define FLYBOT_FADE_STARTTIME (2 * TICRATE) +#define FLYBOT_SCALE (17 * FRACUNIT / 20) +static const fixed_t PI = 355 * FRACUNIT / 113; + +static fixed_t SetFlybotZ(mobj_t *flybot) +{ + flybot->z = FixedMul(mapobjectscale, FLYBOT_VERTICAL_OFFSET) + FixedMul(mapobjectscale, P_ReturnThrustX(NULL, flybot->movedir, FLYBOT_BOB_AMPLITUDE)); + if (flybot->eflags & MFE_VERTICALFLIP) + { + flybot->z = -flybot->z - flybot->height; + } + else + { + flybot->z += flybot->target->height; + } + flybot->z += flybot->target->z; + return flybot->z; +} + +void Obj_SpawnFlybotsForPlayer(player_t *player) +{ + UINT8 i; + mobj_t *mo = player->mo; + fixed_t radius = mo->radius; + + for (i = 0; i < FLYBOT_QUANTITY; i++) + { + angle_t angle = mo->angle + ANGLE_90 + FixedAngle(i * 360 * FRACUNIT / FLYBOT_QUANTITY); + mobj_t *flybot = P_SpawnMobj( + mo->x + P_ReturnThrustX(NULL, angle, radius), + mo->y + P_ReturnThrustY(NULL, angle, radius), + mo->z, + MT_FLYBOT767 + ); + + P_InstaScale(flybot, flybot->old_scale = FixedMul(mapobjectscale, FLYBOT_SCALE)); + P_SetTarget(&flybot->target, mo); + flybot->eflags |= mo->eflags & MFE_VERTICALFLIP; + flybot->movedir = flybot->old_angle = flybot->angle = angle + ANGLE_90; + flybot->old_z = SetFlybotZ(flybot); + flybot->renderflags |= (i * RF_DONTDRAW); + } +} + +void Obj_FlybotThink(mobj_t *flybot) +{ + UINT16 stunned = UINT16_MAX; + angle_t deltaAngle, angle; + fixed_t radius, circumference; + fixed_t speed = FixedMul(mapobjectscale, flybot->info->speed); + mobj_t *mo = flybot->target; + + if (P_MobjWasRemoved(mo)) + { + P_KillMobj(flybot, NULL, NULL, 0); + return; + } + + if (mo->player) + { + if (((stunned = mo->player->stunned & 0x7FFF) == 0) || (mo->player->playerstate == PST_DEAD)) + { + P_KillMobj(flybot, NULL, NULL, 0); + return; + } + } + + flybot->frame = flybot->frame & ~FF_TRANSMASK; + if (stunned < FLYBOT_FADE_STARTTIME) + { + flybot->frame |= Easing_InCubic(FixedDiv(stunned, FLYBOT_FADE_STARTTIME), 7, 1) << FF_TRANSSHIFT; + } + + flybot->eflags = (flybot->eflags & ~MFE_VERTICALFLIP) | (mo->eflags & MFE_VERTICALFLIP); + flybot->movedir += FLYBOT_BOB_FREQUENCY; + flybot->renderflags ^= RF_DONTDRAW; + + radius = mo->radius; + circumference = 2 * FixedMul(PI, radius); + deltaAngle = FixedAngle(FixedMul(FixedDiv(speed, circumference), 360 * FRACUNIT)); + flybot->angle += deltaAngle; + angle = flybot->angle - ANGLE_90; + + P_MoveOrigin(flybot, + mo->x + P_ReturnThrustX(NULL, angle, radius), + mo->y + P_ReturnThrustY(NULL, angle, radius), + SetFlybotZ(flybot) + ); +} + +void Obj_FlybotDeath(mobj_t *flybot) +{ + UINT8 i; + angle_t angle = 0; + fixed_t hThrust = 4*mapobjectscale, vThrust = 4*mapobjectscale; + vector3_t mom = {0, 0, 0}; + mobj_t *mo = flybot->target; + + if (!P_MobjWasRemoved(mo)) + { + mom.x = mo->momx; + mom.y = mo->momy; + mom.z = mo->momz; + //S_StartSound(mo, flybot->info->deathsound); + } + + for (i = 0; i < 4; i++) + { + mo = P_SpawnMobjFromMobj(flybot, 0, 0, 0, MT_PARTICLE); + P_SetMobjState(mo, S_SPINDASHDUST); + mo->flags |= MF_NOSQUISH; + mo->renderflags |= RF_FULLBRIGHT; + mo->momx = mom.x; + mo->momy = mom.y; + mo->momz = mom.z + vThrust; + P_Thrust(mo, angle, hThrust); + vThrust *= -1; + angle += ANGLE_90; + } +} diff --git a/src/p_inter.c b/src/p_inter.c index b3d178254..02fe7393e 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -125,13 +125,20 @@ boolean P_CanPickupItem(player_t *player, UINT8 weapon) // 2: Eggbox // 3: Paperitem - if (weapon != 2 && player->instaWhipCharge) + if (weapon != PICKUP_EGGBOX && player->instaWhipCharge) return false; - if (weapon) + if (weapon == PICKUP_RINGORSPHERE) + { + if (player->stunned > 0) + { + return false; + } + } + else { // Item slot already taken up - if (weapon == 2) + if (weapon == PICKUP_EGGBOX) { // Invulnerable if (player->flashing > 0) @@ -153,11 +160,11 @@ boolean P_CanPickupItem(player_t *player, UINT8 weapon) // Item slot already taken up if (player->itemRoulette.active == true || player->ringboxdelay > 0 - || (weapon != 3 && player->itemamount) + || (weapon != PICKUP_PAPERITEM && player->itemamount) || (player->itemflags & IF_ITEMOUT)) return false; - if (weapon == 3 && K_GetShieldFromItem(player->itemtype) != KSHIELD_NONE) + if (weapon == PICKUP_PAPERITEM && K_GetShieldFromItem(player->itemtype) != KSHIELD_NONE) return false; // No stacking shields! } } @@ -411,7 +418,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (special->scale < special->destscale/2) return; - if (!P_CanPickupItem(player, 3) || (player->itemamount && player->itemtype != special->threshold)) + if (!P_CanPickupItem(player, PICKUP_PAPERITEM) || (player->itemamount && player->itemtype != special->threshold)) return; player->itemtype = special->threshold; @@ -433,7 +440,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) case MT_RANDOMITEM: { UINT8 cheesetype = (special->flags2 & MF2_BOSSDEAD) ? 2 : 1; // perma ring box - if (!P_CanPickupItem(player, 1)) + if (!P_CanPickupItem(player, PICKUP_ITEMBOX)) return; if (P_IsPickupCheesy(player, cheesetype)) return; @@ -473,7 +480,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) return; } case MT_SPHEREBOX: - if (!P_CanPickupItem(player, 0)) + if (!P_CanPickupItem(player, PICKUP_RINGORSPHERE)) return; special->momx = special->momy = special->momz = 0; @@ -499,7 +506,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) return; break; default: - if (!P_CanPickupItem(player, 1)) + if (!P_CanPickupItem(player, PICKUP_ITEMBOX)) return; if (P_IsPickupCheesy(player, 3)) return; @@ -558,7 +565,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) return; } case MT_EMERALD: - if (!P_CanPickupItem(player, 0) || P_PlayerInPain(player)) + if (!P_CanPickupItem(player, PICKUP_RINGORSPHERE) || P_PlayerInPain(player)) return; if (special->threshold > 0) @@ -617,7 +624,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) return; case MT_CDUFO: // SRB2kart - if (special->fuse || !P_CanPickupItem(player, 1)) + if (special->fuse || !P_CanPickupItem(player, PICKUP_ITEMBOX)) return; K_StartItemRoulette(player, false); @@ -693,7 +700,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (special->threshold > 0 || P_PlayerInPain(player) || player->spindash) // player->spindash: Otherwise, players can pick up rings that are thrown out of them from invinc spindash penalty return; - if (!(P_CanPickupItem(player, 0))) + if (!(P_CanPickupItem(player, PICKUP_RINGORSPHERE))) return; // Reached the cap, don't waste 'em! @@ -715,7 +722,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) return; case MT_BLUESPHERE: - if (!(P_CanPickupItem(player, 0))) + if (!(P_CanPickupItem(player, PICKUP_RINGORSPHERE))) return; P_GivePlayerSpheres(player, 1); @@ -2301,6 +2308,9 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget case MT_EMFAUCET_DRIP: Obj_EMZDripDeath(target); break; + case MT_FLYBOT767: + Obj_FlybotDeath(target); + break; default: break; } @@ -3001,6 +3011,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da UINT8 type = (damagetype & DMG_TYPEMASK); const boolean hardhit = (type == DMG_EXPLODE || type == DMG_KARMA || type == DMG_TUMBLE); // This damage type can do evil stuff like ALWAYS combo INT16 ringburst = 5; + UINT16 stunTics = 0; // Check if the player is allowed to be damaged! // If not, then spawn the instashield effect instead. @@ -3391,6 +3402,17 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da player->flipDI = true; } + // I'm wondering if weight 9 should have it for 70 tics, while weight 1 would have it for like 280 (basically x4) + // It may be worth designing it LIKE a value that could be changed for the future though, we may want different things to give different multipliers of stun later imo + stunTics = 2*TICRATE + (6*TICRATE * (9 - player->kartweight) / 8); + stunTics >>= player->stunnedCombo; // consecutive hits add half as much stun as the previous hit + + if (player->stunnedCombo < UINT8_MAX) + { + player->stunnedCombo++; + } + player->stunned = (player->stunned & 0x8000) | min(0x7FFF, (player->stunned & 0x7FFF) + stunTics); + K_DefensiveOverdrive(target->player); } } diff --git a/src/p_local.h b/src/p_local.h index ee269807f..00e86b453 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -557,6 +557,12 @@ void P_CheckTimeLimit(void); void P_CheckPointLimit(void); boolean P_CheckRacers(void); +// Pickup types +#define PICKUP_RINGORSPHERE 0 +#define PICKUP_ITEMBOX 1 +#define PICKUP_EGGBOX 2 +#define PICKUP_PAPERITEM 3 + boolean P_CanPickupItem(player_t *player, UINT8 weapon); boolean P_IsPickupCheesy(player_t *player, UINT8 type); void P_UpdateLastPickup(player_t *player, UINT8 type); diff --git a/src/p_mobj.c b/src/p_mobj.c index ee7a8f5e0..f1f84c444 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -10070,6 +10070,12 @@ static boolean P_MobjRegularThink(mobj_t *mobj) break; } + case MT_FLYBOT767: + { + Obj_FlybotThink(mobj); + break; + } + default: // check mobj against possible water content, before movement code P_MobjCheckWater(mobj); diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index 9f11fbb70..0b03a974f 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -455,6 +455,8 @@ static void P_NetArchivePlayers(savebuffer_t *save) WRITEUINT8(save->p, players[i].noEbrakeMagnet); WRITEUINT8(save->p, players[i].tumbleBounces); WRITEUINT16(save->p, players[i].tumbleHeight); + WRITEUINT16(save->p, players[i].stunned); + WRITEUINT8(save->p, players[i].stunnedCombo); WRITEUINT8(save->p, players[i].justDI); WRITEUINT8(save->p, players[i].flipDI); @@ -1093,6 +1095,8 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) players[i].noEbrakeMagnet = READUINT8(save->p); players[i].tumbleBounces = READUINT8(save->p); players[i].tumbleHeight = READUINT16(save->p); + players[i].stunned = READUINT16(save->p); + players[i].stunnedCombo = READUINT8(save->p); players[i].justDI = READUINT8(save->p); players[i].flipDI = (boolean)READUINT8(save->p); From ca06b2a3347e2bfc70503b46ea4ddadb5fcacf0a Mon Sep 17 00:00:00 2001 From: Lach Date: Wed, 14 May 2025 02:04:40 +1000 Subject: [PATCH 07/32] Replace further P_CanPickupItem constants and allow them to be parsed in Lua --- src/deh_tables.c | 5 +++++ src/k_botsearch.cpp | 8 ++++---- src/k_collide.cpp | 2 +- src/k_kart.c | 2 +- src/objects/hyudoro.c | 2 +- src/objects/random-item.c | 2 +- 6 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/deh_tables.c b/src/deh_tables.c index 773b47b90..715d78394 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -5251,6 +5251,11 @@ struct int_const_s const INT_CONST[] = { {"TN_CHANGEPITCH",TN_CHANGEPITCH}, {"TN_LOOPING",TN_LOOPING}, + {"PICKUP_RINGORSPHERE", PICKUP_RINGORSPHERE}, + {"PICKUP_ITEMBOX", PICKUP_ITEMBOX}, + {"PICKUP_EGGBOX", PICKUP_EGGBOX}, + {"PICKUP_PAPERITEM", PICKUP_PAPERITEM}, + {NULL,0} }; diff --git a/src/k_botsearch.cpp b/src/k_botsearch.cpp index 666e2adcf..1bb53c051 100644 --- a/src/k_botsearch.cpp +++ b/src/k_botsearch.cpp @@ -497,7 +497,7 @@ static BlockItReturn_t K_FindObjectsForNudging(mobj_t *thing) break; } - if (P_CanPickupItem(g_nudgeSearch.botmo->player, 1)) + if (P_CanPickupItem(g_nudgeSearch.botmo->player, PICKUP_ITEMBOX)) { K_AddAttackObject(thing, side, ((thing->extravalue1 < RINGBOX_TIME) ? 10 : 20)); } @@ -508,7 +508,7 @@ static BlockItReturn_t K_FindObjectsForNudging(mobj_t *thing) break; } - if (P_CanPickupItem(g_nudgeSearch.botmo->player, 1)) // Can pick up an actual item + if (P_CanPickupItem(g_nudgeSearch.botmo->player, PICKUP_ITEMBOX)) // Can pick up an actual item { const UINT8 stealth = K_EggboxStealth(thing->x, thing->y); const UINT8 requiredstealth = (g_nudgeSearch.botmo->player->botvars.difficulty * g_nudgeSearch.botmo->player->botvars.difficulty); @@ -529,7 +529,7 @@ static BlockItReturn_t K_FindObjectsForNudging(mobj_t *thing) break; } - if (P_CanPickupItem(g_nudgeSearch.botmo->player, 3)) + if (P_CanPickupItem(g_nudgeSearch.botmo->player, PICKUP_PAPERITEM)) { K_AddAttackObject(thing, side, 20); } @@ -542,7 +542,7 @@ static BlockItReturn_t K_FindObjectsForNudging(mobj_t *thing) } if ((RINGTOTAL(g_nudgeSearch.botmo->player) < 20 && !(g_nudgeSearch.botmo->player->pflags & PF_RINGLOCK) - && P_CanPickupItem(g_nudgeSearch.botmo->player, 0)) + && P_CanPickupItem(g_nudgeSearch.botmo->player, PICKUP_RINGORSPHERE)) && !thing->extravalue1 && (g_nudgeSearch.botmo->player->itemtype != KITEM_LIGHTNINGSHIELD)) { diff --git a/src/k_collide.cpp b/src/k_collide.cpp index 3e048234e..caa445edf 100644 --- a/src/k_collide.cpp +++ b/src/k_collide.cpp @@ -172,7 +172,7 @@ boolean K_EggItemCollide(mobj_t *t1, mobj_t *t2) if (t1->health <= 0 || t2->health <= 0) return true; - if (!P_CanPickupItem(t2->player, 2)) + if (!P_CanPickupItem(t2->player, PICKUP_EGGBOX)) return true; K_DropItems(t2->player); diff --git a/src/k_kart.c b/src/k_kart.c index 0716ab7e5..ef3e2e6fa 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -10066,7 +10066,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) } extern consvar_t cv_fuzz; - if (cv_fuzz.value && P_CanPickupItem(player, 1)) + if (cv_fuzz.value && P_CanPickupItem(player, PICKUP_ITEMBOX)) { K_StartItemRoulette(player, P_RandomRange(PR_FUZZ, 0, 1)); } diff --git a/src/objects/hyudoro.c b/src/objects/hyudoro.c index 2527ea903..f1e0f440e 100644 --- a/src/objects/hyudoro.c +++ b/src/objects/hyudoro.c @@ -629,7 +629,7 @@ award_immediately (mobj_t *hyu) return false; } - if (!P_CanPickupItem(player, 1)) + if (!P_CanPickupItem(player, PICKUP_ITEMBOX)) return false; // Prevent receiving any more items or even stacked diff --git a/src/objects/random-item.c b/src/objects/random-item.c index 0e0a2a788..244fcf2bb 100644 --- a/src/objects/random-item.c +++ b/src/objects/random-item.c @@ -48,7 +48,7 @@ static player_t *GetItemBoxPlayer(mobj_t *mobj) } // Always use normal item box rules -- could pass in "2" for fakes but they blend in better like this - if (P_CanPickupItem(&players[i], 1)) + if (P_CanPickupItem(&players[i], PICKUP_ITEMBOX)) { // Check for players who can take this pickup, but won't be allowed to (antifarming) UINT8 mytype = (mobj->flags2 & MF2_BOSSDEAD) ? 2 : 1; From 5129250e48ad8dadde328292ac3318b51cf059e1 Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Tue, 13 May 2025 12:39:16 -0400 Subject: [PATCH 08/32] WIP: no early items --- src/d_player.h | 2 ++ src/g_game.c | 6 ++++++ src/k_kart.c | 1 + src/lua_playerlib.c | 4 ++++ src/objects/random-item.c | 9 +++++++++ src/p_inter.c | 3 +++ src/p_saveg.cpp | 4 ++++ 7 files changed, 29 insertions(+) diff --git a/src/d_player.h b/src/d_player.h index 64547b348..ca4e4f38e 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -727,6 +727,8 @@ struct player_t UINT8 justDI; // Turn-lockout timer to briefly prevent unintended turning after DI, resets when actionable or no input boolean flipDI; // Bananas flip the DI direction. Was a bug, but it made bananas much more interesting. + boolean cangrabitems; + SINT8 drift; // (-5 to 5) - Drifting Left or Right, plus a bigger counter = sharper turn fixed_t driftcharge; // Charge your drift so you can release a burst of speed UINT16 driftboost; // (0 to 125 baseline) - Boost you get from drifting diff --git a/src/g_game.c b/src/g_game.c index 392d6f72b..e0bc76b25 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2241,6 +2241,8 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) UINT8 botdiffincrease; boolean botrival; + boolean cangrabitems; + SINT8 xtralife; uint8_t public_key[PUBKEYLENGTH]; @@ -2342,6 +2344,8 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) bot = players[player].bot; botdifficulty = players[player].botvars.difficulty; + cangrabitems = players[player].cangrabitems; + botdiffincrease = players[player].botvars.diffincrease; botrival = players[player].botvars.rival; @@ -2567,6 +2571,8 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) p->charflags = charflags; p->lastfakeskin = lastfakeskin; + p->cangrabitems = cangrabitems; + memcpy(players[player].availabilities, availabilities, sizeof(availabilities)); p->followitem = followitem; diff --git a/src/k_kart.c b/src/k_kart.c index 5173fa3eb..48e20206b 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -4235,6 +4235,7 @@ void K_CheckpointCrossAward(player_t *player) return; player->exp += K_GetExpAdjustment(player); + player->cangrabitems = true; K_AwardPlayerRings(player, (player->bot ? 20 : 10), true); } diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 62eca4611..0da01f714 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -264,6 +264,8 @@ static int player_get(lua_State *L) lua_pushinteger(L, plr->justDI); else if (fastcmp(field,"flipdi")) lua_pushboolean(L, plr->flipDI); + else if (fastcmp(field,"cangrabitems")) + lua_pushboolean(L, plr->cangrabitems); else if (fastcmp(field,"analoginput")) lua_pushboolean(L, plr->analoginput); else if (fastcmp(field,"transfer")) @@ -876,6 +878,8 @@ static int player_set(lua_State *L) plr->justDI = luaL_checkinteger(L, 3); else if (fastcmp(field,"flipdi")) plr->flipDI = luaL_checkboolean(L, 3); + else if (fastcmp(field,"cangrabitems")) + plr->cangrabitems = luaL_checkboolean(L, 3); else if (fastcmp(field,"incontrol")) plr->incontrol = luaL_checkinteger(L, 3); else if (fastcmp(field,"progressivethrust")) diff --git a/src/objects/random-item.c b/src/objects/random-item.c index 0e0a2a788..fa5419084 100644 --- a/src/objects/random-item.c +++ b/src/objects/random-item.c @@ -112,6 +112,15 @@ void Obj_RandomItemVisuals(mobj_t *mobj) ItemBoxScaling(mobj); item_vfxtimer(mobj)++; + for (UINT8 i = 0; i <= r_splitscreen; i++) + { + UINT32 flag = K_GetPlayerDontDrawFlag(&players[displayplayers[i]]); + if (!players[displayplayers[i]].cangrabitems) + mobj->renderflags |= flag; + else + mobj->renderflags &= ~(flag); + } + if (mobj->type != MT_RANDOMITEM) return; diff --git a/src/p_inter.c b/src/p_inter.c index b3d178254..76997b541 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -128,6 +128,9 @@ boolean P_CanPickupItem(player_t *player, UINT8 weapon) if (weapon != 2 && player->instaWhipCharge) return false; + if (weapon == 1 && !player->cangrabitems) + return false; + if (weapon) { // Item slot already taken up diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index 9f11fbb70..4abeb51fb 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -459,6 +459,8 @@ static void P_NetArchivePlayers(savebuffer_t *save) WRITEUINT8(save->p, players[i].justDI); WRITEUINT8(save->p, players[i].flipDI); + WRITEUINT8(save->p, players[i].cangrabitems); + WRITESINT8(save->p, players[i].drift); WRITEFIXED(save->p, players[i].driftcharge); WRITEUINT16(save->p, players[i].driftboost); @@ -1097,6 +1099,8 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) players[i].justDI = READUINT8(save->p); players[i].flipDI = (boolean)READUINT8(save->p); + players[i].cangrabitems = (boolean)READUINT8(save->p); + players[i].drift = READSINT8(save->p); players[i].driftcharge = READFIXED(save->p); players[i].driftboost = READUINT16(save->p); From 4b1417fe9784a9e795d5dd18ffe209f22b103302 Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Tue, 13 May 2025 13:52:15 -0400 Subject: [PATCH 09/32] Reset grabitems between maps --- src/g_game.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/g_game.c b/src/g_game.c index e0bc76b25..f129c13f9 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2429,6 +2429,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) bigwaypointgap = 0; tallyactive = false; + cangrabitems = false; } else { From 7ac26760b7a6e5166ace0e06826e28530d2ef488 Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Tue, 13 May 2025 15:30:18 -0400 Subject: [PATCH 10/32] Reenable solver angle cheats --- src/p_user.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/p_user.c b/src/p_user.c index c29b5e84d..abd2138e4 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -2359,6 +2359,8 @@ static void P_UpdatePlayerAngle(player_t *player) angle_t targetAngle = (player->cmd.angle) << TICCMD_REDUCE; angle_t targetDelta = targetAngle - (player->mo->angle); +#define SOLVERANGLECHEATS + #ifdef SOLVERANGLECHEATS // Corrections via fake turn go through easing. // That means undoing them takes the same amount of time as doing them. From 25de15672e3589d0036135ea7090a4cda4c1c3fc Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Tue, 13 May 2025 16:00:37 -0400 Subject: [PATCH 11/32] Fix item flicker --- src/objects/random-item.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/objects/random-item.c b/src/objects/random-item.c index fa5419084..f1409ceb2 100644 --- a/src/objects/random-item.c +++ b/src/objects/random-item.c @@ -112,18 +112,22 @@ void Obj_RandomItemVisuals(mobj_t *mobj) ItemBoxScaling(mobj); item_vfxtimer(mobj)++; - for (UINT8 i = 0; i <= r_splitscreen; i++) - { - UINT32 flag = K_GetPlayerDontDrawFlag(&players[displayplayers[i]]); - if (!players[displayplayers[i]].cangrabitems) - mobj->renderflags |= flag; - else - mobj->renderflags &= ~(flag); - } - if (mobj->type != MT_RANDOMITEM) return; + if (!((mobj->flags & MF_NOCLIPTHING) || mobj->fuse)) + { + for (UINT8 i = 0; i <= r_splitscreen; i++) + { + UINT32 flag = K_GetPlayerDontDrawFlag(&players[displayplayers[i]]); + if (!players[displayplayers[i]].cangrabitems) + mobj->renderflags |= flag; + else + mobj->renderflags &= ~(flag); + } + } + + // Respawn flow, documented by a dumb asshole: // P_TouchSpecialThing -> P_ItemPop sets fuse, NOCLIPTHING and DONTDRAW. // P_FuseThink does visual flicker, and when fuse is 0, unsets NOCLIPTHING/DONTDRAW/etc... From 77167d1e32519a24851ca1c354abb91ba22b2853 Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Tue, 13 May 2025 16:49:32 -0400 Subject: [PATCH 12/32] Early item fade, fix gametypes / edge cases --- src/d_player.h | 2 +- src/g_game.c | 8 +++++++- src/k_kart.c | 4 +++- src/k_kart.h | 2 ++ src/lua_playerlib.c | 4 ++-- src/objects/random-item.c | 29 ++++++++++++++++++++++++----- src/p_saveg.cpp | 2 +- 7 files changed, 40 insertions(+), 11 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index ca4e4f38e..75db7c887 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -727,7 +727,7 @@ struct player_t UINT8 justDI; // Turn-lockout timer to briefly prevent unintended turning after DI, resets when actionable or no input boolean flipDI; // Bananas flip the DI direction. Was a bug, but it made bananas much more interesting. - boolean cangrabitems; + UINT8 cangrabitems; SINT8 drift; // (-5 to 5) - Drifting Left or Right, plus a bigger counter = sharper turn fixed_t driftcharge; // Charge your drift so you can release a burst of speed diff --git a/src/g_game.c b/src/g_game.c index f129c13f9..0b305c3d1 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2429,7 +2429,13 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) bigwaypointgap = 0; tallyactive = false; - cangrabitems = false; + + cangrabitems = 0; + if (gametyperules & GTR_SPHERES + || gametyperules & GTR_CATCHER + || G_TimeAttackStart() + || gametype == GT_TUTORIAL) + cangrabitems = EARLY_ITEM_FLICKER; } else { diff --git a/src/k_kart.c b/src/k_kart.c index 48e20206b..66e47e269 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -4235,7 +4235,7 @@ void K_CheckpointCrossAward(player_t *player) return; player->exp += K_GetExpAdjustment(player); - player->cangrabitems = true; + player->cangrabitems = 1; K_AwardPlayerRings(player, (player->bot ? 20 : 10), true); } @@ -9579,6 +9579,8 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) player->invincibilitytimer--; } + if (player->cangrabitems && player->cangrabitems <= EARLY_ITEM_FLICKER) + player->cangrabitems++; if (!player->invincibilitytimer) player->invincibilityextensions = 0; diff --git a/src/k_kart.h b/src/k_kart.h index 00b0e44c3..b634b96eb 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -60,6 +60,8 @@ Make sure this matches the actual number of states #define SCAMDIST (2000) +#define EARLY_ITEM_FLICKER (NUMTRANSMAPS) + // 2023-08-26 +ang20 to Sal's OG values to make them friendlier - Tyron #define STUMBLE_STEEP_VAL (ANG60 + ANG20) #define STUMBLE_STEEP_VAL_AIR (ANG30 + ANG10 + ANG20) diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 0da01f714..42904bb21 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -265,7 +265,7 @@ static int player_get(lua_State *L) else if (fastcmp(field,"flipdi")) lua_pushboolean(L, plr->flipDI); else if (fastcmp(field,"cangrabitems")) - lua_pushboolean(L, plr->cangrabitems); + lua_pushinteger(L, plr->cangrabitems); else if (fastcmp(field,"analoginput")) lua_pushboolean(L, plr->analoginput); else if (fastcmp(field,"transfer")) @@ -879,7 +879,7 @@ static int player_set(lua_State *L) else if (fastcmp(field,"flipdi")) plr->flipDI = luaL_checkboolean(L, 3); else if (fastcmp(field,"cangrabitems")) - plr->cangrabitems = luaL_checkboolean(L, 3); + plr->cangrabitems = luaL_checkinteger(L, 3); else if (fastcmp(field,"incontrol")) plr->incontrol = luaL_checkinteger(L, 3); else if (fastcmp(field,"progressivethrust")) diff --git a/src/objects/random-item.c b/src/objects/random-item.c index f1409ceb2..3ef9d1f9b 100644 --- a/src/objects/random-item.c +++ b/src/objects/random-item.c @@ -115,15 +115,34 @@ void Obj_RandomItemVisuals(mobj_t *mobj) if (mobj->type != MT_RANDOMITEM) return; + // Fade items in as we cross the first checkpoint, but don't touch their visibility otherwise! if (!((mobj->flags & MF_NOCLIPTHING) || mobj->fuse)) { + UINT8 maxgrab = 0; + for (UINT8 i = 0; i <= r_splitscreen; i++) { - UINT32 flag = K_GetPlayerDontDrawFlag(&players[displayplayers[i]]); - if (!players[displayplayers[i]].cangrabitems) - mobj->renderflags |= flag; - else - mobj->renderflags &= ~(flag); + maxgrab = max(maxgrab, players[displayplayers[i]].cangrabitems); + } + + if (maxgrab == 0) + mobj->renderflags |= RF_DONTDRAW; + else + mobj->renderflags &= ~RF_DONTDRAW; + + if (maxgrab > 0 && maxgrab <= EARLY_ITEM_FLICKER) + { + UINT8 maxtranslevel = NUMTRANSMAPS; + + UINT8 trans = maxgrab; + if (trans > maxtranslevel) + trans = maxtranslevel; + trans = NUMTRANSMAPS - trans; + + mobj->renderflags &= ~(RF_TRANSMASK); + + if (trans != 0) + mobj->renderflags |= (trans << RF_TRANSSHIFT); } } diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index 4abeb51fb..827ab32cb 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -1099,7 +1099,7 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) players[i].justDI = READUINT8(save->p); players[i].flipDI = (boolean)READUINT8(save->p); - players[i].cangrabitems = (boolean)READUINT8(save->p); + players[i].cangrabitems = READUINT8(save->p); players[i].drift = READSINT8(save->p); players[i].driftcharge = READFIXED(save->p); From b9d02184d243b07bbb4edc55b9d381fa7e3eacda Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Tue, 13 May 2025 20:04:34 -0400 Subject: [PATCH 13/32] Never retrigger item fade --- src/k_kart.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/k_kart.c b/src/k_kart.c index 66e47e269..daced7c9e 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -4235,7 +4235,8 @@ void K_CheckpointCrossAward(player_t *player) return; player->exp += K_GetExpAdjustment(player); - player->cangrabitems = 1; + if (!player->cangrabitems) + player->cangrabitems = 1; K_AwardPlayerRings(player, (player->bot ? 20 : 10), true); } From 45484383088cab29b20a266decd9072e5f9652bf Mon Sep 17 00:00:00 2001 From: Lach Date: Wed, 14 May 2025 23:48:58 +1000 Subject: [PATCH 14/32] Adjust stun durations & fix some item capsule pickup issues --- src/k_botsearch.cpp | 2 +- src/k_kart.c | 2 +- src/objects/random-item.c | 4 ++-- src/p_inter.c | 48 +++++++++++++++++++++------------------ src/p_local.h | 5 ++++ 5 files changed, 35 insertions(+), 26 deletions(-) diff --git a/src/k_botsearch.cpp b/src/k_botsearch.cpp index 1bb53c051..e60b6c85d 100644 --- a/src/k_botsearch.cpp +++ b/src/k_botsearch.cpp @@ -541,7 +541,7 @@ static BlockItReturn_t K_FindObjectsForNudging(mobj_t *thing) break; } - if ((RINGTOTAL(g_nudgeSearch.botmo->player) < 20 && !(g_nudgeSearch.botmo->player->pflags & PF_RINGLOCK) + if ((RINGTOTAL(g_nudgeSearch.botmo->player) < 20 && P_CanPickupItem(g_nudgeSearch.botmo->player, PICKUP_RINGORSPHERE)) && !thing->extravalue1 && (g_nudgeSearch.botmo->player->itemtype != KITEM_LIGHTNINGSHIELD)) diff --git a/src/k_kart.c b/src/k_kart.c index 80aa7cd4b..4973f1e7a 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -8819,7 +8819,7 @@ static inline BlockItReturn_t PIT_AttractingRings(mobj_t *thing) return BMIT_CONTINUE; // Too far away } - if (RINGTOTAL(attractmo->player) >= 20 || (attractmo->player->pflags & PF_RINGLOCK)) + if (RINGTOTAL(attractmo->player) >= 20 || !P_CanPickupItem(attractmo->player, PICKUP_RINGORSPHERE)) { // Already reached max -- just joustle rings around. diff --git a/src/objects/random-item.c b/src/objects/random-item.c index cd4cf8570..70f565046 100644 --- a/src/objects/random-item.c +++ b/src/objects/random-item.c @@ -47,11 +47,11 @@ static player_t *GetItemBoxPlayer(mobj_t *mobj) continue; } - // Always use normal item box rules -- could pass in "2" for fakes but they blend in better like this + // Always use normal item box rules -- could pass in "PICKUP_EGGBOX" for fakes but they blend in better like this if (P_CanPickupItem(&players[i], PICKUP_ITEMBOX)) { // Check for players who can take this pickup, but won't be allowed to (antifarming) - UINT8 mytype = (mobj->flags2 & MF2_BOSSDEAD) ? 2 : 1; + UINT8 mytype = (mobj->flags2 & MF2_BOSSDEAD) ? CHEESE_RINGBOX : CHEESE_ITEMBOX; if (P_IsPickupCheesy(&players[i], mytype)) continue; diff --git a/src/p_inter.c b/src/p_inter.c index 2e0a7d5a1..b15bf8dab 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -120,10 +120,7 @@ boolean P_CanPickupItem(player_t *player, UINT8 weapon) if (player->exiting || mapreset || (player->pflags & PF_ELIMINATED) || player->itemRoulette.reserved) return false; - // 0: Sphere/Ring - // 1: Random Item / Capsule - // 2: Eggbox - // 3: Paperitem + // See p_local.h for pickup types if (weapon != PICKUP_EGGBOX && player->instaWhipCharge) return false; @@ -133,6 +130,13 @@ boolean P_CanPickupItem(player_t *player, UINT8 weapon) if (weapon == PICKUP_RINGORSPHERE) { + // No picking up rings while SPB is targetting you + if (player->pflags & PF_RINGLOCK) + { + return false; + } + + // No picking up rings while stunned if (player->stunned > 0) { return false; @@ -178,7 +182,7 @@ boolean P_CanPickupItem(player_t *player, UINT8 weapon) // Allow players to pick up only one pickup from each set of pickups. // Anticheese pickup types are different than-P_CanPickupItem weapon, because that system is // already slightly scary without introducing special cases for different types of the same pickup. -// 1 = floating item, 2 = perma ring, 3 = capsule +// See p_local.h for cheese types. boolean P_IsPickupCheesy(player_t *player, UINT8 type) { extern consvar_t cv_debugcheese; @@ -441,7 +445,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) special->flags &= ~MF_SPECIAL; return; case MT_RANDOMITEM: { - UINT8 cheesetype = (special->flags2 & MF2_BOSSDEAD) ? 2 : 1; // perma ring box + UINT8 cheesetype = (special->flags2 & MF2_BOSSDEAD) ? CHEESE_RINGBOX : CHEESE_ITEMBOX; // perma ring box if (!P_CanPickupItem(player, PICKUP_ITEMBOX)) return; @@ -503,15 +507,13 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) return; break; case KITEM_SUPERRING: - if (player->pflags & PF_RINGLOCK) // no cheaty rings - return; - if (player->instaWhipCharge) + if (!P_CanPickupItem(player, PICKUP_RINGORSPHERE)) // no cheaty rings return; break; default: - if (!P_CanPickupItem(player, PICKUP_ITEMBOX)) + if (!P_CanPickupItem(player, PICKUP_ITEMCAPSULE)) return; - if (P_IsPickupCheesy(player, 3)) + if (P_IsPickupCheesy(player, CHEESE_ITEMCAPSULE)) return; break; } @@ -691,14 +693,6 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (special->extravalue1) return; - // No picking up rings while SPB is targetting you - if (player->pflags & PF_RINGLOCK) - return; - - // Prepping instawhip? Don't ruin it by collecting rings - if (player->instaWhipCharge) - return; - // Don't immediately pick up spilled rings if (special->threshold > 0 || P_PlayerInPain(player) || player->spindash) // player->spindash: Otherwise, players can pick up rings that are thrown out of them from invinc spindash penalty return; @@ -3405,16 +3399,26 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da player->flipDI = true; } - // I'm wondering if weight 9 should have it for 70 tics, while weight 1 would have it for like 280 (basically x4) - // It may be worth designing it LIKE a value that could be changed for the future though, we may want different things to give different multipliers of stun later imo - stunTics = 2*TICRATE + (6*TICRATE * (9 - player->kartweight) / 8); + // Apply stun! + // Feel free to move these calculations higher up if different damage sources should apply variable stun in future + #define MIN_STUNTICS (8 * TICRATE) + #define MAX_STUNTICS (18 * TICRATE) + stunTics = Easing_Linear((player->kartweight - 1) * FRACUNIT / 8, MAX_STUNTICS, MIN_STUNTICS); stunTics >>= player->stunnedCombo; // consecutive hits add half as much stun as the previous hit + // 1/3 base stun values in battle + if (gametyperules & GTR_SPHERES) + { + stunTics /= 3; + } + if (player->stunnedCombo < UINT8_MAX) { player->stunnedCombo++; } player->stunned = (player->stunned & 0x8000) | min(0x7FFF, (player->stunned & 0x7FFF) + stunTics); + #undef MIN_STUNTICS + #undef MAX_STUNTICS K_DefensiveOverdrive(target->player); } diff --git a/src/p_local.h b/src/p_local.h index 00e86b453..505ccd8e0 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -562,6 +562,11 @@ boolean P_CheckRacers(void); #define PICKUP_ITEMBOX 1 #define PICKUP_EGGBOX 2 #define PICKUP_PAPERITEM 3 +#define PICKUP_ITEMCAPSULE 4 + +#define CHEESE_ITEMBOX 1 +#define CHEESE_RINGBOX 2 +#define CHEESE_ITEMCAPSULE 3 boolean P_CanPickupItem(player_t *player, UINT8 weapon); boolean P_IsPickupCheesy(player_t *player, UINT8 type); From 7a4b17c230640ddf325e175630dd4ded57706ea9 Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 14 May 2025 13:07:25 -0700 Subject: [PATCH 15/32] TwodeeRenderer::rewrite_patch_quad_vertices: ensure high side of clipping clamp is >= low side - Fixes assert raised on GCC 15 - Testing - Use Debug build - Use GCC 15 - Be on linux - Give yourself the Bubble Shield --- src/hwr2/twodee_renderer.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/hwr2/twodee_renderer.cpp b/src/hwr2/twodee_renderer.cpp index 020b8306d..a7e8fe761 100644 --- a/src/hwr2/twodee_renderer.cpp +++ b/src/hwr2/twodee_renderer.cpp @@ -130,10 +130,10 @@ void TwodeeRenderer::rewrite_patch_quad_vertices(Draw2dList& list, const Draw2dP const float cmd_xrange = cmd.xmax - cmd.xmin; const float cmd_yrange = cmd.ymax - cmd.ymin; - const float clipped_xmin = cmd.clip ? std::clamp(cmd.xmin, cmd.clip_xmin, cmd.clip_xmax) : cmd.xmin; - const float clipped_xmax = cmd.clip ? std::clamp(cmd.xmax, cmd.clip_xmin, cmd.clip_xmax) : cmd.xmax; - const float clipped_ymin = cmd.clip ? std::clamp(cmd.ymin, cmd.clip_ymin, cmd.clip_ymax) : cmd.ymin; - const float clipped_ymax = cmd.clip ? std::clamp(cmd.ymax, cmd.clip_ymin, cmd.clip_ymax) : cmd.ymax; + const float clipped_xmin = cmd.clip ? std::clamp(cmd.xmin, cmd.clip_xmin, std::max(cmd.clip_xmax, cmd.clip_xmin)) : cmd.xmin; + const float clipped_xmax = cmd.clip ? std::clamp(cmd.xmax, cmd.clip_xmin, std::max(cmd.clip_xmax, cmd.clip_xmin)) : cmd.xmax; + const float clipped_ymin = cmd.clip ? std::clamp(cmd.ymin, cmd.clip_ymin, std::max(cmd.clip_ymax, cmd.clip_ymin)) : cmd.ymin; + const float clipped_ymax = cmd.clip ? std::clamp(cmd.ymax, cmd.clip_ymin, std::max(cmd.clip_ymax, cmd.clip_ymin)) : cmd.ymax; const float trimmed_left = cmd.flip ? (1.f - trim_umax) : trim_umin; const float trimmed_right = cmd.flip ? trim_umin : (1.f - trim_umax); From 7910d128097feb161b3bb667c4d87bab6ef79eab Mon Sep 17 00:00:00 2001 From: Eidolon Date: Wed, 14 May 2025 19:13:54 -0500 Subject: [PATCH 16/32] Default move construct/assign of rhi::Handle --- src/rhi/handle.hpp | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/src/rhi/handle.hpp b/src/rhi/handle.hpp index deed29498..8101d9482 100644 --- a/src/rhi/handle.hpp +++ b/src/rhi/handle.hpp @@ -44,20 +44,11 @@ public: Handle(NullHandleType) noexcept : Handle() {} - Handle(const Handle&) = default; - Handle(Handle&& rhs) noexcept - { - id_ = std::exchange(rhs.id_, 0); - generation_ = std::exchange(rhs.generation_, 0); - }; + Handle(const Handle&) noexcept = default; + Handle(Handle&&) noexcept = default; - Handle& operator=(const Handle&) = default; - Handle& operator=(Handle&& rhs) noexcept - { - id_ = std::exchange(rhs.id_, 0); - generation_ = std::exchange(rhs.generation_, 0); - return *this; - } + Handle& operator=(const Handle&) noexcept = default; + Handle& operator=(Handle&&) noexcept = default; // Conversions from Handles of derived type U to base type T From 81f49a2cf8adb1149fa44987c8ad985dd07e0767 Mon Sep 17 00:00:00 2001 From: Eidolon Date: Thu, 15 May 2025 20:45:51 -0500 Subject: [PATCH 17/32] Fix uninitialized values of hashmap propagating in move --- src/core/hash_map.hpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/core/hash_map.hpp b/src/core/hash_map.hpp index 8f430aee3..70cc18c37 100644 --- a/src/core/hash_map.hpp +++ b/src/core/hash_map.hpp @@ -240,7 +240,16 @@ public: HashMap(HashMap&& r) noexcept { - *this = std::move(r); + buckets_ = r.buckets_; + r.buckets_ = 0; + size_ = r.size_; + r.size_ = 0; + heads_ = r.heads_; + r.heads_ = nullptr; + hasher_ = r.hasher_; + r.hasher_ = {}; + key_equal_ = r.key_equal_; + r.key_equal_ = {}; }; ~HashMap() From dcbbea4c4b883f17bfb91a293b0ed314ac3777e4 Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Fri, 16 May 2025 11:44:57 -0400 Subject: [PATCH 18/32] WIP: pick-me-up --- src/d_player.h | 2 + src/k_collide.cpp | 18 +++++++ src/k_kart.c | 117 +++++++++++++++++++++++++++++++++++++++++ src/k_kart.h | 2 + src/lua_playerlib.c | 4 ++ src/objects/orbinaut.c | 3 ++ src/p_saveg.cpp | 4 ++ 7 files changed, 150 insertions(+) diff --git a/src/d_player.h b/src/d_player.h index 75db7c887..758edd1ec 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -779,6 +779,8 @@ struct player_t // Item held stuff SINT8 itemtype; // KITEM_ constant for item number UINT8 itemamount; // Amount of said item + SINT8 backupitemtype; + UINT8 backupitemamount; SINT8 throwdir; // Held dir of controls; 1 = forward, 0 = none, -1 = backward (was "player->heldDir") UINT8 itemscale; // Item scale value, from when an item was taken out. (0 for normal, 1 for grow, 2 for shrink.) diff --git a/src/k_collide.cpp b/src/k_collide.cpp index 3e048234e..55f7812df 100644 --- a/src/k_collide.cpp +++ b/src/k_collide.cpp @@ -72,6 +72,9 @@ boolean K_BananaBallhogCollide(mobj_t *t1, mobj_t *t2) if (t1->type == MT_BALLHOG && t2->type == MT_BALLHOG) return true; // Ballhogs don't collide with eachother + if (K_TryPickMeUp(t1, t2)) + return true; + if (t2->player) { if (t2->player->flashing > 0 && t2->hitlag == 0) @@ -164,6 +167,9 @@ boolean K_EggItemCollide(mobj_t *t1, mobj_t *t2) return true; } + if (K_TryPickMeUp(t1, t2)) + return true; + if (t2->player) { if ((t1->target == t2 || t1->target == t2->target) && (t1->threshold > 0)) @@ -425,6 +431,9 @@ boolean K_LandMineCollide(mobj_t *t1, mobj_t *t2) if (t1->health <= 0 || t2->health <= 0) return true; + if (K_TryPickMeUp(t1, t2)) + return true; + if (t2->player) { const INT32 oldhitlag = t2->hitlag; @@ -532,6 +541,9 @@ boolean K_DropTargetCollide(mobj_t *t1, mobj_t *t2) if (t2->player && (t2->player->hyudorotimer || t2->player->justbumped)) return true; + if (K_TryPickMeUp(t1, t2)) + return true; + if (draggeddroptarget && P_MobjWasRemoved(draggeddroptarget)) draggeddroptarget = NULL; // Beware order-of-execution on crushers, I guess?! @@ -839,6 +851,9 @@ boolean K_BubbleShieldReflect(mobj_t *t1, mobj_t *t2) boolean K_BubbleShieldCollide(mobj_t *t1, mobj_t *t2) { + if (K_TryPickMeUp(t1, t2)) + return true; + if (t2->type == MT_PLAYER) { // Counter desyncs @@ -1053,6 +1068,9 @@ boolean K_KitchenSinkCollide(mobj_t *t1, mobj_t *t2) if (((t1->target == t2) || (!(t2->flags & (MF_ENEMY|MF_BOSS)) && (t1->target == t2->target))) && (t1->threshold > 0 || (t2->type != MT_PLAYER && t2->threshold > 0))) return true; + if (K_TryPickMeUp(t1, t2)) + return true; + if (t2->player) { if (t2->player->flashing > 0 && t2->hitlag == 0) diff --git a/src/k_kart.c b/src/k_kart.c index daced7c9e..c524af727 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -9158,6 +9158,17 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) if (player->itemtype == KITEM_NONE) player->itemflags &= ~IF_HOLDREADY; + if (player->itemtype == KITEM_NONE && player->backupitemtype) + { + player->itemtype = player->backupitemtype; + player->itemamount = player->backupitemamount; + + player->backupitemtype = 0; + player->backupitemamount = 0; + + S_StartSound(player->mo, sfx_mbs54); + } + if (onground || player->transfer < 10*player->mo->scale) { player->transfer = 0; @@ -15453,4 +15464,110 @@ UINT32 K_GetNumGradingPoints(void) return numlaps * (1 + Obj_GetCheckpointCount()); } +static SINT8 K_PickUp(player_t *player, mobj_t *picked) +{ + SINT8 type = -1; + SINT8 amount = 1; + + switch (picked->type) + { + case MT_ORBINAUT: + case MT_ORBINAUT_SHIELD: + type = KITEM_ORBINAUT; + break; + case MT_JAWZ: + case MT_JAWZ_SHIELD: + type = KITEM_JAWZ; + break; + case MT_BALLHOG: + type = KITEM_BALLHOG; + break; + case MT_LANDMINE: + type = KITEM_LANDMINE; + break; + case MT_EGGMANITEM: + case MT_EGGMANITEM_SHIELD: + type = KITEM_EGGMAN; + break; + case MT_BANANA: + case MT_BANANA_SHIELD: + type = KITEM_BANANA; + break; + case MT_DROPTARGET: + case MT_DROPTARGET_SHIELD: + type = KITEM_DROPTARGET; + break; + default: + type = KITEM_SAD; + break; + } + + if (player->itemtype == type && player->itemamount && !player->itemflags & IF_ITEMOUT) + { + // We have this item in main slot but not deployed, just add it + player->itemamount += amount; + } + else if (player->backupitemamount && player->backupitemtype) + { + // We already have a backup item, stack it if it can be stacked or discard it + if (player->backupitemtype == type) + { + player->backupitemamount += amount; + } + else + { + K_DropPaperItem(player, player->backupitemtype, player->backupitemamount); + player->backupitemtype = type; + player->backupitemamount = amount; + } + } + else + { + // We have no backup item, load one up + player->backupitemtype = type; + player->backupitemamount = amount; + } + + S_StartSound(player->mo, sfx_gsha7); +} + +// ACHTUNG this destroys items when returning true, make sure to bail out +boolean K_TryPickMeUp(mobj_t *m1, mobj_t *m2) +{ + if (!m1 || P_MobjWasRemoved(m1)) + return false; + + if (!m2 || P_MobjWasRemoved(m2)) + return false; + + if (m1->type != MT_PLAYER && m2->type != MT_PLAYER) + return false; + + if (m1->type == MT_PLAYER && m2->type == MT_PLAYER) + return false; + + CONS_Printf("player check passed\n"); + + mobj_t *victim = m1; + mobj_t *inflictor = m2; + + // Convenience for collision functions where arg order is freaky + if (m2->type == MT_PLAYER) + { + victim = m2; + inflictor = m1; + } + + if (inflictor->target != victim) + return false; + + CONS_Printf("target check passed\n"); + + K_AddHitLag(victim, 2, false); + K_PickUp(victim->player, inflictor); + + P_RemoveMobj(inflictor); + return true; +} + //} diff --git a/src/k_kart.h b/src/k_kart.h index b634b96eb..5c3cc88db 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -314,6 +314,8 @@ UINT16 K_GetDisplayEXP(player_t *player); UINT32 K_GetNumGradingPoints(void); +boolean K_TryPickMeUp(mobj_t *m1, mobj_t *m2); + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 42904bb21..ee78293f7 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -434,6 +434,10 @@ static int player_get(lua_State *L) lua_pushinteger(L, plr->itemtype); else if (fastcmp(field,"itemamount")) lua_pushinteger(L, plr->itemamount); + else if (fastcmp(field,"backupitemtype")) + lua_pushinteger(L, plr->backupitemtype); + else if (fastcmp(field,"backupitemamount")) + lua_pushinteger(L, plr->backupitemamount); else if (fastcmp(field,"throwdir")) lua_pushinteger(L, plr->throwdir); else if (fastcmp(field,"sadtimer")) diff --git a/src/objects/orbinaut.c b/src/objects/orbinaut.c index aa162bc9b..e8f774fd2 100644 --- a/src/objects/orbinaut.c +++ b/src/objects/orbinaut.c @@ -190,6 +190,9 @@ boolean Obj_OrbinautJawzCollide(mobj_t *t1, mobj_t *t2) return true; } + if (K_TryPickMeUp(t1, t2)) + return true; + if (t1->type == MT_GARDENTOP) { tumbleitem = true; diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index 827ab32cb..90cea3d74 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -508,6 +508,8 @@ static void P_NetArchivePlayers(savebuffer_t *save) WRITESINT8(save->p, players[i].itemtype); WRITEUINT8(save->p, players[i].itemamount); + WRITESINT8(save->p, players[i].backupitemtype); + WRITEUINT8(save->p, players[i].backupitemamount); WRITESINT8(save->p, players[i].throwdir); WRITEUINT8(save->p, players[i].sadtimer); @@ -1148,6 +1150,8 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) players[i].itemtype = READSINT8(save->p); players[i].itemamount = READUINT8(save->p); + players[i].backupitemtype = READSINT8(save->p); + players[i].backupitemamount = READUINT8(save->p); players[i].throwdir = READSINT8(save->p); players[i].sadtimer = READUINT8(save->p); From 34ddee575ad09619a8ec153ba7399518fe6a745a Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Fri, 16 May 2025 13:04:55 -0400 Subject: [PATCH 19/32] WIP: more pick-me-up --- src/k_hud.cpp | 176 ++++++++++++++++++++++++++++++++++++++++++++ src/k_hud_track.cpp | 11 +++ src/k_kart.c | 40 ++++++---- src/p_mobj.c | 10 +++ 4 files changed, 222 insertions(+), 15 deletions(-) diff --git a/src/k_hud.cpp b/src/k_hud.cpp index ec93ed554..db6d7e701 100644 --- a/src/k_hud.cpp +++ b/src/k_hud.cpp @@ -1889,6 +1889,179 @@ static void K_drawKartItem(void) } } +// So, like, we've already established that HUD code is unsavable, right? +static void K_drawBackupItem(void) +{ + // ITEM_X = BASEVIDWIDTH-50; // 270 + // ITEM_Y = 24; // 24 + + // Why write V_DrawScaledPatch calls over and over when they're all the same? + // Set to 'no item' just in case. + const UINT8 offset = 1; + patch_t *localpatch[3] = { kp_nodraw, kp_nodraw, kp_nodraw }; + UINT8 localamt[3] = {0, 0, 0}; + patch_t *localbg = ((offset) ? kp_itembg[2] : kp_itembg[0]); + patch_t *localinv = ((offset) ? kp_invincibility[((leveltime % (6*3)) / 3) + 7] : kp_invincibility[(leveltime % (7*3)) / 3]); + INT32 fx = 0, fy = 0, fflags = 0; // final coords for hud and flags... + const INT32 numberdisplaymin = ((!offset && stplyr->itemtype == KITEM_ORBINAUT) ? 5 : 2); + skincolornum_t localcolor[3] = { static_cast(stplyr->skincolor) }; + SINT8 colormode[3] = { TC_RAINBOW }; + boolean flipamount = false; // Used for 3P/4P splitscreen to flip item amount stuff + + fixed_t rouletteOffset = 0; + fixed_t rouletteSpace = ROULETTE_SPACING; + vector2_t rouletteCrop = {7, 7}; + + boolean flashOnOne = false; + boolean flashOnTwo = false; + + if (stplyr->backupitemamount <= 0) + return; + + switch (stplyr->backupitemtype) + { + case KITEM_INVINCIBILITY: + localpatch[1] = localinv; + localbg = kp_itembg[offset+1]; + break; + + case KITEM_ORBINAUT: + localpatch[1] = kp_orbinaut[(offset ? 4 : std::min(stplyr->itemamount-1, 3))]; + break; + + case KITEM_SPB: + case KITEM_LIGHTNINGSHIELD: + case KITEM_BUBBLESHIELD: + case KITEM_FLAMESHIELD: + localbg = kp_itembg[offset+1]; + /*FALLTHRU*/ + + default: + localpatch[1] = K_GetCachedItemPatch(stplyr->backupitemtype, offset); + + if (localpatch[1] == NULL) + localpatch[1] = kp_nodraw; // diagnose underflows + break; + } + + // pain and suffering defined below + if (offset) + { + if (!(R_GetViewNumber() & 1)) // If we are P1 or P3... + { + fx = 0; + fy = 0; + fflags = V_SNAPTOLEFT|V_SNAPTOTOP|V_SPLITSCREEN; + } + else // else, that means we're P2 or P4. + { + fx = ITEM2_X*2; + fy = 0; + fflags = V_SNAPTORIGHT|V_SNAPTOTOP|V_SPLITSCREEN; + flipamount = true; + } + + rouletteSpace = ROULETTE_SPACING_SPLITSCREEN; + rouletteOffset = FixedMul(rouletteOffset, FixedDiv(ROULETTE_SPACING_SPLITSCREEN, ROULETTE_SPACING)); + rouletteCrop.x = 16; + rouletteCrop.y = 15; + } + else + { + fx = ITEM_X; + fy = ITEM_Y; + fflags = V_SNAPTOTOP|V_SNAPTOLEFT|V_SPLITSCREEN; + } + + if (r_splitscreen == 1) + { + fy -= 5; + } + + auto draw_item = [&](fixed_t y, int i) + { + const UINT8 *colormap = (localcolor[i] ? R_GetTranslationColormap(colormode[i], localcolor[i], GTC_CACHE) : NULL); + V_DrawFixedPatch( + fx< 1) + { + using srb2::Draw; + Draw( + fx + rouletteCrop.x + FixedToFloat(rouletteSpace/2), + fy + rouletteCrop.y + FixedToFloat(rouletteOffset + y + rouletteSpace) - (r_splitscreen > 1 ? 15 : 33)) + .font(r_splitscreen > 1 ? Draw::Font::kRollingNum4P : Draw::Font::kRollingNum) + .align(Draw::Align::kCenter) + .flags(V_HUDTRANS|V_SLIDEIN|fflags) + .colormap(colormap) + .text("{}", localamt[i]); + } + }; + + draw_item(rouletteSpace, 0); + draw_item(-rouletteSpace, 2); + + if (false) + { + // Draw the item underneath the box. + draw_item(0, 1); + V_ClearClipRect(); + } + else + { + // Draw the item above the box. + V_ClearClipRect(); + + boolean transflag = V_HUDTRANS; + + if (cv_reducevfx.value && (flashOnOne || flashOnTwo)) + { + transflag = V_HUDTRANSHALF; + } + + if (stplyr->backupitemamount >= numberdisplaymin && stplyr->itemRoulette.active == false) + { + /* + // Then, the numbers: + V_DrawScaledPatch( + fx + (flipamount ? 48 : 0), fy, + V_HUDTRANS|V_SLIDEIN|fflags|(flipamount ? V_FLIP : 0), + kp_itemmulsticker[offset] + ); // flip this graphic for p2 and p4 in split and shift it. + */ + + V_DrawFixedPatch( + fx<backupitemamount)); + else + V_DrawString(fx+24, fy+31, V_HUDTRANS|V_SLIDEIN|fflags, va("x%d", stplyr->backupitemamount)); + } + else + { + V_DrawScaledPatch(fy+28, fy+41, V_HUDTRANS|V_SLIDEIN|fflags, kp_itemx); + V_DrawTimerString(fx+38, fy+36, V_HUDTRANS|V_SLIDEIN|fflags, va("%d", stplyr->backupitemamount)); + } + } + else + { + V_DrawFixedPatch( + fx<backupitemtype) + K_drawBackupItem(); } } } diff --git a/src/k_hud_track.cpp b/src/k_hud_track.cpp index 0307babb5..301f2f52d 100644 --- a/src/k_hud_track.cpp +++ b/src/k_hud_track.cpp @@ -378,6 +378,17 @@ bool is_object_tracking_target(const mobj_t* mobj) return !(mobj->renderflags & (RF_TRANSMASK | RF_DONTDRAW)) && // the spraycan wasn't collected yet P_CheckSight(stplyr->mo, const_cast(mobj)); + case MT_JAWZ: + case MT_JAWZ_SHIELD: + case MT_ORBINAUT: + case MT_ORBINAUT_SHIELD: + case MT_DROPTARGET: + case MT_DROPTARGET_SHIELD: + case MT_LANDMINE: + case MT_BANANA: + case MT_BANANA_SHIELD: + return (mobj->target && !P_MobjWasRemoved(mobj->target) && mobj->target->player && stplyr == mobj->target->player); + default: return false; } diff --git a/src/k_kart.c b/src/k_kart.c index c524af727..7395d9aa1 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -8349,6 +8349,21 @@ static void K_MoveHeldObjects(player_t *player) } } +// If we can move our backup item into main slots, do so. +static void K_TryMoveBackupItem(player_t *player) +{ + if (player->itemtype == KITEM_NONE && player->backupitemtype) + { + player->itemtype = player->backupitemtype; + player->itemamount = player->backupitemamount; + + player->backupitemtype = 0; + player->backupitemamount = 0; + + S_StartSound(player->mo, sfx_mbs54); + } +} + mobj_t *K_FindJawzTarget(mobj_t *actor, player_t *source, angle_t range) { fixed_t best = INT32_MAX; @@ -9158,16 +9173,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) if (player->itemtype == KITEM_NONE) player->itemflags &= ~IF_HOLDREADY; - if (player->itemtype == KITEM_NONE && player->backupitemtype) - { - player->itemtype = player->backupitemtype; - player->itemamount = player->backupitemamount; - - player->backupitemtype = 0; - player->backupitemamount = 0; - - S_StartSound(player->mo, sfx_mbs54); - } + K_TryMoveBackupItem(player); if (onground || player->transfer < 10*player->mo->scale) { @@ -15464,7 +15470,7 @@ UINT32 K_GetNumGradingPoints(void) return numlaps * (1 + Obj_GetCheckpointCount()); } -static SINT8 K_PickUp(player_t *player, mobj_t *picked) +static void K_PickUp(player_t *player, mobj_t *picked) { SINT8 type = -1; SINT8 amount = 1; @@ -15502,7 +15508,9 @@ static SINT8 K_PickUp(player_t *player, mobj_t *picked) break; } - if (player->itemtype == type && player->itemamount && !player->itemflags & IF_ITEMOUT) + // CONS_Printf("it %d ia %d t %d a %d\n", player->itemtype, player->itemamount, type, amount); + + if (player->itemtype == type && player->itemamount && !(player->itemflags & IF_ITEMOUT)) { // We have this item in main slot but not deployed, just add it player->itemamount += amount; @@ -15519,6 +15527,7 @@ static SINT8 K_PickUp(player_t *player, mobj_t *picked) K_DropPaperItem(player, player->backupitemtype, player->backupitemamount); player->backupitemtype = type; player->backupitemamount = amount; + S_StartSound(player->mo, sfx_kc65); } } else @@ -15529,6 +15538,7 @@ static SINT8 K_PickUp(player_t *player, mobj_t *picked) } S_StartSound(player->mo, sfx_gsha7); + K_TryMoveBackupItem(player); } // ACHTUNG this destroys items when returning true, make sure to bail out @@ -15546,7 +15556,7 @@ boolean K_TryPickMeUp(mobj_t *m1, mobj_t *m2) if (m1->type == MT_PLAYER && m2->type == MT_PLAYER) return false; - CONS_Printf("player check passed\n"); + // CONS_Printf("player check passed\n"); mobj_t *victim = m1; mobj_t *inflictor = m2; @@ -15561,9 +15571,9 @@ boolean K_TryPickMeUp(mobj_t *m1, mobj_t *m2) if (inflictor->target != victim) return false; - CONS_Printf("target check passed\n"); + // CONS_Printf("target check passed\n"); - K_AddHitLag(victim, 2, false); + K_AddHitLag(victim, 3, false); K_PickUp(victim->player, inflictor); P_RemoveMobj(inflictor); diff --git a/src/p_mobj.c b/src/p_mobj.c index ee7a8f5e0..52c6fec00 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -5402,6 +5402,16 @@ static boolean P_IsTrackerType(INT32 type) case MT_GARDENTOP: // Frey return true; + case MT_JAWZ_SHIELD: // Pick-me-up + case MT_ORBINAUT: + case MT_ORBINAUT_SHIELD: + case MT_DROPTARGET: + case MT_DROPTARGET_SHIELD: + case MT_LANDMINE: + case MT_BANANA: + case MT_BANANA_SHIELD: + return true; + default: return false; } From ef853bafc4c6c457b2341cec8acca6fde377118f Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Fri, 16 May 2025 14:47:47 -0400 Subject: [PATCH 20/32] Pick-me-up HUD tracking --- src/k_hud.cpp | 4 ++++ src/k_hud.h | 2 ++ src/k_hud_track.cpp | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+) diff --git a/src/k_hud.cpp b/src/k_hud.cpp index db6d7e701..1c3fc3faa 100644 --- a/src/k_hud.cpp +++ b/src/k_hud.cpp @@ -209,6 +209,7 @@ static patch_t *kp_alagles[10]; static patch_t *kp_blagles[6]; static patch_t *kp_cpu[2]; +patch_t *kp_pickmeup[2]; static patch_t *kp_nametagstem; @@ -867,6 +868,9 @@ void K_LoadKartHUDGraphics(void) HU_UpdatePatch(&kp_cpu[0], "K_CPU1"); HU_UpdatePatch(&kp_cpu[1], "K_CPU2"); + HU_UpdatePatch(&kp_pickmeup[0], "K_PMU1"); + HU_UpdatePatch(&kp_pickmeup[1], "K_PMU2"); + HU_UpdatePatch(&kp_nametagstem, "K_NAMEST"); HU_UpdatePatch(&kp_trickcool[0], "K_COOL1"); diff --git a/src/k_hud.h b/src/k_hud.h index 8fb826f78..8daf58ce1 100644 --- a/src/k_hud.h +++ b/src/k_hud.h @@ -130,6 +130,8 @@ extern patch_t *gen_button_keycenter[2]; extern patch_t *kp_eggnum[6]; extern patch_t *kp_facenum[MAXPLAYERS+1]; +extern patch_t *kp_pickmeup[2]; + extern patch_t *kp_unknownminimap; void K_AddMessage(const char *msg, boolean interrupt, boolean persist); diff --git a/src/k_hud_track.cpp b/src/k_hud_track.cpp index 301f2f52d..2df26bcf3 100644 --- a/src/k_hud_track.cpp +++ b/src/k_hud_track.cpp @@ -278,6 +278,22 @@ private: }}, }; + case MT_JAWZ: + case MT_JAWZ_SHIELD: + case MT_ORBINAUT: + case MT_ORBINAUT_SHIELD: + case MT_DROPTARGET: + case MT_DROPTARGET_SHIELD: + case MT_LANDMINE: + case MT_BANANA: + case MT_BANANA_SHIELD: + return { + { // Near + {2, TICRATE/2, {kp_pickmeup}, 0}, // 1P + {{2, TICRATE/2, {kp_pickmeup}, 0}}, // 4P + }, + }; + default: return { { // Near @@ -874,6 +890,31 @@ void K_drawTargetHUD(const vector3_t* origin, player_t* player) if (tracking) { + fixed_t itemOffset = 36*mobj->scale; + switch (mobj->type) + { + case MT_JAWZ: + case MT_JAWZ_SHIELD: + case MT_ORBINAUT: + case MT_ORBINAUT_SHIELD: + case MT_DROPTARGET: + case MT_DROPTARGET_SHIELD: + case MT_LANDMINE: + case MT_BANANA: + case MT_BANANA_SHIELD: + if (stplyr->mo->eflags & MFE_VERTICALFLIP) + { + pos.z -= itemOffset; + } + else + { + pos.z += itemOffset; + } + break; + default: + break; + } + K_ObjectTracking(&tr.result, &pos, false); targetList.push_back(tr); } From a1c99fa890bbdd3f42a1385fd617519e38ba4508 Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Fri, 16 May 2025 14:58:47 -0400 Subject: [PATCH 21/32] Fix unlobbable eggbox --- src/k_collide.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/k_collide.cpp b/src/k_collide.cpp index 55f7812df..061b25500 100644 --- a/src/k_collide.cpp +++ b/src/k_collide.cpp @@ -167,9 +167,6 @@ boolean K_EggItemCollide(mobj_t *t1, mobj_t *t2) return true; } - if (K_TryPickMeUp(t1, t2)) - return true; - if (t2->player) { if ((t1->target == t2 || t1->target == t2->target) && (t1->threshold > 0)) @@ -178,6 +175,9 @@ boolean K_EggItemCollide(mobj_t *t1, mobj_t *t2) if (t1->health <= 0 || t2->health <= 0) return true; + if (K_TryPickMeUp(t1, t2)) + return true; + if (!P_CanPickupItem(t2->player, 2)) return true; From b62f3c65a8028b7c89d2164247f9c118f5052ab3 Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Fri, 16 May 2025 17:15:50 -0400 Subject: [PATCH 22/32] Pick-me-up HUD --- src/k_hud.cpp | 186 ++++++++++++++++++-------------------------------- 1 file changed, 66 insertions(+), 120 deletions(-) diff --git a/src/k_hud.cpp b/src/k_hud.cpp index 1c3fc3faa..ae686aac4 100644 --- a/src/k_hud.cpp +++ b/src/k_hud.cpp @@ -1894,31 +1894,19 @@ static void K_drawKartItem(void) } // So, like, we've already established that HUD code is unsavable, right? +// == SHITGARBAGE UNLIMITED 3: HACKS GONE WILD == static void K_drawBackupItem(void) { - // ITEM_X = BASEVIDWIDTH-50; // 270 - // ITEM_Y = 24; // 24 - - // Why write V_DrawScaledPatch calls over and over when they're all the same? - // Set to 'no item' just in case. - const UINT8 offset = 1; - patch_t *localpatch[3] = { kp_nodraw, kp_nodraw, kp_nodraw }; - UINT8 localamt[3] = {0, 0, 0}; - patch_t *localbg = ((offset) ? kp_itembg[2] : kp_itembg[0]); - patch_t *localinv = ((offset) ? kp_invincibility[((leveltime % (6*3)) / 3) + 7] : kp_invincibility[(leveltime % (7*3)) / 3]); - INT32 fx = 0, fy = 0, fflags = 0; // final coords for hud and flags... - const INT32 numberdisplaymin = ((!offset && stplyr->itemtype == KITEM_ORBINAUT) ? 5 : 2); + bool tiny = r_splitscreen > 1; + patch_t *localpatch[3] = { kp_nodraw, kp_nodraw, kp_nodraw }; + patch_t *localbg = (kp_itembg[2]); + patch_t *localinv = kp_invincibility[((leveltime % (6*3)) / 3) + 7 + tiny]; + INT32 fx = 0, fy = 0, fflags = 0, tx = 0, ty = 0; // final coords for hud and flags... + const INT32 numberdisplaymin = 2; skincolornum_t localcolor[3] = { static_cast(stplyr->skincolor) }; SINT8 colormode[3] = { TC_RAINBOW }; boolean flipamount = false; // Used for 3P/4P splitscreen to flip item amount stuff - fixed_t rouletteOffset = 0; - fixed_t rouletteSpace = ROULETTE_SPACING; - vector2_t rouletteCrop = {7, 7}; - - boolean flashOnOne = false; - boolean flashOnTwo = false; - if (stplyr->backupitemamount <= 0) return; @@ -1926,22 +1914,22 @@ static void K_drawBackupItem(void) { case KITEM_INVINCIBILITY: localpatch[1] = localinv; - localbg = kp_itembg[offset+1]; + localbg = kp_itembg[2]; break; case KITEM_ORBINAUT: - localpatch[1] = kp_orbinaut[(offset ? 4 : std::min(stplyr->itemamount-1, 3))]; + localpatch[1] = kp_orbinaut[tiny+4]; break; case KITEM_SPB: case KITEM_LIGHTNINGSHIELD: case KITEM_BUBBLESHIELD: case KITEM_FLAMESHIELD: - localbg = kp_itembg[offset+1]; + localbg = kp_itembg[2]; /*FALLTHRU*/ default: - localpatch[1] = K_GetCachedItemPatch(stplyr->backupitemtype, offset); + localpatch[1] = K_GetCachedItemPatch(stplyr->backupitemtype, 1 + tiny); if (localpatch[1] == NULL) localpatch[1] = kp_nodraw; // diagnose underflows @@ -1949,32 +1937,18 @@ static void K_drawBackupItem(void) } // pain and suffering defined below - if (offset) - { - if (!(R_GetViewNumber() & 1)) // If we are P1 or P3... - { - fx = 0; - fy = 0; - fflags = V_SNAPTOLEFT|V_SNAPTOTOP|V_SPLITSCREEN; - } - else // else, that means we're P2 or P4. - { - fx = ITEM2_X*2; - fy = 0; - fflags = V_SNAPTORIGHT|V_SNAPTOTOP|V_SPLITSCREEN; - flipamount = true; - } - - rouletteSpace = ROULETTE_SPACING_SPLITSCREEN; - rouletteOffset = FixedMul(rouletteOffset, FixedDiv(ROULETTE_SPACING_SPLITSCREEN, ROULETTE_SPACING)); - rouletteCrop.x = 16; - rouletteCrop.y = 15; - } - else + if (!(R_GetViewNumber() & 1) || (!tiny)) // If we are P1 or P3... { fx = ITEM_X; fy = ITEM_Y; - fflags = V_SNAPTOTOP|V_SNAPTOLEFT|V_SPLITSCREEN; + fflags = V_SNAPTOLEFT|V_SNAPTOTOP|V_SPLITSCREEN; + } + else // else, that means we're P2 or P4. + { + fx = ITEM2_X; + fy = ITEM2_Y; + fflags = V_SNAPTORIGHT|V_SNAPTOTOP|V_SPLITSCREEN; + flipamount = true; } if (r_splitscreen == 1) @@ -1982,87 +1956,59 @@ static void K_drawBackupItem(void) fy -= 5; } - auto draw_item = [&](fixed_t y, int i) + // final fudge - vegeta 2025 + if (tiny && !(R_GetViewNumber() & 1)) // P1/P3 4P { - const UINT8 *colormap = (localcolor[i] ? R_GetTranslationColormap(colormode[i], localcolor[i], GTC_CACHE) : NULL); + fx += 26; + fy += 5; + tx += 10; + ty += 10; + } + else if (tiny && (R_GetViewNumber() & 1)) // P2/P4 4P + { + fx += -4; + fy += 5; + tx += -5; + ty += 10; + } + else // 1P/2P + { + fx += 30; + fy += -10; + tx += 25; + ty += 30; + } + + boolean transflag = V_HUDTRANS; + + // I feel like the cardinal sin of all evolving HUDcode is, like, assuming the old offsets do something that makes sense. + + if (stplyr->backupitemamount >= numberdisplaymin && stplyr->itemRoulette.active == false) + { + /* + // Then, the numbers: + V_DrawScaledPatch( + fx + (flipamount ? 48 : 0), fy, + V_HUDTRANS|V_SLIDEIN|fflags|(flipamount ? V_FLIP : 0), + kp_itemmulsticker[offset] + ); // flip this graphic for p2 and p4 in split and shift it. + */ + V_DrawFixedPatch( - fx< 1) - { - using srb2::Draw; - Draw( - fx + rouletteCrop.x + FixedToFloat(rouletteSpace/2), - fy + rouletteCrop.y + FixedToFloat(rouletteOffset + y + rouletteSpace) - (r_splitscreen > 1 ? 15 : 33)) - .font(r_splitscreen > 1 ? Draw::Font::kRollingNum4P : Draw::Font::kRollingNum) - .align(Draw::Align::kCenter) - .flags(V_HUDTRANS|V_SLIDEIN|fflags) - .colormap(colormap) - .text("{}", localamt[i]); - } - }; - draw_item(rouletteSpace, 0); - draw_item(-rouletteSpace, 2); - - if (false) - { - // Draw the item underneath the box. - draw_item(0, 1); - V_ClearClipRect(); + V_DrawString(fx+tx, fy+ty, V_HUDTRANS|V_SLIDEIN|fflags, va("x%d", stplyr->backupitemamount)); } else { - // Draw the item above the box. - V_ClearClipRect(); - - boolean transflag = V_HUDTRANS; - - if (cv_reducevfx.value && (flashOnOne || flashOnTwo)) - { - transflag = V_HUDTRANSHALF; - } - - if (stplyr->backupitemamount >= numberdisplaymin && stplyr->itemRoulette.active == false) - { - /* - // Then, the numbers: - V_DrawScaledPatch( - fx + (flipamount ? 48 : 0), fy, - V_HUDTRANS|V_SLIDEIN|fflags|(flipamount ? V_FLIP : 0), - kp_itemmulsticker[offset] - ); // flip this graphic for p2 and p4 in split and shift it. - */ - - V_DrawFixedPatch( - fx<backupitemamount)); - else - V_DrawString(fx+24, fy+31, V_HUDTRANS|V_SLIDEIN|fflags, va("x%d", stplyr->backupitemamount)); - } - else - { - V_DrawScaledPatch(fy+28, fy+41, V_HUDTRANS|V_SLIDEIN|fflags, kp_itemx); - V_DrawTimerString(fx+38, fy+36, V_HUDTRANS|V_SLIDEIN|fflags, va("%d", stplyr->backupitemamount)); - } - } - else - { - V_DrawFixedPatch( - fx< Date: Fri, 16 May 2025 18:11:10 -0400 Subject: [PATCH 23/32] Pickmeup teams --- src/k_hud.cpp | 6 +++--- src/k_hud_track.cpp | 5 ++++- src/k_kart.c | 11 ++++++++++- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/k_hud.cpp b/src/k_hud.cpp index ae686aac4..6e592dfe1 100644 --- a/src/k_hud.cpp +++ b/src/k_hud.cpp @@ -1962,14 +1962,14 @@ static void K_drawBackupItem(void) fx += 26; fy += 5; tx += 10; - ty += 10; + ty += 18; } else if (tiny && (R_GetViewNumber() & 1)) // P2/P4 4P { fx += -4; fy += 5; - tx += -5; - ty += 10; + tx += 1; + ty += 18; } else // 1P/2P { diff --git a/src/k_hud_track.cpp b/src/k_hud_track.cpp index 2df26bcf3..6088ee957 100644 --- a/src/k_hud_track.cpp +++ b/src/k_hud_track.cpp @@ -403,7 +403,10 @@ bool is_object_tracking_target(const mobj_t* mobj) case MT_LANDMINE: case MT_BANANA: case MT_BANANA_SHIELD: - return (mobj->target && !P_MobjWasRemoved(mobj->target) && mobj->target->player && stplyr == mobj->target->player); + return (mobj->target && !P_MobjWasRemoved(mobj->target) && ( + (mobj->target->player && stplyr == mobj->target->player) + || (mobj->target->player && G_SameTeam(stplyr, mobj->target->player)) + ); default: return false; diff --git a/src/k_kart.c b/src/k_kart.c index 7395d9aa1..f18927a36 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -15568,7 +15568,16 @@ boolean K_TryPickMeUp(mobj_t *m1, mobj_t *m2) inflictor = m1; } - if (inflictor->target != victim) + if (!victim->player) + return false; + + boolean allied = (inflictor->target == victim); + + if (!allied) + if (inflictor->target->player && G_SameTeam(inflictor->target->player, victim->player)) + allied = true; + + if (!allied) return false; // CONS_Printf("target check passed\n"); From cd68b1145abedef3babf00006d7561f37272212e Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Fri, 16 May 2025 18:26:11 -0400 Subject: [PATCH 24/32] Holy shit don't email me about CI I'll fix it ok --- src/k_hud_track.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_hud_track.cpp b/src/k_hud_track.cpp index 6088ee957..8574c9159 100644 --- a/src/k_hud_track.cpp +++ b/src/k_hud_track.cpp @@ -406,7 +406,7 @@ bool is_object_tracking_target(const mobj_t* mobj) return (mobj->target && !P_MobjWasRemoved(mobj->target) && ( (mobj->target->player && stplyr == mobj->target->player) || (mobj->target->player && G_SameTeam(stplyr, mobj->target->player)) - ); + )); default: return false; From 7a7b0938ac88cdbd0ddc9a3c4678103515395203 Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Fri, 16 May 2025 21:41:28 -0400 Subject: [PATCH 25/32] Pick-me-up Volt fixes --- src/k_hud_track.cpp | 3 ++- src/k_kart.c | 17 +++++++++++++++-- src/p_mobj.c | 1 + 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/k_hud_track.cpp b/src/k_hud_track.cpp index 8574c9159..c587e33e1 100644 --- a/src/k_hud_track.cpp +++ b/src/k_hud_track.cpp @@ -403,10 +403,11 @@ bool is_object_tracking_target(const mobj_t* mobj) case MT_LANDMINE: case MT_BANANA: case MT_BANANA_SHIELD: + case MT_GACHABOM: return (mobj->target && !P_MobjWasRemoved(mobj->target) && ( (mobj->target->player && stplyr == mobj->target->player) || (mobj->target->player && G_SameTeam(stplyr, mobj->target->player)) - )); + ) && P_CheckSight(stplyr->mo, const_cast(mobj))); default: return false; diff --git a/src/k_kart.c b/src/k_kart.c index f18927a36..723ade47e 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -8352,7 +8352,17 @@ static void K_MoveHeldObjects(player_t *player) // If we can move our backup item into main slots, do so. static void K_TryMoveBackupItem(player_t *player) { - if (player->itemtype == KITEM_NONE && player->backupitemtype) + if (player->itemtype && player->itemtype == player->backupitemtype) + { + player->itemamount += player->backupitemamount; + + player->backupitemtype = 0; + player->backupitemamount = 0; + + S_StartSound(player->mo, sfx_mbs54); + } + + if (player->itemtype == KITEM_NONE && player->backupitemtype && !(player->itemRoulette.active)) { player->itemtype = player->backupitemtype; player->itemamount = player->backupitemamount; @@ -15503,6 +15513,9 @@ static void K_PickUp(player_t *player, mobj_t *picked) case MT_DROPTARGET_SHIELD: type = KITEM_DROPTARGET; break; + case MT_GACHABOM: + type = KITEM_GACHABOM; + break; default: type = KITEM_SAD; break; @@ -15573,7 +15586,7 @@ boolean K_TryPickMeUp(mobj_t *m1, mobj_t *m2) boolean allied = (inflictor->target == victim); - if (!allied) + if (!allied && inflictor->target && !P_MobjWasRemoved(inflictor->target)) if (inflictor->target->player && G_SameTeam(inflictor->target->player, victim->player)) allied = true; diff --git a/src/p_mobj.c b/src/p_mobj.c index 52c6fec00..102a183c9 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -5410,6 +5410,7 @@ static boolean P_IsTrackerType(INT32 type) case MT_LANDMINE: case MT_BANANA: case MT_BANANA_SHIELD: + case MT_GACHABOM: return true; default: From 719841705f401bd9b850a30cc5f08af740f699b3 Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Fri, 16 May 2025 22:18:03 -0400 Subject: [PATCH 26/32] Get that Gunstar sound outta here --- src/k_kart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_kart.c b/src/k_kart.c index 723ade47e..2910fb4c5 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -15550,7 +15550,7 @@ static void K_PickUp(player_t *player, mobj_t *picked) player->backupitemamount = amount; } - S_StartSound(player->mo, sfx_gsha7); + S_StartSound(player->mo, sfx_mbs54); K_TryMoveBackupItem(player); } From 54b34507688bcb343eda8ffaaf420431224c27a4 Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Fri, 16 May 2025 23:21:47 -0400 Subject: [PATCH 27/32] Pickmeup: one last round --- src/k_hud_track.cpp | 2 ++ src/k_kart.c | 2 +- src/sounds.c | 3 +++ src/sounds.h | 3 +++ 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/k_hud_track.cpp b/src/k_hud_track.cpp index c587e33e1..488340360 100644 --- a/src/k_hud_track.cpp +++ b/src/k_hud_track.cpp @@ -287,6 +287,7 @@ private: case MT_LANDMINE: case MT_BANANA: case MT_BANANA_SHIELD: + case MT_GACHABOM: return { { // Near {2, TICRATE/2, {kp_pickmeup}, 0}, // 1P @@ -906,6 +907,7 @@ void K_drawTargetHUD(const vector3_t* origin, player_t* player) case MT_LANDMINE: case MT_BANANA: case MT_BANANA_SHIELD: + case MT_GACHABOM: if (stplyr->mo->eflags & MFE_VERTICALFLIP) { pos.z -= itemOffset; diff --git a/src/k_kart.c b/src/k_kart.c index 9bb27e5b9..c88104ed1 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -15581,7 +15581,7 @@ static void K_PickUp(player_t *player, mobj_t *picked) player->backupitemamount = amount; } - S_StartSound(player->mo, sfx_mbs54); + S_StartSound(player->mo, sfx_aple); K_TryMoveBackupItem(player); } diff --git a/src/sounds.c b/src/sounds.c index 4de9f3119..a14aa31b5 100644 --- a/src/sounds.c +++ b/src/sounds.c @@ -1540,6 +1540,9 @@ sfxinfo_t S_sfx[NUMSFX] = // Walltransfer {"ggfall", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + // :apple: + {"aple", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, + // SRB2kart - Skin sounds {"kwin", false, 64, 96, -1, NULL, 0, SKSKWIN, -1, LUMPERROR, ""}, {"klose", false, 64, 96, -1, NULL, 0, SKSKLOSE, -1, LUMPERROR, ""}, diff --git a/src/sounds.h b/src/sounds.h index 5609710da..90034ba72 100644 --- a/src/sounds.h +++ b/src/sounds.h @@ -1616,6 +1616,9 @@ typedef enum // Walltransfer fuck sfx_ggfall, + // :apple: + sfx_aple, + // And LASTLY, Kart's skin sounds. sfx_kwin, sfx_klose, From 1009996ba47adfde6fa2836f9dc586db3b096afd Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Sat, 17 May 2025 16:54:57 -0400 Subject: [PATCH 28/32] Fix pickmeup bubble shield --- src/k_collide.cpp | 3 --- src/k_hud_track.cpp | 3 +++ src/k_kart.c | 3 +++ src/p_inter.c | 3 +++ 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/k_collide.cpp b/src/k_collide.cpp index 0948b3be1..3d503077f 100644 --- a/src/k_collide.cpp +++ b/src/k_collide.cpp @@ -851,9 +851,6 @@ boolean K_BubbleShieldReflect(mobj_t *t1, mobj_t *t2) boolean K_BubbleShieldCollide(mobj_t *t1, mobj_t *t2) { - if (K_TryPickMeUp(t1, t2)) - return true; - if (t2->type == MT_PLAYER) { // Counter desyncs diff --git a/src/k_hud_track.cpp b/src/k_hud_track.cpp index 488340360..129112e37 100644 --- a/src/k_hud_track.cpp +++ b/src/k_hud_track.cpp @@ -288,6 +288,7 @@ private: case MT_BANANA: case MT_BANANA_SHIELD: case MT_GACHABOM: + case MT_BUBBLESHIELDTRAP: return { { // Near {2, TICRATE/2, {kp_pickmeup}, 0}, // 1P @@ -405,6 +406,7 @@ bool is_object_tracking_target(const mobj_t* mobj) case MT_BANANA: case MT_BANANA_SHIELD: case MT_GACHABOM: + case MT_BUBBLESHIELDTRAP: return (mobj->target && !P_MobjWasRemoved(mobj->target) && ( (mobj->target->player && stplyr == mobj->target->player) || (mobj->target->player && G_SameTeam(stplyr, mobj->target->player)) @@ -908,6 +910,7 @@ void K_drawTargetHUD(const vector3_t* origin, player_t* player) case MT_BANANA: case MT_BANANA_SHIELD: case MT_GACHABOM: + case MT_BUBBLESHIELDTRAP: if (stplyr->mo->eflags & MFE_VERTICALFLIP) { pos.z -= itemOffset; diff --git a/src/k_kart.c b/src/k_kart.c index c88104ed1..b5d879180 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -15547,6 +15547,9 @@ static void K_PickUp(player_t *player, mobj_t *picked) case MT_GACHABOM: type = KITEM_GACHABOM; break; + case MT_BUBBLESHIELDTRAP: + type = KITEM_BUBBLESHIELD; + break; default: type = KITEM_SAD; break; diff --git a/src/p_inter.c b/src/p_inter.c index b15bf8dab..fd30df42a 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -667,6 +667,9 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (!player->mo || player->spectator) return; + if (K_TryPickMeUp(special, toucher)) + return; + // attach to player! P_SetTarget(&special->tracer, toucher); toucher->flags |= MF_NOGRAVITY; From 38a92467ee0f4e8d7caa30bbcbc65e4b85dd3b5c Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Sat, 17 May 2025 20:59:28 -0400 Subject: [PATCH 29/32] Less free rings (resolves #1517) --- src/k_kart.c | 5 ++++- src/k_roulette.c | 2 +- src/p_mobj.c | 1 + 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 90024cd0f..3a8e220e3 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -9436,7 +9436,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) // UINT16 oldringboost = player->ringboost; - if (player->superring == 0) + if (player->superring == 0 || player->stunned) player->ringboost -= max((player->ringboost / roller), 1); else player->ringboost -= min(K_GetFullKartRingPower(player, false) - 1, max(player->ringboost / 2 / roller, 1)); @@ -9723,6 +9723,9 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) UINT8 ringrate = 3 - min(2, (player->superring + existing) / fastringscaler); // Used to consume fat stacks of cash faster. + if (player->stunned) + ringrate = 6; + if (player->nextringaward >= ringrate) { if (player->instaWhipCharge) diff --git a/src/k_roulette.c b/src/k_roulette.c index a20ad1f2c..4056f67d6 100644 --- a/src/k_roulette.c +++ b/src/k_roulette.c @@ -927,7 +927,7 @@ static void K_AddItemToReel(const player_t *player, itemroulette_t *const roulet // If we're in ring debt, pad out the reel with // a BUNCH of Super Rings. if (K_ItemEnabled(KITEM_SUPERRING) == true - && player->rings <= 0 + && player->rings <= -10 && player->position == 1 && (gametyperules & GTR_SPHERES) == 0) { diff --git a/src/p_mobj.c b/src/p_mobj.c index f1f84c444..4aa9ce95a 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -12727,6 +12727,7 @@ static boolean P_AllowMobjSpawn(mapthing_t* mthing, mobjtype_t i) switch (i) { case MT_RING: + case MT_RANDOMITEM: if (modeattacking & ATTACKING_SPB) return false; break; From fc6eb792f30e69c1dcec3fbaf72f7f48cbc5692b Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Sun, 18 May 2025 08:00:58 -0400 Subject: [PATCH 30/32] Pick-me-up review fixups --- src/k_kart.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index b5d879180..ffa5e4625 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -8362,7 +8362,7 @@ static void K_TryMoveBackupItem(player_t *player) S_StartSound(player->mo, sfx_mbs54); } - if (player->itemtype == KITEM_NONE && player->backupitemtype && !(player->itemRoulette.active)) + if (player->itemtype == KITEM_NONE && player->backupitemtype && P_CanPickupItem(player, PICKUP_RINGORSPHERE)) { player->itemtype = player->backupitemtype; player->itemamount = player->backupitemamount; @@ -15511,7 +15511,7 @@ UINT32 K_GetNumGradingPoints(void) return numlaps * (1 + Obj_GetCheckpointCount()); } -static void K_PickUp(player_t *player, mobj_t *picked) +static boolean K_PickUp(player_t *player, mobj_t *picked) { SINT8 type = -1; SINT8 amount = 1; @@ -15550,11 +15550,17 @@ static void K_PickUp(player_t *player, mobj_t *picked) case MT_BUBBLESHIELDTRAP: type = KITEM_BUBBLESHIELD; break; + case MT_SINK: + type = KITEM_KITCHENSINK; + break; default: type = KITEM_SAD; break; } + if (type == KITEM_SAD) + return false; + // CONS_Printf("it %d ia %d t %d a %d\n", player->itemtype, player->itemamount, type, amount); if (player->itemtype == type && player->itemamount && !(player->itemflags & IF_ITEMOUT)) @@ -15586,6 +15592,8 @@ static void K_PickUp(player_t *player, mobj_t *picked) S_StartSound(player->mo, sfx_aple); K_TryMoveBackupItem(player); + + return true; } // ACHTUNG this destroys items when returning true, make sure to bail out @@ -15629,8 +15637,10 @@ boolean K_TryPickMeUp(mobj_t *m1, mobj_t *m2) // CONS_Printf("target check passed\n"); - K_AddHitLag(victim, 3, false); - K_PickUp(victim->player, inflictor); + if (!K_PickUp(victim->player, inflictor)) + return false; + + K_AddHitLag(victim, 3, false); P_RemoveMobj(inflictor); return true; From 0780effcf9aa4b6a967b56ed2473c1ad7dff7bcd Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Sun, 18 May 2025 12:21:21 -0400 Subject: [PATCH 31/32] doy --- src/k_kart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_kart.c b/src/k_kart.c index ffa5e4625..1c3fcefa4 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -8362,7 +8362,7 @@ static void K_TryMoveBackupItem(player_t *player) S_StartSound(player->mo, sfx_mbs54); } - if (player->itemtype == KITEM_NONE && player->backupitemtype && P_CanPickupItem(player, PICKUP_RINGORSPHERE)) + if (player->itemtype == KITEM_NONE && player->backupitemtype && P_CanPickupItem(player, PICKUP_PAPERITEM)) { player->itemtype = player->backupitemtype; player->itemamount = player->backupitemamount; From 7fa5967ccb6155be91b94aa227b25af63b9348b7 Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Sun, 18 May 2025 13:11:35 -0400 Subject: [PATCH 32/32] Pick-me-up: eggmark hud tracking --- src/k_hud_track.cpp | 6 ++++++ src/p_mobj.c | 2 ++ 2 files changed, 8 insertions(+) diff --git a/src/k_hud_track.cpp b/src/k_hud_track.cpp index 129112e37..49abd8a5b 100644 --- a/src/k_hud_track.cpp +++ b/src/k_hud_track.cpp @@ -288,6 +288,8 @@ private: case MT_BANANA: case MT_BANANA_SHIELD: case MT_GACHABOM: + case MT_EGGMANITEM: + case MT_EGGMANITEM_SHIELD: case MT_BUBBLESHIELDTRAP: return { { // Near @@ -407,6 +409,8 @@ bool is_object_tracking_target(const mobj_t* mobj) case MT_BANANA_SHIELD: case MT_GACHABOM: case MT_BUBBLESHIELDTRAP: + case MT_EGGMANITEM: + case MT_EGGMANITEM_SHIELD: return (mobj->target && !P_MobjWasRemoved(mobj->target) && ( (mobj->target->player && stplyr == mobj->target->player) || (mobj->target->player && G_SameTeam(stplyr, mobj->target->player)) @@ -911,6 +915,8 @@ void K_drawTargetHUD(const vector3_t* origin, player_t* player) case MT_BANANA_SHIELD: case MT_GACHABOM: case MT_BUBBLESHIELDTRAP: + case MT_EGGMANITEM: + case MT_EGGMANITEM_SHIELD: if (stplyr->mo->eflags & MFE_VERTICALFLIP) { pos.z -= itemOffset; diff --git a/src/p_mobj.c b/src/p_mobj.c index a38c6651f..5a7a1b873 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -5411,6 +5411,8 @@ static boolean P_IsTrackerType(INT32 type) case MT_BANANA: case MT_BANANA_SHIELD: case MT_GACHABOM: + case MT_EGGMANITEM: + case MT_EGGMANITEM_SHIELD: return true; default: