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/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); 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/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/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 df5595b99..1dedf3833 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; @@ -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); @@ -5426,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)) 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/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/k_collide.cpp b/src/k_collide.cpp index 4e1a3451b..77cf778c8 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)) @@ -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 - lightningDist > thing->z + thing->height) + return BMIT_CONTINUE; // overhead + if (lightningSource->z + lightningSource->height + lightningDist < thing->z) + return BMIT_CONTINUE; // underneath + #if 0 if (P_CheckSight(lightningSource, thing) == false) { @@ -1036,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 @@ -1053,8 +1066,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(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; } @@ -1068,7 +1086,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_hud.cpp b/src/k_hud.cpp index 0fe61fafa..91bd054f1 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) @@ -3289,8 +3295,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; @@ -3322,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 @@ -3397,7 +3400,138 @@ 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]); + // 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 + + // DESIGN INTENT: Create realistic-looking Puyo garbage stacks, while using the + // leading garbage symbol as an indicator of the current Margin Boost value. + + 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; + + // CONS_Printf("margin %d min %d max %d roll %d shiu %d ms %d\n", margin, minscore, maxscore, roll, symbolheadroominuse, minsymbol); + + if (rawmargin/boostspersymbol >= (MARGINLEVELS-1)) + { + // Capped out. Show 5 Chaos. + nummargindigits = 5; + for(UINT8 i = 0; i < nummargindigits; i++) + { + 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; + } + } + + 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 -= marginspacing; + } } static INT32 easedallyscore = 0; @@ -3480,6 +3614,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; @@ -3535,7 +3672,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) { @@ -3702,6 +3839,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; @@ -4141,7 +4283,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... { @@ -7113,35 +7264,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); } } 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/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 d3a183097..856ea65d4 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<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 <= starttime || player->gradingpointnum == 0) + { + 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++; @@ -11455,6 +11470,14 @@ INT16 K_GetKartTurnValue(const player_t *player, INT16 turnvalue) turnfixed = FixedMul(turnfixed, 2*FRACUNIT); // Base increase to turning } + /* + if (overtimecheckpoints) + { + fixed_t assistpercent = FRACUNIT * overtimecheckpoints / 32; + turnfixed += FixedMul(Easing_Linear(assistpercent, 0, FRACUNIT/2), turnfixed); + } + */ + if (player->drift != 0 && P_IsObjectOnGround(player->mo)) { if (G_CompatLevel(0x000A)) @@ -15772,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; @@ -15862,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; @@ -15897,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 5c36fcd26..721b5763f 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -325,7 +325,9 @@ boolean K_LegacyRingboost(player_t *player); void K_BotHitPenalty(player_t *player); -boolean K_TryPickMeUp(mobj_t *m1, mobj_t *m2); +boolean K_IsPickMeUpItem(mobjtype_t type); + +boolean K_TryPickMeUp(mobj_t *m1, mobj_t *m2, boolean allowHostile); fixed_t K_TeamComebackMultiplier(player_t *player); 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/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/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/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); 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; 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/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_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_inter.c b/src/p_inter.c index 2df953dec..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! @@ -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; 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_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; } 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; 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. } diff --git a/src/sdl/new_sound.cpp b/src/sdl/new_sound.cpp index 3ded71879..1fa9deea2 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,14 +998,14 @@ 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) { if (g_input_device_id == 0 && enabled) { - if (SDL_GetNumAudioDevices(true) == 0) + if (!sound_started || SDL_GetNumAudioDevices(true) == 0) { return false; } @@ -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) 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();