From da6a0ae48becfd123396ce65cf4a465d869c2fe9 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 25 May 2025 21:47:31 -0700 Subject: [PATCH 01/26] srb2::Vector: fix copy assignment not clearing contents of vector - Test - debugwaypoints - very easily observable, all labels are the same without this fix - ACS text would sometimes be reversed without this fix, not as easily observable --- src/core/vector.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/vector.hpp b/src/core/vector.hpp index e6d66f91f..ec26334ea 100644 --- a/src/core/vector.hpp +++ b/src/core/vector.hpp @@ -149,6 +149,7 @@ public: Vector& operator=(const Vector& rhs) { + clear(); for (auto itr = rhs.begin(); itr != rhs.end(); itr++) { push_back(*itr); From 5e7cce9047e774e2b3ec93c7fce329b6f1528ef1 Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 26 May 2025 15:22:30 +0100 Subject: [PATCH 02/26] CallFunc_SetThingProperty: Get next mobj in TID chain at start of loop In cases where state/property set can cause instant deletion, definitely interrupts FindMobjFromTID iteration after one step and potentially uses after free Also adds comment warnings to this effect near ways to find P_FindMobjFromTID, and updates P_ProcessSpecial even though we could probably stand to rip it out now --- src/acs/call-funcs.cpp | 8 ++++---- src/p_local.h | 3 +++ src/p_mobj.c | 2 ++ src/p_spec.c | 7 ++++++- 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/acs/call-funcs.cpp b/src/acs/call-funcs.cpp index 47502cb0f..8e598bef8 100644 --- a/src/acs/call-funcs.cpp +++ b/src/acs/call-funcs.cpp @@ -3589,13 +3589,15 @@ bool CallFunc_SetThingProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, A INT32 value = 0; tag = argV[0]; - mobj = P_FindMobjFromTID(tag, mobj, info->mo); + mobj_t *next = P_FindMobjFromTID(tag, mobj, info->mo); property = argV[1]; value = argV[2]; - while (mobj != NULL) + while ((mobj = next) != NULL) { + // First in case of deletion. (Can't check for value == S_NULL because of A_ calls, etc) + next = P_FindMobjFromTID(tag, mobj, info->mo); #define PROP_READONLY(x, y) \ case x: \ @@ -3830,8 +3832,6 @@ bool CallFunc_SetThingProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, A } } - mobj = P_FindMobjFromTID(tag, mobj, info->mo); - #undef PROP_FLAGS #undef PROP_SCALE #undef PROP_MOBJ diff --git a/src/p_local.h b/src/p_local.h index 505ccd8e0..af4f722bf 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -612,6 +612,9 @@ void P_InitTIDHash(void); void P_AddThingTID(mobj_t *mo); void P_RemoveThingTID(mobj_t *mo); void P_SetThingTID(mobj_t *mo, mtag_t tid); + +// This function cannot be safely called after *i is removed! +// Please call at start of loops if *i is to be mutated mobj_t *P_FindMobjFromTID(mtag_t tid, mobj_t *i, mobj_t *activator); void P_DeleteMobjStringArgs(mobj_t *mobj); diff --git a/src/p_mobj.c b/src/p_mobj.c index 717cc6cca..98d377637 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -15417,6 +15417,8 @@ void P_SetThingTID(mobj_t *mo, mtag_t tid) // // P_FindMobjFromTID // Mobj tag search function. +// This function cannot be safely called after *i is removed! +// Please call at start of loops if *i is to be mutated // mobj_t *P_FindMobjFromTID(mtag_t tid, mobj_t *i, mobj_t *activator) { diff --git a/src/p_spec.c b/src/p_spec.c index fc91dfad4..eb30cc265 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -3275,8 +3275,13 @@ boolean P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, cha return false; } - while ((targetThing = P_FindMobjFromTID(args[1], targetThing, mo)) != NULL) + mobj_t *next = P_FindMobjFromTID(args[1], targetThing, mo); + + while ((targetThing = next) != NULL) { + // First in case of deletion. (Can't check for state == S_NULL because of A_ calls, etc) + next = P_FindMobjFromTID(args[1], targetThing, mo); + if (targetThing->player != NULL) { continue; From 5ec9d4491566ad43d696adb4afb327d3cd04236f Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Mon, 26 May 2025 15:59:23 -0400 Subject: [PATCH 03/26] Margin Boost refinements --- src/k_hud.cpp | 22 +++++++++++++++++++--- src/k_kart.c | 10 +++++++++- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/k_hud.cpp b/src/k_hud.cpp index 0fe61fafa..56875b8d7 100644 --- a/src/k_hud.cpp +++ b/src/k_hud.cpp @@ -3289,8 +3289,6 @@ static void K_drawKartDuelScores(void) INT32 youfill = skincolors[stplyr->skincolor].ramp[ri]; INT32 foefill = skincolors[foe->skincolor].ramp[ri]; - INT32 margin = std::min(overtimecheckpoints, (UINT8)5); // Absolutely what the fuck kind of cast. - V_DrawScaledPatch(basex, basey, flags, kp_duel_sticker); INT32 scoredelta = stplyr->duelscore - foe->duelscore; @@ -3397,7 +3395,25 @@ static void K_drawKartDuelScores(void) V_DrawMappedPatch(drawx+xoff, drawy+yoff, flags|flipflag, faceprefix[workingskin][FACE_RANK], colormap); } - V_DrawScaledPatch(basex, basey, flags, kp_duel_margin[margin]); + UINT8 MARGINLEVELS = 6; + INT32 marginx = 0; + INT32 marginoffset = 6; + + INT32 margin = 1 + overtimecheckpoints; + // margin = ((leveltime/10)%25)+1; // debug + + INT32 margindigits = 1 + (margin-1)/MARGINLEVELS; + + marginx -= (margindigits-1) * (marginoffset/2); + + while (margindigits) + { + V_DrawScaledPatch(basex + marginx, basey, flags, kp_duel_margin[std::min(margin-1, MARGINLEVELS-1)]); + + margindigits--; + margin -= MARGINLEVELS; + marginx += marginoffset; + } } static INT32 easedallyscore = 0; diff --git a/src/k_kart.c b/src/k_kart.c index d3a183097..2fb84c986 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -497,7 +497,7 @@ fixed_t K_GetKartGameSpeedScalar(SINT8 value) value = 3; fixed_t base = ((13 + (3*value)) << FRACBITS) / 16; - fixed_t duel = overtimecheckpoints*(1<drift != 0 && P_IsObjectOnGround(player->mo)) { if (G_CompatLevel(0x000A)) From 29234d8f20f0450c153e1711c1a83f27e99d3aa7 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 26 May 2025 14:45:55 -0700 Subject: [PATCH 04/26] Add A_GenericBumper, hardcoded generic version of some bumper object scripts --- src/deh_tables.c | 1 + src/info.h | 2 ++ src/p_enemy.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++ src/p_map.c | 14 ++++++++++++- 4 files changed, 68 insertions(+), 1 deletion(-) diff --git a/src/deh_tables.c b/src/deh_tables.c index 2d92aac06..e115287c8 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -297,6 +297,7 @@ actionpointer_t actionpointers[] = {{A_MakeSSCandle}, "A_MAKESSCANDLE"}, {{A_HologramRandomTranslucency}, "A_HOLOGRAMRANDOMTRANSLUCENCY"}, {{A_SSChainShatter}, "A_SSCHAINSHATTER"}, + {{A_GenericBumper}, "A_GENERICBUMPER"}, {{NULL}, "NONE"}, diff --git a/src/info.h b/src/info.h index d7495e852..2be47f164 100644 --- a/src/info.h +++ b/src/info.h @@ -289,6 +289,7 @@ enum actionnum A_MAKESSCANDLE, A_HOLOGRAMRANDOMTRANSLUCENCY, A_SSCHAINSHATTER, + A_GENERICBUMPER, NUMACTIONS }; @@ -557,6 +558,7 @@ void A_BlendEyePuyoHack(); void A_MakeSSCandle(); void A_HologramRandomTranslucency(); void A_SSChainShatter(); +void A_GenericBumper(); extern boolean actionsoverridden[NUMACTIONS]; diff --git a/src/p_enemy.c b/src/p_enemy.c index de957c0c4..3006b7369 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -321,6 +321,7 @@ void A_BlendEyePuyoHack(mobj_t *actor); void A_MakeSSCandle(mobj_t *actor); void A_HologramRandomTranslucency(mobj_t *actor); void A_SSChainShatter(mobj_t *actor); +void A_GenericBumper(mobj_t *actor); //for p_enemy.c @@ -12668,3 +12669,54 @@ void A_SSChainShatter(mobj_t* actor) actor->fuse = 1; } + +// var1 = If -1, triggered by collision event +// var2 = Strength value +// +// mobjinfo dependencies: +// - deathsound - bumper noise +// - seestate - bumper flashing state +// +void A_GenericBumper(mobj_t* actor) +{ + if (var1 != -1) + return; + + mobj_t *other = actor->target; + + if (!other) + return; + + // This code was ported from Lua + // Original was Balloon Park's bumpers? + INT32 hang = R_PointToAngle2( + actor->x, actor->y, + other->x, other->y + ); + + INT32 vang = 0; + + if (!P_IsObjectOnGround(other)) + { + vang = R_PointToAngle2( + FixedHypot(actor->x, actor->y), actor->z + (actor->height / 2), + FixedHypot(other->x, other->y), other->z + (other->height / 2) + ); + } + + INT32 baseStrength = abs(astate->var2); + fixed_t strength = (baseStrength * actor->scale) / 2; + + other->momx = FixedMul(FixedMul(strength, FCOS(hang)), abs(FCOS(vang))); + other->momy = FixedMul(FixedMul(strength, FSIN(hang)), abs(FCOS(vang))); + other->momz = FixedMul(strength, FSIN(vang)); + + if (other->player) + K_SetTireGrease(other->player, max(other->player->tiregrease, 2*TICRATE)); + + if (actor->state != &states[actor->info->seestate]) + { + S_StartSound(actor, actor->info->deathsound); + P_SetMobjState(actor, actor->info->seestate); + } +} diff --git a/src/p_map.c b/src/p_map.c index bdec595d7..07090967f 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -1552,7 +1552,19 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing) if (!K_PuntCollide(thing, g_tm.thing)) { - K_KartSolidBounce(g_tm.thing, thing); + state_t *st = &states[thing->info->spawnstate]; + + if (st->action.acp1 == A_GenericBumper) + { + P_SetTarget(&thing->target, g_tm.thing); + + var1 = -1; + var2 = 0; + astate = st; + st->action.acp1(thing); + } + else + K_KartSolidBounce(g_tm.thing, thing); } return BMIT_CONTINUE; } From 465387c69e2966698485f8860070a75ef12106a3 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 26 May 2025 14:56:44 -0700 Subject: [PATCH 05/26] MT_SEALEDSTAR_BUMPER: use A_GenericBumper --- src/info.c | 4 +-- src/k_objects.h | 1 - src/objects/sealed-star.c | 52 --------------------------------------- src/p_inter.c | 4 --- 4 files changed, 2 insertions(+), 59 deletions(-) diff --git a/src/info.c b/src/info.c index 59c252880..d017dda40 100644 --- a/src/info.c +++ b/src/info.c @@ -3640,7 +3640,7 @@ state_t states[NUMSTATES] = {SPR_S_SP, FF_ANIMATE|FF_SEMIBRIGHT, -1, {NULL}, 3, 2, S_NULL}, // S_SLSTMACE // MT_SEALEDSTAR_BUMPER - {SPR_SBMP, 0|FF_FULLBRIGHT, -1, {NULL}, 2, 8, S_SEALEDSTAR_BUMPER}, // S_SEALEDSTAR_BUMPER + {SPR_SBMP, 0|FF_FULLBRIGHT, -1, {A_GenericBumper}, 0, 56, S_SEALEDSTAR_BUMPER}, // S_SEALEDSTAR_BUMPER {SPR_SBMP, 1|FF_ANIMATE|FF_FULLBRIGHT, 8, {NULL}, 1, 2, S_SEALEDSTAR_BUMPER}, // S_SEALEDSTAR_BUMPERHIT // MT_SSCHAIN_SPAWNER @@ -22233,7 +22233,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 0, // mass 0, // damage sfx_None, // activesound - MF_SPECIAL|MF_NOGRAVITY, // flags + MF_NOGRAVITY|MF_SOLID, // flags S_NULL // raisestate }, { // MT_SSCHAIN_SPAWNER diff --git a/src/k_objects.h b/src/k_objects.h index cbdb189a7..c6cc64db9 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -411,7 +411,6 @@ void Obj_SSGobletMobjThink(mobj_t* mo); void Obj_SSLampMapThingSpawn(mobj_t* mo, mapthing_t* mt); void Obj_SSWindowMapThingSpawn(mobj_t* mo, mapthing_t* mt); void Obj_SLSTMaceMobjThink(mobj_t* mo); -void Obj_SSBumperTouchSpecial(mobj_t* special, mobj_t* toucher); void Obj_SSBumperMobjSpawn(mobj_t* mo); void Obj_SSChainMobjThink(mobj_t* mo); void Obj_SSGachaTargetMobjSpawn(mobj_t* mo); diff --git a/src/objects/sealed-star.c b/src/objects/sealed-star.c index ccf136800..2ee16a6fd 100644 --- a/src/objects/sealed-star.c +++ b/src/objects/sealed-star.c @@ -542,58 +542,6 @@ void Obj_SLSTMaceMobjThink(mobj_t* mo) } } -#define BUMPER_STRENGTH (56) - -void Obj_SSBumperTouchSpecial(mobj_t* special, mobj_t* toucher) -{ - angle_t hang; - angle_t vang; - fixed_t str; - int i; - - hang = R_PointToAngle2(special->x, special->y, toucher->x, toucher->y); - vang = 0; - - if (P_IsObjectOnGround(toucher) == false) - { - vang = R_PointToAngle2( - FixedHypot(special->x, special->y), special->z + (special->height >> 1), - FixedHypot(toucher->x, toucher->y), toucher->z + (toucher->height >> 1) - ); - } - - str = (BUMPER_STRENGTH * special->scale) >> 1; - - toucher->momx = FixedMul(FixedMul(str, FCOS(hang)), abs(FCOS(vang))); - toucher->momy = FixedMul(FixedMul(str, FSIN(hang)), abs(FCOS(vang))); - toucher->momz = FixedMul(str, FSIN(vang)); - - if (toucher->player) - { - if (toucher->player->tiregrease == 0) - { - for (i = 0; i < 2; i++) - { - mobj_t *grease = P_SpawnMobjFromMobj(toucher, 0, 0, 0, MT_TIREGREASE); - P_SetTarget(&grease->target, toucher); - grease->angle = toucher->angle; - grease->extravalue1 = i; - } - } - - if (toucher->player->tiregrease < 2*TICRATE) // greasetics - { - toucher->player->tiregrease = 2*TICRATE; - } - } - - if (special->state != &states[special->info->seestate]) - { - S_StartSound(special, special->info->deathsound); - P_SetMobjState(special, special->info->seestate); - } -} - void Obj_SSBumperMobjSpawn(mobj_t* mo) { mo->shadowscale = FRACUNIT; diff --git a/src/p_inter.c b/src/p_inter.c index 2df953dec..ff2b90ed1 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1083,10 +1083,6 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) Obj_TrickBalloonTouchSpecial(special, toucher); return; - case MT_SEALEDSTAR_BUMPER: - Obj_SSBumperTouchSpecial(special, toucher); - return; - case MT_PULLUPHOOK: Obj_PulleyHookTouch(special, toucher); return; From 0ae5ca334135eb1876c39213787c1e543e36ec14 Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Tue, 27 May 2025 03:38:04 -0400 Subject: [PATCH 06/26] Fix weird inconsistent initialization of cangrabitems maybe??? I took a shower and I want to go to bed --- src/g_game.c | 11 ++--------- src/k_kart.c | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index df5595b99..6498d417a 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2352,8 +2352,6 @@ 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; @@ -2439,13 +2437,6 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) tallyactive = false; cangrabitems = 0; - if (gametyperules & GTR_SPHERES - || gametyperules & GTR_CATCHER - || G_TimeAttackStart() - || gametype == GT_TUTORIAL - || !M_NotFreePlay() - || K_GetNumWaypoints() == 0) - cangrabitems = EARLY_ITEM_FLICKER; } else { @@ -2501,6 +2492,8 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) { tally = players[player].tally; } + + cangrabitems = players[player].cangrabitems; } spectatorReentry = (betweenmaps ? 0 : players[player].spectatorReentry); diff --git a/src/k_kart.c b/src/k_kart.c index 2fb84c986..566bcf17e 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -9781,6 +9781,21 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) player->invincibilitytimer--; } + // The precise ordering of start-of-level made me want to cut my head off, + // so let's try this instead. Whatever! + if (leveltime < introtime) + { + if ((gametyperules & GTR_SPHERES) + || (gametyperules & GTR_CATCHER) + || G_TimeAttackStart() + || (gametype == GT_TUTORIAL) + || !M_NotFreePlay() + || (K_GetNumWaypoints() == 0)) + player->cangrabitems = EARLY_ITEM_FLICKER; + else + player->cangrabitems = 0; + } + if (player->cangrabitems && player->cangrabitems <= EARLY_ITEM_FLICKER) player->cangrabitems++; From 9dfdacb7c569bded57c46a34f71833716d5b0ff4 Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Tue, 27 May 2025 15:00:32 -0400 Subject: [PATCH 07/26] Always update match stats even in PWRLV_DISABLED --- src/y_inter.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/y_inter.cpp b/src/y_inter.cpp index 6d07a8aea..b2159285a 100644 --- a/src/y_inter.cpp +++ b/src/y_inter.cpp @@ -2544,9 +2544,10 @@ void Y_StartIntermission(void) } K_CashInPowerLevels(); - SV_BumpMatchStats(); } + SV_BumpMatchStats(); + if (!timer) { Y_EndIntermission(); From e0ec684539d4a5e380840dcc30a42c7fe97f3aa1 Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Tue, 27 May 2025 15:36:11 -0400 Subject: [PATCH 08/26] Scale flame shield to player when ticking --- src/objects/flame-shield.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/objects/flame-shield.cpp b/src/objects/flame-shield.cpp index bed96c97a..df8d5b255 100644 --- a/src/objects/flame-shield.cpp +++ b/src/objects/flame-shield.cpp @@ -60,6 +60,7 @@ struct Visual : Mobj } move_origin(shield()->pos()); + scale(5 * shield()->follow()->scale() / 4); renderflags = (renderflags & ~RF_DONTDRAW) | (shield()->state()->num() == S_INVISIBLE ? 0 : RF_DONTDRAW); From 2ba7310a1000775d19a04c93d993b18be879f048 Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Tue, 27 May 2025 16:16:44 -0400 Subject: [PATCH 09/26] EAT SHIT --- 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 566bcf17e..c453f6558 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -9783,7 +9783,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) // The precise ordering of start-of-level made me want to cut my head off, // so let's try this instead. Whatever! - if (leveltime < introtime) + if (leveltime <= introtime) { if ((gametyperules & GTR_SPHERES) || (gametyperules & GTR_CATCHER) From 38e4fcf911a413084d385576ac0a84533f6c470c Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Tue, 27 May 2025 16:25:33 -0400 Subject: [PATCH 10/26] Fix lightning while I'm here --- src/objects/lightning-shield.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/objects/lightning-shield.cpp b/src/objects/lightning-shield.cpp index e08e4529c..3c80e1b70 100644 --- a/src/objects/lightning-shield.cpp +++ b/src/objects/lightning-shield.cpp @@ -55,6 +55,7 @@ struct Visual : Mobj } move_origin(shield()->pos()); + scale(5 * shield()->follow()->scale() / 4); dispoffset = state()->num() == S_THNB1 ? -1 : 1; return true; From 628d9cd1271e7e2fd0d5e119782ebda64da2fbbb Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Tue, 27 May 2025 20:46:37 -0400 Subject: [PATCH 11/26] Height check Lightning Shield attack --- src/k_collide.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/k_collide.cpp b/src/k_collide.cpp index 4e1a3451b..0cc1d2cc9 100644 --- a/src/k_collide.cpp +++ b/src/k_collide.cpp @@ -777,6 +777,12 @@ static inline BlockItReturn_t PIT_LightningShieldAttack(mobj_t *thing) return BMIT_CONTINUE; } + // see if it went over / under + if (lightningSource->z > thing->z + thing->height) + return BMIT_CONTINUE; // overhead + if (lightningSource->z + lightningSource->height < thing->z) + return BMIT_CONTINUE; // underneath + #if 0 if (P_CheckSight(lightningSource, thing) == false) { From ea7a7ea7e6fa440499616d59b6d8c2f23d17c019 Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Tue, 27 May 2025 20:54:45 -0400 Subject: [PATCH 12/26] Allow viewing enemy team in replays --- src/g_game.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/g_game.c b/src/g_game.c index df5595b99..e5d96a138 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1556,7 +1556,7 @@ boolean G_CouldView(INT32 playernum) return false; // SRB2Kart: we have no team-based modes, YET... - if (G_GametypeHasTeams()) + if (G_GametypeHasTeams() && !demo.playback) { if (players[consoleplayer].spectator == false && player->team != players[consoleplayer].team) return false; From a333806da4efca654f4bf5a0e97338d620b6cbc0 Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Tue, 27 May 2025 21:27:55 -0400 Subject: [PATCH 13/26] Fix 4P accessibility icons (close enough) --- src/k_hud.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/k_hud.cpp b/src/k_hud.cpp index 56875b8d7..c134279fa 100644 --- a/src/k_hud.cpp +++ b/src/k_hud.cpp @@ -3718,6 +3718,11 @@ static void K_drawKartTeamScores(void) */ } +static boolean K_DrawingLaps() +{ + return (numlaps != 1 && !K_InRaceDuel() && (UINT16)stplyr->exp != UINT16_MAX); +} + static boolean K_drawKartLaps(void) { INT32 splitflags = V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_SPLITSCREEN; @@ -4157,7 +4162,16 @@ static void K_drawKartAccessibilityIcons(boolean gametypeinfoshown, INT32 fx) } else { - fx = LAPS_X+44; + fx = LAPS_X + 33; + + if ((gametyperules & (GTR_BUMPERS|GTR_CIRCUIT)) == GTR_BUMPERS) + fx -= 14; + + if (K_DrawingLaps()) + { + fx += 30; + } + fy = LAPS_Y; if (R_GetViewNumber() & 1) // If we are not P1 or P3... { From 7ea9945769238ac406839881aa82dcf08b3e4650 Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Tue, 27 May 2025 21:39:25 -0400 Subject: [PATCH 14/26] Cubic Lightning --- src/k_collide.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/k_collide.cpp b/src/k_collide.cpp index 0cc1d2cc9..4abe3f269 100644 --- a/src/k_collide.cpp +++ b/src/k_collide.cpp @@ -778,9 +778,9 @@ static inline BlockItReturn_t PIT_LightningShieldAttack(mobj_t *thing) } // see if it went over / under - if (lightningSource->z > thing->z + thing->height) + if (lightningSource->z - lightningDist > thing->z + thing->height) return BMIT_CONTINUE; // overhead - if (lightningSource->z + lightningSource->height < thing->z) + if (lightningSource->z + lightningSource->height + lightningDist < thing->z) return BMIT_CONTINUE; // underneath #if 0 From 045d561e4b3fce8f796c348950083fdf5f3c9a4a Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Tue, 27 May 2025 23:52:26 -0400 Subject: [PATCH 15/26] Fix zero division in unusual teams situations --- src/k_hud.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/k_hud.cpp b/src/k_hud.cpp index 56875b8d7..976d74c00 100644 --- a/src/k_hud.cpp +++ b/src/k_hud.cpp @@ -3496,6 +3496,9 @@ static void K_drawKartTeamScores(void) UINT16 enemyscore = g_teamscores[enemies]; UINT16 totalscore = allyscore + enemyscore; + if (totalscore == 0) + return; + using srb2::Draw; srb2::Draw::Font scorefont = Draw::Font::kTimer; @@ -3551,7 +3554,7 @@ static void K_drawKartTeamScores(void) { INT32 delta = abs(easedallyscore - allyscore); // how wrong is display score? - if (scorechangecooldown == 0) + if (scorechangecooldown == 0 && delta) { if (allyscore > easedallyscore) { From e75ffd0707156201c0cfc3d1727d7c41da4d2c41 Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Wed, 28 May 2025 15:20:06 -0400 Subject: [PATCH 16/26] Save team assignments in demo (DEMO BREAKER) --- src/g_demo.cpp | 6 ++++++ src/g_game.c | 2 ++ 2 files changed, 8 insertions(+) diff --git a/src/g_demo.cpp b/src/g_demo.cpp index 88828fee6..e60e1e759 100644 --- a/src/g_demo.cpp +++ b/src/g_demo.cpp @@ -2368,6 +2368,8 @@ void G_BeginRecording(void) WRITEUINT8(demobuf.p, player->skin); WRITEUINT8(demobuf.p, player->lastfakeskin); + WRITEUINT8(demobuf.p, player->team); + // Color demobuf.p += copy_fixed_buf(demobuf.p, skincolors[player->skincolor].name, g_buffer_sizes.color_name); @@ -3500,6 +3502,8 @@ void G_DoPlayDemoEx(const char *defdemoname, lumpnum_t deflumpnum) demo.currentskinid[p] = 0; lastfakeskin[p] = READUINT8(demobuf.p); + players[p].team = READUINT8(demobuf.p); + // Color demobuf.p += copy_fixed_buf(color, demobuf.p, g_buffer_sizes.color_name); for (i = 0; i < numskincolors; i++) @@ -3783,6 +3787,8 @@ void G_AddGhost(savebuffer_t *buffer, const char *defdemoname) ghskin = &skins[skinlist[i].mapping]; p++; // lastfakeskin + p++; // team + // Color p += copy_fixed_buf(color, p, ghostsizes.color_name); diff --git a/src/g_game.c b/src/g_game.c index 2287ea3fa..1dedf3833 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -5419,6 +5419,8 @@ void G_InitNew(UINT8 pencoremode, INT32 map, boolean resetplayer, boolean skippr players[i].xtralife = 0; players[i].totalring = 0; players[i].score = 0; + if (roundqueue.position == 0) // Don't unassign teams in tournament play + players[i].team = TEAM_UNASSIGNED; } if (resetplayer || !(gametyperules & GTR_CHECKPOINTS && map == gamemap)) From e6de2e73d808efeae09a29f0d997d940021c0724 Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Wed, 28 May 2025 15:30:25 -0400 Subject: [PATCH 17/26] I'M TIRED OF YOU --- 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 c453f6558..fc6a42ae2 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -9783,7 +9783,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) // The precise ordering of start-of-level made me want to cut my head off, // so let's try this instead. Whatever! - if (leveltime <= introtime) + if (leveltime <= starttime || player->gradingpointnum == 0) { if ((gametyperules & GTR_SPHERES) || (gametyperules & GTR_CATCHER) From 5d4dc6e29713d6e83b1fbee68f377b416ed13c05 Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Wed, 28 May 2025 17:20:05 -0400 Subject: [PATCH 18/26] Don't draw screen edge arrows for pickmeups --- src/k_hud_track.cpp | 68 +++++++++++++++------------------------------ src/k_kart.c | 23 +++++++++++++++ src/k_kart.h | 2 ++ 3 files changed, 47 insertions(+), 46 deletions(-) diff --git a/src/k_hud_track.cpp b/src/k_hud_track.cpp index 49abd8a5b..0ac07e641 100644 --- a/src/k_hud_track.cpp +++ b/src/k_hud_track.cpp @@ -143,6 +143,8 @@ struct TargetTracking return false; default: + if (K_IsPickMeUpItem(mobj->type)) + return false; return true; } } @@ -277,28 +279,17 @@ private: {{6, 2, {kp_spraycantarget_far[1]}, V_ADD}}, // 4P }}, }; - - 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: - case MT_GACHABOM: - case MT_EGGMANITEM: - case MT_EGGMANITEM_SHIELD: - case MT_BUBBLESHIELDTRAP: - return { - { // Near - {2, TICRATE/2, {kp_pickmeup}, 0}, // 1P - {{2, TICRATE/2, {kp_pickmeup}, 0}}, // 4P - }, - }; - default: + if (K_IsPickMeUpItem(mobj->type)) + { + return { + { // Near + {2, TICRATE/2, {kp_pickmeup}, 0}, // 1P + {{2, TICRATE/2, {kp_pickmeup}, 0}}, // 4P + }, + }; + } + return { { // Near {8, 2, {kp_capsuletarget_near[0]}}, // 1P @@ -902,32 +893,17 @@ void K_drawTargetHUD(const vector3_t* origin, player_t* player) if (tracking) { fixed_t itemOffset = 36*mobj->scale; - switch (mobj->type) + + if (K_IsPickMeUpItem(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: - case MT_GACHABOM: - case MT_BUBBLESHIELDTRAP: - case MT_EGGMANITEM: - case MT_EGGMANITEM_SHIELD: - if (stplyr->mo->eflags & MFE_VERTICALFLIP) - { - pos.z -= itemOffset; - } - else - { - pos.z += itemOffset; - } - break; - default: - break; + if (stplyr->mo->eflags & MFE_VERTICALFLIP) + { + pos.z -= itemOffset; + } + else + { + pos.z += itemOffset; + } } K_ObjectTracking(&tr.result, &pos, false); diff --git a/src/k_kart.c b/src/k_kart.c index c453f6558..dc22b0694 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -15795,6 +15795,29 @@ void K_BotHitPenalty(player_t *player) } } +boolean K_IsPickMeUpItem(mobjtype_t type) +{ + switch (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: + case MT_GACHABOM: + case MT_EGGMANITEM: + case MT_EGGMANITEM_SHIELD: + case MT_BUBBLESHIELDTRAP: + return true; + default: + return false; + } +} + static boolean K_PickUp(player_t *player, mobj_t *picked) { SINT8 type = -1; diff --git a/src/k_kart.h b/src/k_kart.h index 5c36fcd26..762ab997a 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -325,6 +325,8 @@ boolean K_LegacyRingboost(player_t *player); void K_BotHitPenalty(player_t *player); +boolean K_IsPickMeUpItem(mobjtype_t type); + boolean K_TryPickMeUp(mobj_t *m1, mobj_t *m2); fixed_t K_TeamComebackMultiplier(player_t *player); From 4bcf0e29243b544f5cb3480e9762a0e09597b72f Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Thu, 29 May 2025 11:57:44 -0400 Subject: [PATCH 19/26] Snap MessageFeed --- src/k_hud.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/k_hud.cpp b/src/k_hud.cpp index 229438d18..700fdb3f4 100644 --- a/src/k_hud.cpp +++ b/src/k_hud.cpp @@ -7146,35 +7146,39 @@ static void K_DrawMessageFeed(void) text.font(Draw::Font::kMenu); - UINT8 x = BASEVIDWIDTH/2; - UINT8 y = 10; + UINT32 vw = vid.width / vid.dupx; + UINT32 vh = vid.height / vid.dupy; + + UINT32 x = vw / 2; + UINT32 y = 10; + SINT8 shift = 0; if (r_splitscreen >= 2) { text.font(Draw::Font::kThin); shift = -2; - x = BASEVIDWIDTH/4; + x = vw/4; y = 5; if (i % 2) - x += BASEVIDWIDTH/2; + x += vw / 2; if (i >= 2) - y += BASEVIDHEIGHT / 2; + y += vh / 2; } else if (r_splitscreen >= 1) { y = 5; if (i >= 1) - y += BASEVIDHEIGHT / 2; + y += vh / 2; } UINT16 sw = text.width(); - K_DrawSticker(x - sw/2, y, sw, 0, true); - Draw(x, y+shift).align(Draw::Align::kCenter).text(text); + K_DrawSticker(x - sw/2, y, sw, V_SNAPTOTOP|V_SNAPTOLEFT, true); + Draw(x, y+shift).align(Draw::Align::kCenter).flags(V_SNAPTOTOP|V_SNAPTOLEFT).text(text); } } From 0aa4c3f4725be14d6eb2b50eacb988bea1aa86be Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Thu, 29 May 2025 13:47:58 -0400 Subject: [PATCH 20/26] WIP - Puyo-like margin icon stacking --- src/k_hud.cpp | 48 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/src/k_hud.cpp b/src/k_hud.cpp index 229438d18..9f06e69ea 100644 --- a/src/k_hud.cpp +++ b/src/k_hud.cpp @@ -3395,25 +3395,47 @@ static void K_drawKartDuelScores(void) V_DrawMappedPatch(drawx+xoff, drawy+yoff, flags|flipflag, faceprefix[workingskin][FACE_RANK], colormap); } - UINT8 MARGINLEVELS = 6; - INT32 marginx = 0; - INT32 marginoffset = 6; + #define MARGINLEVELS (6) - INT32 margin = 1 + overtimecheckpoints; - // margin = ((leveltime/10)%25)+1; // debug + INT32 marginvalues[MARGINLEVELS] = {1, 5, 7, 9, 11, 13}; - INT32 margindigits = 1 + (margin-1)/MARGINLEVELS; + INT32 margindigits[20]; + memset(margindigits, -1, sizeof(margindigits)); - marginx -= (margindigits-1) * (marginoffset/2); + INT32 nummargindigits = 0; - while (margindigits) + INT32 margin = overtimecheckpoints; + margin = ((leveltime/20)%50)+1; // debug + + if (margin == 0) + return; + + while (margin) { - V_DrawScaledPatch(basex + marginx, basey, flags, kp_duel_margin[std::min(margin-1, MARGINLEVELS-1)]); - - margindigits--; - margin -= MARGINLEVELS; - marginx += marginoffset; + UINT32 significant_margin = 0; + for (UINT8 i = MARGINLEVELS-1; i >= 0; i--) + { + if (margin >= marginvalues[i]) + { + significant_margin = i; + break; + } + } + margindigits[nummargindigits] = significant_margin; + nummargindigits++; + margin -= marginvalues[significant_margin]; } + + INT32 marginoffset = 6; + INT32 marginx = ((nummargindigits-1) * marginoffset)/2; + + for (INT32 i = nummargindigits - 1; i >= 0; i--) + { + V_DrawScaledPatch(basex + marginx, basey, flags, kp_duel_margin[margindigits[i]]); + marginx -= marginoffset; + } + + #undef MARGINLEVELS } static INT32 easedallyscore = 0; From 55a8a5307736077259fd1adece405a77a6bfa59f Mon Sep 17 00:00:00 2001 From: Eidolon Date: Thu, 29 May 2025 15:18:58 -0500 Subject: [PATCH 21/26] Turn on and off microphone explicitly On most devices these days, there is a mandatory microphone indicator when an input device is being used. Moreover, on macOS and some Linux distros, the user will be prompted to grant permission to the game for microphone access. To ensure we're playing nicely with these expectations, instead of just leaving the device on at all times on first use, close and reopen the device as sound input is needed. --- src/d_clisrv.c | 13 +++++++++---- src/menus/options-voice.cpp | 22 ++++++++++++++++++++-- src/sdl/new_sound.cpp | 17 ++++++----------- 3 files changed, 35 insertions(+), 17 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 33e9dceaa..def543d63 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -281,7 +281,7 @@ shouldsign_t ShouldSignChallenge(uint8_t *message) if ((max(now, then) - min(now, then)) > 60*15) return SIGN_BADTIME; - // ____ _____ ___ ____ _ + // ____ _____ ___ ____ _ // / ___|_ _/ _ \| _ \| | // \___ \ | || | | | |_) | | // ___) || || |_| | __/|_| @@ -2436,6 +2436,11 @@ static void CL_ConnectToServer(void) joinedIP[0] = '\0'; // And empty this for good measure regardless of whether or not we actually used it. + // Enable sound input/microphone in netgames, activating the microphone device. + if (netgame) + { + S_SoundInputSetEnabled(true); + } } static void Command_connect(void) @@ -2502,6 +2507,8 @@ static void Command_connect(void) { CONS_Alert(CONS_ERROR, M_GetText("There is no server identification with this network driver\n")); D_CloseConnection(); + + S_SoundInputSetEnabled(false); return; } } @@ -3659,6 +3666,7 @@ void D_QuitNetGame(void) K_ClearClientPowerLevels(); G_ObliterateParties(); K_ResetMidVote(); + S_SoundInputSetEnabled(false); DEBFILE("===========================================================================\n" " Log finish\n" @@ -7376,9 +7384,6 @@ void NetVoiceUpdate(void) return; } - // This necessarily runs every frame, not every tic - S_SoundInputSetEnabled(true); - UINT32 bytes_dequed = 0; do { diff --git a/src/menus/options-voice.cpp b/src/menus/options-voice.cpp index 8f02fad5f..7f3a8ba00 100644 --- a/src/menus/options-voice.cpp +++ b/src/menus/options-voice.cpp @@ -72,6 +72,24 @@ static boolean input_routine(INT32) return false; } +static void init_routine(void) +{ + if (!netgame) + { + S_SoundInputSetEnabled(true); + } +} + +static boolean quit_routine(void) +{ + if (!netgame) + { + S_SoundInputSetEnabled(false); + } + + return true; +} + menu_t OPTIONS_VoiceDef = { sizeof (OPTIONS_Voice) / sizeof (menuitem_t), &OPTIONS_MainDef, @@ -85,7 +103,7 @@ menu_t OPTIONS_VoiceDef = { draw_routine, M_DrawOptionsCogs, tick_routine, - NULL, - NULL, + init_routine, + quit_routine, input_routine, }; diff --git a/src/sdl/new_sound.cpp b/src/sdl/new_sound.cpp index 3ded71879..1e1d93797 100644 --- a/src/sdl/new_sound.cpp +++ b/src/sdl/new_sound.cpp @@ -198,7 +198,6 @@ static void (*music_fade_callback)(); static SDL_AudioDeviceID g_device_id; static SDL_AudioDeviceID g_input_device_id; -static boolean g_input_device_paused; void* I_GetSfx(sfxinfo_t* sfx) { @@ -999,7 +998,7 @@ void I_UpdateAudioRecorder(void) boolean I_SoundInputIsEnabled(void) { - return g_input_device_id != 0 && !g_input_device_paused; + return g_input_device_id != 0; } boolean I_SoundInputSetEnabled(boolean enabled) @@ -1023,21 +1022,17 @@ boolean I_SoundInputSetEnabled(boolean enabled) CONS_Alert(CONS_WARNING, "Failed to open input audio device: %s\n", SDL_GetError()); return false; } - g_input_device_paused = true; - } - - if (enabled && g_input_device_paused) - { SDL_PauseAudioDevice(g_input_device_id, SDL_FALSE); - g_input_device_paused = false; } - else if (!enabled && !g_input_device_paused) + else if (g_input_device_id != 0 && !enabled) { SDL_PauseAudioDevice(g_input_device_id, SDL_TRUE); SDL_ClearQueuedAudio(g_input_device_id); - g_input_device_paused = true; + SDL_CloseAudioDevice(g_input_device_id); + g_input_device_id = 0; } - return !g_input_device_paused; + + return enabled; } UINT32 I_SoundInputDequeueSamples(void *data, UINT32 len) From 610599e7d157fdcffc4e74b74412c2e749036e1d Mon Sep 17 00:00:00 2001 From: Eidolon Date: Thu, 29 May 2025 15:31:24 -0500 Subject: [PATCH 22/26] Don't try to open microphone when sound is disabled --- src/sdl/new_sound.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sdl/new_sound.cpp b/src/sdl/new_sound.cpp index 1e1d93797..1fa9deea2 100644 --- a/src/sdl/new_sound.cpp +++ b/src/sdl/new_sound.cpp @@ -1005,7 +1005,7 @@ boolean I_SoundInputSetEnabled(boolean enabled) { if (g_input_device_id == 0 && enabled) { - if (SDL_GetNumAudioDevices(true) == 0) + if (!sound_started || SDL_GetNumAudioDevices(true) == 0) { return false; } From 9c07bd3ca84fa4d7a1ec062df69c39cd67944968 Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Thu, 29 May 2025 19:11:32 -0400 Subject: [PATCH 23/26] Margin Boost UI psychosis --- src/k_hud.cpp | 152 ++++++++++++++++++++++++++++++++++++++++--------- src/k_hud.h | 2 + src/m_random.h | 2 + src/p_user.c | 5 +- 4 files changed, 132 insertions(+), 29 deletions(-) diff --git a/src/k_hud.cpp b/src/k_hud.cpp index 9f06e69ea..35e0bb0dd 100644 --- a/src/k_hud.cpp +++ b/src/k_hud.cpp @@ -240,7 +240,7 @@ static patch_t *kp_duel_you; static patch_t *kp_duel_sticker; static patch_t *kp_duel_under; static patch_t *kp_duel_over; -static patch_t *kp_duel_margin[6]; +static patch_t *kp_duel_margin[24]; patch_t *kp_autoroulette; patch_t *kp_autoring; @@ -1074,9 +1074,13 @@ void K_LoadKartHUDGraphics(void) HU_UpdatePatch(&kp_duel_under, "DUEL_B"); HU_UpdatePatch(&kp_duel_over, "DUEL_B2"); HU_UpdatePatch(&kp_duel_you, "DUEL_YOU"); - for (i = 0; i < 6; i++) + + sprintf(buffer, "DUELMBxx"); + for (i = 0; i < MARGINLEVELS; i++) { - HU_UpdatePatch(&kp_duel_margin[i], "DUELMB0%d", i); + buffer[6] = '0'+(i/10); + buffer[7] = '0'+(i%10); + HU_UpdatePatch(&kp_duel_margin[i], "%s", buffer); } } @@ -3250,6 +3254,8 @@ INT32 K_GetTransFlagFromFixed(fixed_t value) } static tic_t duel_lastleveltime = 0; +static INT32 duel_marginanim = 0; +static INT32 duel_lastmargin = 0; static INT32 youheight = 0; static void K_drawKartDuelScores(void) @@ -3320,7 +3326,6 @@ static void K_drawKartDuelScores(void) else if (targetyouheight < youheight) youheight -= slide; } - duel_lastleveltime = leveltime; INT32 foeheight = 2*barheight-youheight; // barheight is a single tied bar, so total height of the full gauge is 2x barheight @@ -3395,47 +3400,138 @@ static void K_drawKartDuelScores(void) V_DrawMappedPatch(drawx+xoff, drawy+yoff, flags|flipflag, faceprefix[workingskin][FACE_RANK], colormap); } - #define MARGINLEVELS (6) + // Dogshit. Should have just figured out how to do log base 5 in C++. + // However, I think this works anyway. + // I did my best to comment this but the algorithm is honestly just bad and hard to + // reason about. Please don't try to maintain this, just yell at me if it needs any + // adjustments. -Tyron 2025-05-29 - INT32 marginvalues[MARGINLEVELS] = {1, 5, 7, 9, 11, 13}; + // DESIGN INTENT: Create realistic-looking Puyo garbage stacks, while using the + // leading garbage symbol as an indicator of the current Margin Boost value. - INT32 margindigits[20]; + INT32 rawmargin = overtimecheckpoints; // The actual Margin Boost value. + INT32 boostspersymbol = 3; // How many boosts should it take to see a new symbol? + // rawmargin = (leveltime/10)%(3*boostspersymbol); + + if (duel_lastleveltime != leveltime) // Trigger the "slide" animation when rawmargin changes. + { + duel_marginanim = std::min(duel_marginanim + 1, 100); // not magic just arbitrary + if (duel_lastmargin != rawmargin) + { + duel_marginanim = 0; + duel_lastmargin = rawmargin; + } + } + + duel_lastleveltime = leveltime; + + // CONS_Printf("=== RAWMARGIN %d\n", rawmargin); + + if (rawmargin == 0) + return; + + rawmargin--; // Start at 0, idiot + + // We're invoking the RNG to get a slightly chaotic symbol distribution, + // but we're a HUD hook, so we need to keep the results of the call consistent. + P_SetRandSeed(PR_NUISANCE, 69 + rawmargin); + + INT32 highsymbol = rawmargin/boostspersymbol + 1; // Highest symbol that should appear. + INT32 symbolsperupgrade = 5; // What is each symbol worth relative to each other? Like, 5 Stars = 1 Moon, etc. + + // Okay, so we would LOVE to do this in a way that isn't a big clusterfuck, like just + // doing rawmargin^3 and then subtracting powers of 5 out of that. Unfortunately, UINT64 + // is too small for the values that feel intuitively right here, so we have to do some of + // the math on a limited set of symbols, then shift up. This is the concept of "symbol + // headroom" that's in use here. + // + // (Note that Puyo~n uses a super inconsistent symbol table, probably to avoid this problem, + // but we're assholes and want things to feel logically consistent I guess? + // I dunno. I sort of feel like I should have just directly used the Puyo~n garbage table and + // avoided most of this, LOL) + + INT32 symbolheadroom = 5; // Maximum # symbols we can "step down". + INT32 frac = rawmargin % boostspersymbol; // Used in intermediate calculations. + INT32 minsymbol = std::max(1, highsymbol - symbolheadroom); // The lowest symbol that should appear. + INT32 symbolheadroominuse = highsymbol - minsymbol; // The # of symbols we are stepping down. + INT32 minscore = std::pow(symbolsperupgrade, symbolheadroominuse+1); + INT32 maxscore = std::pow(symbolsperupgrade, symbolheadroominuse+2) - 1; + + // CONS_Printf("min %d max %d\n", minscore, maxscore); + + // We show the player successive combos with the same leading symbol, but we + // waht them to feel intuitively like they're increasing each time. + // Maxscore and minscore have been mapped to the correct power-of-N, so any + // point we pick between them will lead with the correct symbol once we adjust + // for symbol headroom. Pick a point that's appropriate for how "far" into the + // current symbol we are. + fixed_t lobound = FRACUNIT * frac / boostspersymbol; + fixed_t hibound = FRACUNIT * (frac+1) / boostspersymbol; + fixed_t roll = P_RandomRange(PR_NUISANCE, lobound, hibound); + + INT32 margin = Easing_Linear(roll, minscore, maxscore); // The score we're trying to draw a garbage stack for. + + INT32 margindigits[5]; memset(margindigits, -1, sizeof(margindigits)); INT32 nummargindigits = 0; - INT32 margin = overtimecheckpoints; - margin = ((leveltime/20)%50)+1; // debug + // CONS_Printf("margin %d min %d max %d roll %d shiu %d ms %d\n", margin, minscore, maxscore, roll, symbolheadroominuse, minsymbol); - if (margin == 0) - return; - - while (margin) + if (rawmargin/boostspersymbol >= (MARGINLEVELS-1)) { - UINT32 significant_margin = 0; - for (UINT8 i = MARGINLEVELS-1; i >= 0; i--) + // Capped out. Show 5 Chaos. + nummargindigits = 5; + for(UINT8 i = 0; i < nummargindigits; i++) { - if (margin >= marginvalues[i]) - { - significant_margin = i; - break; - } + margindigits[i] = MARGINLEVELS-1; + } + } + else + { + // Subtract powers of N from our chosen score to create a decent-enough-looking + // garbage stack, then queue up the right patches to be drawn, shifting all the math + // up by "minsymbol"—remember, once maxsymbol goes above symbolheadroom, we are doing + // a low-precision version of the math that ignores low enough symbols. + while (margin > 0) + { + INT32 significant_margin = 0; + for (UINT8 i = symbolheadroominuse+1; i >= 0; i--) + { + INT32 test = std::pow(symbolsperupgrade, i); + // CONS_Printf("testing %d (%d)\n", i, test); + if (margin >= test) + { + significant_margin = i; + break; + } + } + + INT32 index = significant_margin; + + margindigits[nummargindigits] = index + minsymbol - 1; + // CONS_Printf("digit %d %d\n", nummargindigits, margindigits[nummargindigits]); + + nummargindigits++; + + // CONS_Printf("margin was %d ", margin); + margin -= std::pow(symbolsperupgrade, index); + // CONS_Printf("is %d\n", margin); + + if (nummargindigits >= 3 + frac) + break; } - margindigits[nummargindigits] = significant_margin; - nummargindigits++; - margin -= marginvalues[significant_margin]; } - INT32 marginoffset = 6; - INT32 marginx = ((nummargindigits-1) * marginoffset)/2; + INT32 marginspacing = std::min(6, duel_marginanim); + INT32 marginx = ((nummargindigits-1) * marginspacing)/2; for (INT32 i = nummargindigits - 1; i >= 0; i--) { + // CONS_Printf("draw %d - %d\n", i, margindigits[i]); V_DrawScaledPatch(basex + marginx, basey, flags, kp_duel_margin[margindigits[i]]); - marginx -= marginoffset; + marginx -= marginspacing; } - - #undef MARGINLEVELS } static INT32 easedallyscore = 0; diff --git a/src/k_hud.h b/src/k_hud.h index cd93a192b..038be77f2 100644 --- a/src/k_hud.h +++ b/src/k_hud.h @@ -25,6 +25,8 @@ extern "C" { #define POS_DELAY_TIME 10 +#define MARGINLEVELS 24 + extern INT32 MINI_X, MINI_Y; struct trackingResult_t diff --git a/src/m_random.h b/src/m_random.h index edc59ee66..dcfeeca3e 100644 --- a/src/m_random.h +++ b/src/m_random.h @@ -92,6 +92,8 @@ typedef enum PR_ITEM_SPAWNER = PROLDDEMO, // Battle mode item spawners PR_TEAMS, // Teamplay shuffling + PR_NUISANCE, // Margin Boost HUD + PRNUMSYNCED, PR_INTERPHUDRANDOM = PRNUMSYNCED, // Interpolation-accomodating HUD randomisation diff --git a/src/p_user.c b/src/p_user.c index c5c9e6207..3b7119c30 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -3763,7 +3763,6 @@ boolean P_SpectatorJoinGame(player_t *player) } player->spectator = false; player->pflags &= ~PF_WANTSTOJOIN; - player->spectatewait = 0; player->team = TEAM_UNASSIGNED; // We will auto-assign later. player->playerstate = PST_REBORN; player->enteredGame = true; @@ -3778,6 +3777,10 @@ boolean P_SpectatorJoinGame(player_t *player) // a surprise tool that will help us later... text = va("\x82*%s entered the game.", player_names[player-players]); + if (P_IsMachineLocalPlayer(player) && player->spectatewait > TICRATE) + S_StartSound(NULL, sfx_s3ka9); + player->spectatewait = 0; + HU_AddChatText(text, false); return true; // no more player->mo, cannot continue. } From 136cb20cb6e2ef6470c12da2ed59e2adeb53b0fa Mon Sep 17 00:00:00 2001 From: Ashnal Date: Sun, 18 May 2025 15:57:59 -0400 Subject: [PATCH 24/26] Whip collects items --- src/k_collide.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/k_collide.cpp b/src/k_collide.cpp index 4abe3f269..37cfa616e 100644 --- a/src/k_collide.cpp +++ b/src/k_collide.cpp @@ -1059,11 +1059,13 @@ boolean K_InstaWhipCollide(mobj_t *shield, mobj_t *victim) shield->extravalue1 = 1; } - if (P_DamageMobj(victim, shield, attacker, 1, DMG_NORMAL)) + if (!K_TryPickMeUp(attackerPlayer->mo, victim)) { - K_AddHitLag(attacker, attackerHitlag, false); - shield->hitlag = attacker->hitlag; + P_DamageMobj(victim, shield, attacker, 1, DMG_NORMAL); } + + K_AddHitLag(attacker, attackerHitlag, false); + shield->hitlag = attacker->hitlag; } return false; } From cdc49d755b5ec443678fe761be604dbf4079e223 Mon Sep 17 00:00:00 2001 From: Ashnal Date: Sun, 18 May 2025 16:21:56 -0400 Subject: [PATCH 25/26] Update K_TryPickMeUp with allowHostile boolean --- src/k_collide.cpp | 12 ++++++------ src/k_kart.c | 4 ++-- src/k_kart.h | 2 +- src/objects/orbinaut.c | 2 +- src/p_inter.c | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/k_collide.cpp b/src/k_collide.cpp index 37cfa616e..28e544d8b 100644 --- a/src/k_collide.cpp +++ b/src/k_collide.cpp @@ -75,7 +75,7 @@ boolean K_BananaBallhogCollide(mobj_t *t1, mobj_t *t2) if (t1->type == MT_BALLHOGBOOM && t2->type == MT_BALLHOGBOOM) return true; // Ballhogs don't collide with eachother - if (K_TryPickMeUp(t1, t2)) + if (K_TryPickMeUp(t1, t2, false)) return true; if (t2->player) @@ -178,7 +178,7 @@ boolean K_EggItemCollide(mobj_t *t1, mobj_t *t2) if (t1->health <= 0 || t2->health <= 0) return true; - if (K_TryPickMeUp(t1, t2)) + if (K_TryPickMeUp(t1, t2, false)) return true; if (!P_CanPickupItem(t2->player, PICKUP_EGGBOX)) @@ -434,7 +434,7 @@ boolean K_LandMineCollide(mobj_t *t1, mobj_t *t2) if (t1->health <= 0 || t2->health <= 0) return true; - if (K_TryPickMeUp(t1, t2)) + if (K_TryPickMeUp(t1, t2, false)) return true; if (t2->player) @@ -544,7 +544,7 @@ 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)) + if (K_TryPickMeUp(t1, t2, false)) return true; if (draggeddroptarget && P_MobjWasRemoved(draggeddroptarget)) @@ -1059,7 +1059,7 @@ boolean K_InstaWhipCollide(mobj_t *shield, mobj_t *victim) shield->extravalue1 = 1; } - if (!K_TryPickMeUp(attackerPlayer->mo, victim)) + if (!K_TryPickMeUp(attackerPlayer->mo, victim, true)) { P_DamageMobj(victim, shield, attacker, 1, DMG_NORMAL); } @@ -1076,7 +1076,7 @@ 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)) + if (K_TryPickMeUp(t1, t2, false)) return true; if (t2->player) diff --git a/src/k_kart.c b/src/k_kart.c index d50b4c31a..856ea65d4 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -15908,7 +15908,7 @@ static boolean K_PickUp(player_t *player, mobj_t *picked) } // ACHTUNG this destroys items when returning true, make sure to bail out -boolean K_TryPickMeUp(mobj_t *m1, mobj_t *m2) +boolean K_TryPickMeUp(mobj_t *m1, mobj_t *m2, boolean allowHostile) { if (!m1 || P_MobjWasRemoved(m1)) return false; @@ -15943,7 +15943,7 @@ boolean K_TryPickMeUp(mobj_t *m1, mobj_t *m2) if (inflictor->target->player && G_SameTeam(inflictor->target->player, victim->player)) allied = true; - if (!allied) + if (!allied && !allowHostile) return false; // CONS_Printf("target check passed\n"); diff --git a/src/k_kart.h b/src/k_kart.h index 762ab997a..721b5763f 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -327,7 +327,7 @@ void K_BotHitPenalty(player_t *player); boolean K_IsPickMeUpItem(mobjtype_t type); -boolean K_TryPickMeUp(mobj_t *m1, mobj_t *m2); +boolean K_TryPickMeUp(mobj_t *m1, mobj_t *m2, boolean allowHostile); fixed_t K_TeamComebackMultiplier(player_t *player); diff --git a/src/objects/orbinaut.c b/src/objects/orbinaut.c index 501bc0e2d..9767f49f1 100644 --- a/src/objects/orbinaut.c +++ b/src/objects/orbinaut.c @@ -190,7 +190,7 @@ boolean Obj_OrbinautJawzCollide(mobj_t *t1, mobj_t *t2) return true; } - if (K_TryPickMeUp(t1, t2)) + if (K_TryPickMeUp(t1, t2, false)) return true; if (t1->type == MT_GARDENTOP) diff --git a/src/p_inter.c b/src/p_inter.c index ff2b90ed1..c7b370f04 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -667,7 +667,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (!player->mo || player->spectator) return; - if (K_TryPickMeUp(special, toucher)) + if (K_TryPickMeUp(special, toucher, false)) return; // attach to player! From b9f80f902b176a8aa6117b15198f60fa339a6dea Mon Sep 17 00:00:00 2001 From: Eidolon Date: Sat, 9 Nov 2024 20:44:40 -0600 Subject: [PATCH 26/26] Allow drop target to be picked up by whipping and a bit of cleanup --- src/k_collide.cpp | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/k_collide.cpp b/src/k_collide.cpp index 28e544d8b..77cf778c8 100644 --- a/src/k_collide.cpp +++ b/src/k_collide.cpp @@ -1042,7 +1042,14 @@ boolean K_InstaWhipCollide(mobj_t *shield, mobj_t *victim) } else if (victim->type == MT_DROPTARGET || victim->type == MT_DROPTARGET_SHIELD) { - K_DropTargetCollide(victim, shield); + if (K_TryPickMeUp(attacker, victim, true)) + { + shield->hitlag = attacker->hitlag; // players hitlag is handled in K_TryPickMeUp, and we need to set for the shield too + } + else + { + K_DropTargetCollide(victim, shield); + } return true; } else @@ -1059,13 +1066,16 @@ boolean K_InstaWhipCollide(mobj_t *shield, mobj_t *victim) shield->extravalue1 = 1; } - if (!K_TryPickMeUp(attackerPlayer->mo, victim, true)) + if (K_TryPickMeUp(attacker, victim, true)) + { + shield->hitlag = attacker->hitlag; // players hitlag is handled in K_TryPickMeUp, and we need to set for the shield too + } + else { P_DamageMobj(victim, shield, attacker, 1, DMG_NORMAL); + K_AddHitLag(attacker, attackerHitlag, false); + shield->hitlag = attacker->hitlag; } - - K_AddHitLag(attacker, attackerHitlag, false); - shield->hitlag = attacker->hitlag; } return false; }