From f86f794d20a016aa8e880effb97e1f196feaa439 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 7 Mar 2023 03:05:12 -0800 Subject: [PATCH 01/27] g_demo.c: track player mobj health instead of bumpers Lord have mercy on my soul!!! --- src/g_demo.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/g_demo.c b/src/g_demo.c index 617dc386b..37504a6c1 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -95,7 +95,8 @@ static struct { // EZT_ITEMDATA SINT8 itemtype; - UINT8 itemamount, bumpers; + UINT8 itemamount; + INT32 health; // EZT_STATDATA UINT8 skinid, kartspeed, kartweight; @@ -813,13 +814,13 @@ void G_WriteGhostTic(mobj_t *ghost, INT32 playernum) if (ghost->player && ( ghostext[playernum].itemtype != ghost->player->itemtype || ghostext[playernum].itemamount != ghost->player->itemamount || - ghostext[playernum].bumpers != ghost->player->bumpers + ghostext[playernum].health < ghost->health )) { ghostext[playernum].flags |= EZT_ITEMDATA; ghostext[playernum].itemtype = ghost->player->itemtype; ghostext[playernum].itemamount = ghost->player->itemamount; - ghostext[playernum].bumpers = ghost->player->bumpers; + ghostext[playernum].health = ghost->health; } if (ghost->player && ( @@ -881,7 +882,7 @@ void G_WriteGhostTic(mobj_t *ghost, INT32 playernum) { WRITESINT8(demobuf.p, ghostext[playernum].itemtype); WRITEUINT8(demobuf.p, ghostext[playernum].itemamount); - WRITEUINT8(demobuf.p, ghostext[playernum].bumpers); + WRITEINT32(demobuf.p, ghostext[playernum].health); } if (ghostext[playernum].flags & EZT_STATDATA) { @@ -1064,7 +1065,7 @@ void G_ConsGhostTic(INT32 playernum) { ghostext[playernum].itemtype = READSINT8(demobuf.p); ghostext[playernum].itemamount = READUINT8(demobuf.p); - ghostext[playernum].bumpers = READUINT8(demobuf.p); + ghostext[playernum].health = READINT32(demobuf.p); } if (xziptic & EZT_STATDATA) { @@ -1134,7 +1135,7 @@ void G_ConsGhostTic(INT32 playernum) if (players[playernum].itemtype != ghostext[playernum].itemtype || players[playernum].itemamount != ghostext[playernum].itemamount - || players[playernum].bumpers != ghostext[playernum].bumpers) + || testmo->health < ghostext[playernum].health) { if (demosynced) CONS_Alert(CONS_WARNING, M_GetText("Demo playback has desynced (item/bumpers)!\n")); @@ -1142,7 +1143,7 @@ void G_ConsGhostTic(INT32 playernum) players[playernum].itemtype = ghostext[playernum].itemtype; players[playernum].itemamount = ghostext[playernum].itemamount; - players[playernum].bumpers = ghostext[playernum].bumpers; + testmo->health = ghostext[playernum].health; } if (players[playernum].kartspeed != ghostext[playernum].kartspeed From a648ff31f1234022fac2dc6ffa6292cafb2ca6dc Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 7 Mar 2023 03:27:00 -0800 Subject: [PATCH 02/27] Let spectators watch players who just died in Battle If they're going to respawn anyway, there's no reason not to spectate them. PF_ELIMINATED condition above excludes Overtime Barrier KO. --- src/g_game.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 6b2af2c5d..b0a8b42dc 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1890,13 +1890,6 @@ boolean G_CouldView(INT32 playernum) if (( player->pflags & PF_NOCONTEST )) return false; - // I don't know if we want this actually, but I'll humor the suggestion anyway - if ((gametyperules & GTR_BUMPERS) && !demo.playback) - { - if (player->bumpers <= 0) - return false; - } - // SRB2Kart: we have no team-based modes, YET... if (G_GametypeHasTeams()) { From 3843f0c3cdb58ed379362b6fa75e47e43bf91ab9 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 7 Mar 2023 03:38:56 -0800 Subject: [PATCH 03/27] Remove more Karma Bomb crap - remove Karma Bomb boost trail condition - remove Karma Bomb condition for Eggman Mark - remove Karma Bomb condition for hiding player name tags - remove Karma Bomb player translucency - remove Karma Bomb respawn invincibility - remove Karma Bomb speed buff - remove Karma Bomb sphere digestion - remove Overtime Karma --- src/d_player.h | 1 - src/g_game.c | 1 - src/k_collide.c | 5 +---- src/k_hud.c | 11 +--------- src/k_kart.c | 54 ++----------------------------------------------- src/p_inter.c | 4 ---- src/p_saveg.c | 2 -- 7 files changed, 4 insertions(+), 74 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index 4a8013f89..856f45291 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -604,7 +604,6 @@ struct player_t UINT8 emeralds; UINT8 bumpers; INT16 karmadelay; - tic_t overtimekarma; // time to live in overtime comeback INT16 spheres; tic_t spheredigestion; diff --git a/src/g_game.c b/src/g_game.c index b0a8b42dc..a5f34dc8d 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2637,7 +2637,6 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) p->growshrinktimer = growshrinktimer; p->bumpers = bumper; p->karmadelay = comebacktime; - p->overtimekarma = 0; p->eggmanblame = -1; p->lastdraft = -1; p->karthud[khud_fault] = khudfault; diff --git a/src/k_collide.c b/src/k_collide.c index 12757a2a3..4e04415c3 100644 --- a/src/k_collide.c +++ b/src/k_collide.c @@ -182,10 +182,7 @@ boolean K_EggItemCollide(mobj_t *t1, mobj_t *t2) if (t1->target && t1->target->player) { - if ((gametyperules & GTR_CIRCUIT) || t1->target->player->bumpers > 0) - t2->player->eggmanblame = t1->target->player-players; - else - t2->player->eggmanblame = t2->player-players; + t2->player->eggmanblame = t1->target->player - players; if (t1->target->hnext == t1) { diff --git a/src/k_hud.c b/src/k_hud.c index 8b5dd5c0e..68b314de1 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -2910,10 +2910,7 @@ static void K_drawKartBumpersOrKarma(void) else V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|V_SLIDEIN|splitflags, kp_bumpersticker, colormap); - if (gametyperules & GTR_KARMA) // TODO BETTER HUD - V_DrawKartString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|V_SLIDEIN|splitflags, va("%d/%d %d", stplyr->bumpers, maxbumper, stplyr->overtimekarma / TICRATE)); - else - V_DrawKartString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|V_SLIDEIN|splitflags, va("%d/%d", stplyr->bumpers, maxbumper)); + V_DrawKartString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|V_SLIDEIN|splitflags, va("%d/%d", stplyr->bumpers, maxbumper)); } } } @@ -3366,12 +3363,6 @@ static void K_drawKartNameTags(void) continue; } - if ((gametyperules & GTR_BUMPERS) && (ntplayer->bumpers <= 0)) - { - // Dead in Battle - continue; - } - if (!P_CheckSight(stplyr->mo, ntplayer->mo)) { // Can't see diff --git a/src/k_kart.c b/src/k_kart.c index 368d4cb0c..95e208cbd 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3262,11 +3262,6 @@ fixed_t K_GetKartSpeed(player_t *player, boolean doboostpower, boolean dorubberb { finalspeed = K_GetKartSpeedFromStat(player->kartspeed); - if (gametyperules & GTR_BUMPERS && player->bumpers <= 0) - { - finalspeed = 3 * finalspeed / 2; - } - if (player->spheres > 0) { fixed_t sphereAdd = (FRACUNIT/40); // 100% at max @@ -3326,12 +3321,6 @@ fixed_t K_GetKartAccel(player_t *player) return FixedMul(k_accel, FRACUNIT / 4); } - // karma bomb gets 2x acceleration - if ((gametyperules & GTR_BUMPERS) && player->bumpers <= 0) - { - k_accel *= 2; - } - // Marble Garden Top gets 1200% accel if (player->curshield == KSHIELD_TOP) { @@ -4334,18 +4323,7 @@ void K_HandleBumperChanges(player_t *player, UINT8 prevBumpers) return; } - // TODO: replace all console text print-outs with a real visual - - if (player->bumpers > 0 && prevBumpers == 0) - { - K_DoInvincibility(player, 8 * TICRATE); - - if (netgame) - { - CONS_Printf(M_GetText("%s is back in the game!\n"), player_names[player-players]); - } - } - else if (player->bumpers == 0 && prevBumpers > 0) + if (player->bumpers == 0 && prevBumpers > 0) { if (battlecapsules || bossinfo.valid) { @@ -5123,8 +5101,7 @@ void K_SpawnBoostTrail(player_t *player) I_Assert(!P_MobjWasRemoved(player->mo)); if (!P_IsObjectOnGround(player->mo) - || player->hyudorotimer != 0 - || ((gametyperules & GTR_BUMPERS) && player->bumpers <= 0 && player->karmadelay)) + || player->hyudorotimer != 0) return; if (player->mo->eflags & MFE_VERTICALFLIP) @@ -7797,13 +7774,6 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) player->spheres = 40; // where's the < 0 check? see below the following block! - if ((gametyperules & GTR_BUMPERS) && (player->bumpers <= 0)) - { - // Deplete 1 every tic when removed from the game. - player->spheres--; - player->spheredigestion = 0; - } - else { tic_t spheredigestion = TICRATE; // Base rate of 1 every second when playing. tic_t digestionpower = ((10 - player->kartspeed) + (10 - player->kartweight))-1; // 1 to 17 @@ -8017,14 +7987,6 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) K_UpdateTripwire(player); - if (battleovertime.enabled && !(player->pflags & PF_ELIMINATED) && player->bumpers <= 0 && player->karmadelay <= 0) - { - if (player->overtimekarma) - player->overtimekarma--; - else - P_DamageMobj(player->mo, NULL, NULL, 1, DMG_TIMEOVER); - } - if ((battleovertime.enabled >= 10*TICRATE) && !(player->pflags & PF_ELIMINATED) && !player->exiting) { fixed_t distanceToBarrier = 0; @@ -11184,18 +11146,6 @@ void K_MoveKartPlayer(player_t *player, boolean onground) player->mo->renderflags &= ~RF_DONTDRAW; } - if (!(gametyperules & GTR_BUMPERS) || player->bumpers > 0) - { - player->mo->renderflags &= ~(RF_TRANSMASK|RF_BRIGHTMASK); - } - else // dead in match? you da bomb - { - K_DropItems(player); //K_StripItems(player); - K_StripOther(player); - player->mo->renderflags |= RF_GHOSTLY; - player->flashing = player->karmadelay; - } - if (player->trickpanel == 1) { const angle_t lr = ANGLE_45; diff --git a/src/p_inter.c b/src/p_inter.c index ff4b9f916..e703cdf7a 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -2285,10 +2285,6 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da // Destroy any remainder bumpers from the player for karma comeback damage damage = K_DestroyBumpers(player, player->bumpers); } - else - { - source->player->overtimekarma += 5*TICRATE; - } if (damagetype & DMG_STEAL) { diff --git a/src/p_saveg.c b/src/p_saveg.c index e0db5fdb0..3c130b453 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -383,7 +383,6 @@ static void P_NetArchivePlayers(savebuffer_t *save) WRITEUINT8(save->p, players[i].emeralds); WRITEUINT8(save->p, players[i].bumpers); WRITEINT16(save->p, players[i].karmadelay); - WRITEUINT32(save->p, players[i].overtimekarma); WRITEINT16(save->p, players[i].spheres); WRITEUINT32(save->p, players[i].spheredigestion); @@ -760,7 +759,6 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) players[i].emeralds = READUINT8(save->p); players[i].bumpers = READUINT8(save->p); players[i].karmadelay = READINT16(save->p); - players[i].overtimekarma = READUINT32(save->p); players[i].spheres = READINT16(save->p); players[i].spheredigestion = READUINT32(save->p); From a1558f1fbda3b4e592bc795f9b8be08ff15e216a Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 7 Mar 2023 19:35:02 -0800 Subject: [PATCH 04/27] Move Break the Capsules and Boss elimination condition to P_KillMobj --- src/k_kart.c | 11 +++-------- src/p_inter.c | 20 +++++++++++++++++--- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 95e208cbd..91c28b0aa 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -4317,20 +4317,15 @@ void K_DebtStingPlayer(player_t *player, mobj_t *source) void K_HandleBumperChanges(player_t *player, UINT8 prevBumpers) { + (void)player; + (void)prevBumpers; + if (!(gametyperules & GTR_BUMPERS)) { // Bumpers aren't being used return; } - if (player->bumpers == 0 && prevBumpers > 0) - { - if (battlecapsules || bossinfo.valid) - { - player->pflags |= (PF_NOCONTEST|PF_ELIMINATED); - } - } - K_CalculateBattleWanted(); K_CheckBumpers(); } diff --git a/src/p_inter.c b/src/p_inter.c index e703cdf7a..08e0c186a 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -39,6 +39,7 @@ #include "p_spec.h" #include "k_objects.h" #include "k_roulette.h" +#include "k_boss.h" // CTF player names #define CTFTEAMCODE(pl) pl->ctfteam ? (pl->ctfteam == 1 ? "\x85" : "\x84") : "" @@ -1395,6 +1396,11 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget P_PlayDeathSound(target); } + + if (battlecapsules || bossinfo.valid) + { + target->player->pflags |= (PF_NOCONTEST|PF_ELIMINATED); + } break; case MT_METALSONIC_RACE: @@ -1937,13 +1943,21 @@ static boolean P_KillPlayer(player_t *player, mobj_t *inflictor, mobj_t *source, switch (type) { case DMG_DEATHPIT: - // Respawn kill types - K_DoIngameRespawn(player); + // Battle player->mo->health -= K_DestroyBumpers(player, 1); - return false; + + if (player->mo->health <= 0) + { + return true; + } + + // Quick respawn; does not kill + return K_DoIngameRespawn(player), false; + case DMG_SPECTATOR: // disappearifies, but still gotta put items back in play break; + default: // Everything else REALLY kills if (leveltime < starttime) From a3c64068e6ea87ec14b6811d9b0112e51750c4f7 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 7 Mar 2023 04:41:55 -0800 Subject: [PATCH 05/27] Replace bumpers completely with health Adds some functions: - K_Bumpers, bumper count for the count, intended for where player->bumpers was used in HUD and visual contexts. - K_BumpersToHealth, converts bumper count to health points. player->mo->health replaces player->bumpers where it was used in health contexts. Removes some functions: - K_HandleBumperChanges - K_DestroyBumpers Everything K_HandleBumperChanges did has either been removed or moved elsewhere. P_KillMobj also already called K_CheckBumpers. K_DestroyBumpers became pointless after player->bumpers was removed. --- src/d_player.h | 1 - src/g_game.c | 6 +-- src/k_battle.c | 50 ++++++++++++++++++++----- src/k_battle.h | 2 + src/k_collide.c | 9 +---- src/k_hud.c | 36 ++++++++++-------- src/k_kart.c | 90 +++++++-------------------------------------- src/k_kart.h | 4 +- src/lua_playerlib.c | 4 -- src/p_enemy.c | 2 +- src/p_inter.c | 58 ++++++----------------------- src/p_map.c | 7 ---- src/p_mobj.c | 52 +++++++------------------- src/p_saveg.c | 2 - src/p_user.c | 6 +-- 15 files changed, 108 insertions(+), 221 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index 856f45291..be2708614 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -602,7 +602,6 @@ struct player_t UINT32 roundscore; // battle score this round UINT8 emeralds; - UINT8 bumpers; INT16 karmadelay; INT16 spheres; tic_t spheredigestion; diff --git a/src/g_game.c b/src/g_game.c index a5f34dc8d..5605fe958 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2420,7 +2420,6 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) INT32 itemtype; INT32 itemamount; INT32 growshrinktimer; - INT32 bumper; boolean songcredit = false; UINT16 nocontrol; INT32 khudfault; @@ -2492,7 +2491,6 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) itemtype = 0; itemamount = 0; growshrinktimer = 0; - bumper = ((gametyperules & GTR_BUMPERS) ? K_StartingBumperCount() : 0); if (gametyperules & GTR_SPHERES) { rings = 0; @@ -2537,7 +2535,6 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) else growshrinktimer = 0; - bumper = players[player].bumpers; rings = players[player].rings; spheres = players[player].spheres; kickstartaccel = players[player].kickstartaccel; @@ -2635,8 +2632,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) p->itemtype = itemtype; p->itemamount = itemamount; p->growshrinktimer = growshrinktimer; - p->bumpers = bumper; - p->karmadelay = comebacktime; + p->karmadelay = 0; p->eggmanblame = -1; p->lastdraft = -1; p->karthud[khud_fault] = khudfault; diff --git a/src/k_battle.c b/src/k_battle.c index 38b86abaf..9b14491a9 100644 --- a/src/k_battle.c +++ b/src/k_battle.c @@ -113,7 +113,7 @@ void K_CheckBumpers(void) numingame++; - if (players[i].bumpers <= 0) // if you don't have any bumpers, you're probably not a winner + if (!P_MobjWasRemoved(players[i].mo) && players[i].mo->health <= 0) // if you don't have any bumpers, you're probably not a winner { nobumpers++; } @@ -362,7 +362,7 @@ void K_RunPaperItemSpawners(void) emeraldsSpawned |= players[i].emeralds; if ((players[i].exiting > 0 || (players[i].pflags & PF_ELIMINATED)) - || ((gametyperules & GTR_BUMPERS) && players[i].bumpers <= 0)) + || ((gametyperules & GTR_BUMPERS) && !P_MobjWasRemoved(players[i].mo) && players[i].mo->health <= 0)) { continue; } @@ -738,16 +738,20 @@ void K_SetupMovingCapsule(mapthing_t *mt, mobj_t *mobj) void K_SpawnPlayerBattleBumpers(player_t *p) { - if (!p->mo || p->bumpers <= 0) + const UINT8 bumpers = K_Bumpers(p); + + if (bumpers <= 0) + { return; + } { INT32 i; - angle_t diff = FixedAngle(360*FRACUNIT/p->bumpers); + angle_t diff = FixedAngle(360*FRACUNIT / bumpers); angle_t newangle = p->mo->angle; mobj_t *bump; - for (i = 0; i < p->bumpers; i++) + for (i = 0; i < bumpers; i++) { bump = P_SpawnMobjFromMobj(p->mo, P_ReturnThrustX(p->mo, newangle + ANGLE_180, 64*FRACUNIT), @@ -784,21 +788,49 @@ void K_BattleInit(boolean singleplayercontext) if (gametyperules & GTR_BUMPERS) { - INT32 maxbumpers = K_StartingBumperCount(); + const INT32 startingHealth = K_BumpersToHealth(K_StartingBumperCount()); for (i = 0; i < MAXPLAYERS; i++) { if (!playeringame[i] || players[i].spectator) continue; - players[i].bumpers = maxbumpers; - if (players[i].mo) { - players[i].mo->health = maxbumpers; + players[i].mo->health = startingHealth; } K_SpawnPlayerBattleBumpers(players+i); } } } + +UINT8 K_Bumpers(player_t *player) +{ + if ((gametyperules & GTR_BUMPERS) == 0) + { + return 0; + } + + if (P_MobjWasRemoved(player->mo)) + { + return 0; + } + + if (player->mo->health < 1) + { + return 0; + } + + if (player->mo->health > UINT8_MAX) + { + return UINT8_MAX; + } + + return player->mo->health; +} + +INT32 K_BumpersToHealth(UINT8 bumpers) +{ + return bumpers; +} diff --git a/src/k_battle.h b/src/k_battle.h index f64cfa967..d1927f256 100644 --- a/src/k_battle.h +++ b/src/k_battle.h @@ -38,6 +38,8 @@ void K_RunBattleOvertime(void); void K_SetupMovingCapsule(mapthing_t *mt, mobj_t *mobj); void K_SpawnPlayerBattleBumpers(player_t *p); void K_BattleInit(boolean singleplayercontext); +UINT8 K_Bumpers(player_t *player); +INT32 K_BumpersToHealth(UINT8 bumpers); #ifdef __cplusplus } // extern "C" diff --git a/src/k_collide.c b/src/k_collide.c index 4e04415c3..7964c3140 100644 --- a/src/k_collide.c +++ b/src/k_collide.c @@ -154,14 +154,7 @@ boolean K_EggItemCollide(mobj_t *t1, mobj_t *t2) if (!P_CanPickupItem(t2->player, 2)) return true; - if ((gametyperules & GTR_BUMPERS) && t2->player->bumpers <= 0) - { - return true; - } - else - { - K_StartEggmanRoulette(t2->player); - } + K_StartEggmanRoulette(t2->player); if (t2->player->flamedash && t2->player->itemtype == KITEM_FLAMESHIELD) { diff --git a/src/k_hud.c b/src/k_hud.c index 68b314de1..c68bbaa07 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -1995,10 +1995,12 @@ static boolean K_drawKartPositionFaces(void) if (LUA_HudEnabled(hud_battlebumpers)) { - if ((gametyperules & GTR_BUMPERS) && players[rankplayer[i]].bumpers > 0) + const UINT8 bumpers = K_Bumpers(&players[rankplayer[i]]); + + if (bumpers > 0) { V_DrawMappedPatch(bumperx-2, Y, V_HUDTRANS|V_SLIDEIN|V_SNAPTOLEFT, kp_tinybumper[0], colormap); - for (j = 1; j < players[rankplayer[i]].bumpers; j++) + for (j = 1; j < bumpers; j++) { bumperx += 5; V_DrawMappedPatch(bumperx, Y, V_HUDTRANS|V_SLIDEIN|V_SNAPTOLEFT, kp_tinybumper[1], colormap); @@ -2023,7 +2025,7 @@ static boolean K_drawKartPositionFaces(void) if (i == strank) V_DrawScaledPatch(FACE_X, Y, V_HUDTRANS|V_SLIDEIN|V_SNAPTOLEFT, kp_facehighlight[(leveltime / 4) % 8]); - if ((gametyperules & GTR_BUMPERS) && players[rankplayer[i]].bumpers <= 0) + if ((gametyperules & GTR_BUMPERS) && players[rankplayer[i]].mo->health <= 0) V_DrawScaledPatch(FACE_X-4, Y-3, V_HUDTRANS|V_SLIDEIN|V_SNAPTOLEFT, kp_ranknobumpers); else { @@ -2356,7 +2358,7 @@ void K_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, IN if (tab[i].num == whiteplayer) V_DrawScaledPatch(x, y-4, 0, kp_facehighlight[(leveltime / 4) % 8]); - if ((gametyperules & GTR_BUMPERS) && players[tab[i].num].bumpers <= 0) + if ((gametyperules & GTR_BUMPERS) && players[tab[i].num].mo->health <= 0) V_DrawScaledPatch(x-4, y-7, 0, kp_ranknobumpers); else { @@ -2866,14 +2868,16 @@ static void K_drawKartBumpersOrKarma(void) } else { - INT32 maxbumper = K_StartingBumperCount(); + const INT32 maxbumper = K_StartingBumperCount(); + const UINT8 bumpers = K_Bumpers(stplyr); + V_DrawMappedPatch(fx+1, fy-2, V_HUDTRANS|V_SLIDEIN|splitflags, kp_rankbumper, colormap); - if (stplyr->bumpers > 9 || maxbumper > 9) + if (bumpers > 9 || maxbumper > 9) { UINT8 ln[2]; - ln[0] = (stplyr->bumpers / 10 % 10); - ln[1] = (stplyr->bumpers % 10); + ln[0] = (bumpers / 10 % 10); + ln[1] = (bumpers % 10); V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[PINGNUM_FONT].font[ln[0]]); V_DrawScaledPatch(fx+17, fy, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[PINGNUM_FONT].font[ln[1]]); @@ -2886,7 +2890,7 @@ static void K_drawKartBumpersOrKarma(void) } else { - V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[(stplyr->bumpers) % 10]); + V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[(bumpers) % 10]); V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[(maxbumper) % 10]); } } @@ -2903,14 +2907,15 @@ static void K_drawKartBumpersOrKarma(void) } else { - INT32 maxbumper = K_StartingBumperCount(); + const INT32 maxbumper = K_StartingBumperCount(); + const UINT8 bumpers = K_Bumpers(stplyr); - if (stplyr->bumpers > 9 && maxbumper > 9) + if (bumpers > 9 && maxbumper > 9) V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|V_SLIDEIN|splitflags, kp_bumperstickerwide, colormap); else V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|V_SLIDEIN|splitflags, kp_bumpersticker, colormap); - V_DrawKartString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|V_SLIDEIN|splitflags, va("%d/%d", stplyr->bumpers, maxbumper)); + V_DrawKartString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|V_SLIDEIN|splitflags, va("%d/%d", bumpers, maxbumper)); } } } @@ -3643,7 +3648,7 @@ static void K_drawKartMinimap(void) } // Now we know it's not a display player, handle non-local player exceptions. - if ((gametyperules & GTR_BUMPERS) && players[i].bumpers <= 0) + if ((gametyperules & GTR_BUMPERS) && players[i].mo->health <= 0) continue; if (players[i].hyudorotimer > 0) @@ -4151,7 +4156,7 @@ static void K_drawBattleFullscreen(void) K_drawKartFinish(true); } - else if (stplyr->bumpers <= 0 && stplyr->karmadelay && !stplyr->spectator && drawcomebacktimer) + else if (stplyr->karmadelay && !stplyr->spectator && drawcomebacktimer) { UINT16 t = stplyr->karmadelay/(10*TICRATE); INT32 txoff, adjust = (r_splitscreen > 1) ? 4 : 6; // normal string is 8, kart string is 12, half of that for ease @@ -4885,8 +4890,7 @@ void K_drawKartHUD(void) battlefullscreen = (!(gametyperules & GTR_CIRCUIT) && (stplyr->exiting - || ((gametyperules & GTR_BUMPERS) && (stplyr->bumpers <= 0) - && ((gametyperules & GTR_KARMA) && (stplyr->karmadelay > 0)) + || (((gametyperules & GTR_KARMA) && (stplyr->karmadelay > 0)) && !(stplyr->pflags & PF_ELIMINATED) && stplyr->playerstate == PST_LIVE))); diff --git a/src/k_kart.c b/src/k_kart.c index 91c28b0aa..1bb745f41 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -374,9 +374,6 @@ boolean K_IsPlayerLosing(player_t *player) if (battlecapsules && numtargets == 0) return true; // Didn't even TRY? - if (battlecapsules || (gametyperules & GTR_BOSS)) - return (player->bumpers <= 0); // anything short of DNF is COOL - if (player->position == 1) return false; @@ -3636,7 +3633,7 @@ void K_DoPowerClash(player_t *t1, player_t *t2) { P_SetScale(clash, 3*clash->destscale/2); } -void K_BattleAwardHit(player_t *player, player_t *victim, mobj_t *inflictor, UINT8 bumpersRemoved) +void K_BattleAwardHit(player_t *player, player_t *victim, mobj_t *inflictor, UINT8 damage) { UINT8 points = 1; boolean trapItem = false; @@ -3674,7 +3671,7 @@ void K_BattleAwardHit(player_t *player, player_t *victim, mobj_t *inflictor, UIN } else if (gametyperules & GTR_BUMPERS) { - if ((victim->bumpers > 0) && (victim->bumpers <= bumpersRemoved)) + if ((victim->mo->health > 0) && (victim->mo->health <= damage)) { // +2 points for finishing off a player points = 2; @@ -4315,65 +4312,22 @@ void K_DebtStingPlayer(player_t *player, mobj_t *source) P_SetPlayerMobjState(player->mo, S_KART_SPINOUT); } -void K_HandleBumperChanges(player_t *player, UINT8 prevBumpers) +void K_TakeBumpersFromPlayer(player_t *player, player_t *victim, UINT8 amount) { - (void)player; - (void)prevBumpers; - - if (!(gametyperules & GTR_BUMPERS)) - { - // Bumpers aren't being used - return; - } - - K_CalculateBattleWanted(); - K_CheckBumpers(); -} - -UINT8 K_DestroyBumpers(player_t *player, UINT8 amount) -{ - UINT8 oldBumpers = player->bumpers; - - if (!(gametyperules & GTR_BUMPERS)) - { - return 0; - } - - amount = min(amount, player->bumpers); - - if (amount == 0) - { - return 0; - } - - player->bumpers -= amount; - K_HandleBumperChanges(player, oldBumpers); - - return amount; -} - -UINT8 K_TakeBumpersFromPlayer(player_t *player, player_t *victim, UINT8 amount) -{ - UINT8 oldPlayerBumpers = player->bumpers; - UINT8 oldVictimBumpers = victim->bumpers; + const UINT8 oldPlayerBumpers = K_Bumpers(player); UINT8 tookBumpers = 0; - if (!(gametyperules & GTR_BUMPERS)) - { - return 0; - } - - amount = min(amount, victim->bumpers); + amount = min(amount, K_Bumpers(victim)); if (amount == 0) { - return 0; + return; } - while ((tookBumpers < amount) && (victim->bumpers > 0)) + while (tookBumpers < amount) { - UINT8 newbumper = player->bumpers; + const UINT8 newbumper = (oldPlayerBumpers + tookBumpers); angle_t newangle, diff; fixed_t newx, newy; @@ -4415,24 +4369,14 @@ UINT8 K_TakeBumpersFromPlayer(player_t *player, player_t *victim, UINT8 amount) P_SetMobjState(newmo, S_BATTLEBUMPER1); } - player->bumpers++; - victim->bumpers--; tookBumpers++; } - if (tookBumpers == 0) - { - // No change occured. - return 0; - } + // :jartcookiedance: + player->mo->health += tookBumpers; // Play steal sound S_StartSound(player->mo, sfx_3db06); - - K_HandleBumperChanges(player, oldPlayerBumpers); - K_HandleBumperChanges(victim, oldVictimBumpers); - - return tookBumpers; } #define MINEQUAKEDIST 4096 @@ -7048,12 +6992,6 @@ mobj_t *K_FindJawzTarget(mobj_t *actor, player_t *source, angle_t range) } else { - if (player->bumpers <= 0) - { - // Don't pay attention to dead players - continue; - } - // Z pos too high/low if (abs(player->mo->z - (actor->z + actor->momz)) > FixedMul(RING_DIST/8, mapobjectscale)) { @@ -7813,12 +7751,12 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) if (!(gametyperules & GTR_KARMA) || (player->pflags & PF_ELIMINATED)) { - player->karmadelay = comebacktime; + player->karmadelay = 0; } else if (player->karmadelay > 0 && !P_PlayerInPain(player)) { player->karmadelay--; - if (P_IsDisplayPlayer(player) && player->bumpers <= 0 && player->karmadelay <= 0) + if (P_IsDisplayPlayer(player) && player->karmadelay <= 0) comebackshowninfo = true; // client has already seen the message } @@ -8011,7 +7949,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) if (player->eggmanexplode) { - if (player->spectator || ((gametyperules & GTR_BUMPERS) && player->bumpers <= 0)) + if (player->spectator) player->eggmanexplode = 0; else { @@ -9531,7 +9469,7 @@ void K_KartUpdatePosition(player_t *player) else if (yourEmeralds == myEmeralds) { // Bumpers are the second tier tie breaker - if (players[i].bumpers > player->bumpers) + if (K_Bumpers(&players[i]) > K_Bumpers(player)) { position++; } diff --git a/src/k_kart.h b/src/k_kart.h index 381395155..4d456320b 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -104,9 +104,7 @@ void K_UpdateStumbleIndicator(player_t *player); void K_UpdateSliptideZipIndicator(player_t *player); INT32 K_ExplodePlayer(player_t *player, mobj_t *inflictor, mobj_t *source); void K_DebtStingPlayer(player_t *player, mobj_t *source); -void K_HandleBumperChanges(player_t *player, UINT8 prevBumpers); -UINT8 K_DestroyBumpers(player_t *player, UINT8 amount); -UINT8 K_TakeBumpersFromPlayer(player_t *player, player_t *victim, UINT8 amount); +void K_TakeBumpersFromPlayer(player_t *player, player_t *victim, UINT8 amount); void K_MineFlashScreen(mobj_t *source); void K_SpawnMineExplosion(mobj_t *source, UINT8 color, tic_t delay); void K_RunFinishLineBeam(void); diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 98203ed51..d9958c738 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -398,8 +398,6 @@ static int player_get(lua_State *L) plr->roundscore = luaL_checkinteger(L, 3); else if (fastcmp(field,"emeralds")) lua_pushinteger(L, plr->emeralds); - else if (fastcmp(field,"bumpers")) - lua_pushinteger(L, plr->bumpers); else if (fastcmp(field,"karmadelay")) lua_pushinteger(L, plr->karmadelay); else if (fastcmp(field,"spheres")) @@ -782,8 +780,6 @@ static int player_set(lua_State *L) lua_pushinteger(L, plr->roundscore); else if (fastcmp(field,"emeralds")) plr->emeralds = luaL_checkinteger(L, 3); - else if (fastcmp(field,"bumpers")) - plr->bumpers = luaL_checkinteger(L, 3); else if (fastcmp(field,"karmadelay")) plr->karmadelay = luaL_checkinteger(L, 3); else if (fastcmp(field,"spheres")) diff --git a/src/p_enemy.c b/src/p_enemy.c index ccf5ff47a..9c457bdb5 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -13595,7 +13595,7 @@ void A_ReaperThinker(mobj_t *actor) continue; player = &players[i]; - if (player && player->mo && player->bumpers && player->score >= maxscore) + if (player && player->mo && K_Bumpers(player) && player->score >= maxscore) { targetplayermo = player->mo; maxscore = player->score; diff --git a/src/p_inter.c b/src/p_inter.c index 08e0c186a..ede06be36 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -115,13 +115,6 @@ boolean P_CanPickupItem(player_t *player, UINT8 weapon) if (player->exiting || mapreset || (player->pflags & PF_ELIMINATED)) return false; - if ((gametyperules & GTR_BUMPERS) // No bumpers in Match -#ifndef OTHERKARMAMODES - && !weapon -#endif - && player->bumpers <= 0) - return false; - if (weapon) { // Item slot already taken up @@ -286,9 +279,6 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (!P_CanPickupItem(player, 3) || (player->itemamount && player->itemtype != special->threshold)) return; - if ((gametyperules & GTR_BUMPERS) && player->bumpers <= 0) - return; - player->itemtype = special->threshold; if ((UINT16)(player->itemamount) + special->movecount > 255) player->itemamount = 255; @@ -321,9 +311,6 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) P_KillMobj(special, toucher, toucher, DMG_NORMAL); return; case MT_ITEMCAPSULE: - if ((gametyperules & GTR_BUMPERS) && player->bumpers <= 0) - return; - if (special->scale < special->extravalue1) // don't break it while it's respawning return; @@ -351,8 +338,6 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) return; if (player == special->target->player) return; - if (player->bumpers <= 0) - return; if (special->target->player->exiting || player->exiting) return; @@ -453,7 +438,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) S_StartSound(special, sfx_s1a2); return; case MT_CDUFO: // SRB2kart - if (special->fuse || !P_CanPickupItem(player, 1) || ((gametyperules & GTR_BUMPERS) && player->bumpers <= 0)) + if (special->fuse || !P_CanPickupItem(player, 1)) return; K_StartItemRoulette(player); @@ -1943,8 +1928,10 @@ static boolean P_KillPlayer(player_t *player, mobj_t *inflictor, mobj_t *source, switch (type) { case DMG_DEATHPIT: - // Battle - player->mo->health -= K_DestroyBumpers(player, 1); + if (gametyperules & GTR_BUMPERS) + { + player->mo->health--; + } if (player->mo->health <= 0) { @@ -2013,8 +2000,6 @@ static boolean P_KillPlayer(player_t *player, mobj_t *inflictor, mobj_t *source, player->pflags |= PF_ELIMINATED; } - K_DestroyBumpers(player, player->bumpers); - return true; } @@ -2170,16 +2155,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da boolean invincible = true; sfxenum_t sfx = sfx_None; - if (gametyperules & GTR_BUMPERS) - { - if (player->bumpers <= 0 && player->karmadelay) - { - // No bumpers & in WAIT, can't be hurt - K_DoInstashield(player); - return false; - } - } - else + if (!(gametyperules & GTR_BUMPERS)) { if (damagetype & DMG_STEAL) { @@ -2254,15 +2230,15 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da // We successfully damaged them! Give 'em some bumpers! if (type != DMG_STING && type != DMG_STUMBLE) { - UINT8 takeBumpers = 1; + damage = 1; if (damagetype & DMG_STEAL) { - takeBumpers = 2; + damage = 2; if (type == DMG_KARMA) { - takeBumpers = player->bumpers; + damage = K_Bumpers(player); } } else @@ -2270,7 +2246,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da if (type == DMG_KARMA) { // Take half of their bumpers for karma comeback damage - takeBumpers = max(1, player->bumpers / 2); + damage = max(1, K_Bumpers(player) / 2); } } @@ -2291,14 +2267,8 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da K_TryHurtSoundExchange(target, source); - K_BattleAwardHit(source->player, player, inflictor, takeBumpers); - damage = K_TakeBumpersFromPlayer(source->player, player, takeBumpers); - - if (type == DMG_KARMA) - { - // Destroy any remainder bumpers from the player for karma comeback damage - damage = K_DestroyBumpers(player, player->bumpers); - } + K_BattleAwardHit(source->player, player, inflictor, damage); + K_TakeBumpersFromPlayer(source->player, player, damage); if (damagetype & DMG_STEAL) { @@ -2314,10 +2284,6 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da Obj_GardenTopDestroy(source->player); } } - else - { - damage = K_DestroyBumpers(player, takeBumpers); - } if (!(damagetype & DMG_STEAL)) { diff --git a/src/p_map.c b/src/p_map.c index a02a5191d..37862c2da 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -1390,13 +1390,6 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing) return BMIT_CONTINUE; } - if ((gametyperules & GTR_BUMPERS) - && ((thing->player->bumpers && !tm.thing->player->bumpers) - || (tm.thing->player->bumpers && !thing->player->bumpers))) - { - return BMIT_CONTINUE; - } - // The bump has to happen last if (P_IsObjectOnGround(thing) && tm.thing->momz < 0 && tm.thing->player->trickpanel) { diff --git a/src/p_mobj.c b/src/p_mobj.c index 4dfc6f525..24f4c5d25 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -4588,9 +4588,6 @@ boolean P_SupermanLook4Players(mobj_t *actor) if (players[c].mo->health <= 0) continue; // dead - if ((gametyperules & GTR_BUMPERS) && players[c].bumpers <= 0) - continue; // other dead - playersinthegame[stop] = &players[c]; stop++; } @@ -6129,6 +6126,8 @@ static void P_MobjSceneryThink(mobj_t *mobj) if (mobj->target && !P_MobjWasRemoved(mobj->target) && mobj->target->player && mobj->target->health > 0 && !mobj->target->player->spectator) { + const UINT8 bumpers = K_Bumpers(mobj->target->player); + fixed_t rad = 32*mobj->target->scale; fixed_t offz; angle_t ang, diff; @@ -6138,10 +6137,10 @@ static void P_MobjSceneryThink(mobj_t *mobj) else ang = FixedAngle(mobj->info->speed); - if (mobj->target->player->bumpers <= 1) + if (bumpers <= 1) diff = 0; else - diff = FixedAngle(360*FRACUNIT/mobj->target->player->bumpers); + diff = FixedAngle(360*FRACUNIT / bumpers); ang = (ang*leveltime) + (diff * (mobj->threshold-1)); @@ -6178,9 +6177,9 @@ static void P_MobjSceneryThink(mobj_t *mobj) mobj->color = mobj->target->color; } - if (mobj->target->player->bumpers < 2) + if (bumpers < 2) P_SetMobjState(mobj, S_BATTLEBUMPER3); - else if (mobj->target->player->bumpers < 3) + else if (bumpers < 3) P_SetMobjState(mobj, S_BATTLEBUMPER2); else P_SetMobjState(mobj, S_BATTLEBUMPER1); @@ -6197,7 +6196,7 @@ static void P_MobjSceneryThink(mobj_t *mobj) P_SetThingPosition(mobj); } - if (mobj->target->player->bumpers <= mobj->threshold) + if (bumpers <= mobj->threshold) { // Do bumper destruction P_KillMobj(mobj, NULL, NULL, DMG_NORMAL); @@ -6231,7 +6230,7 @@ static void P_MobjSceneryThink(mobj_t *mobj) mobj->color = mobj->target->color; K_MatchGenericExtraFlags(mobj, mobj->target); - if ((!(gametyperules & GTR_BUMPERS) || mobj->target->player->bumpers <= 0) + if (!(gametyperules & GTR_BUMPERS) #if 1 // Set to 0 to test without needing to host || (P_IsDisplayPlayer(mobj->target->player)) #endif @@ -8428,7 +8427,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj) statenum_t state = (mobj->state-states); if (!mobj->target || !mobj->target->health || !mobj->target->player || mobj->target->player->spectator - || (!(gametyperules & GTR_BUMPERS) || mobj->target->player->bumpers)) + || !(gametyperules & GTR_BUMPERS)) { P_RemoveMobj(mobj); return false; @@ -11876,38 +11875,15 @@ void P_SpawnPlayer(INT32 playernum) P_SetScale(overheadarrow, mobj->destscale); } - if (gametyperules & GTR_BUMPERS) + if ((gametyperules & GTR_BUMPERS) && !p->spectator) { - if (p->spectator) + // At leveltime == 2, K_TimerInit will get called and reset + // the bumpers to the initial value for the level. + if (leveltime > 2) // Reset those bumpers! { - // HEY! No being cheap... - p->bumpers = 0; - } - else if ((p->bumpers > 0) || (leveltime < starttime) || (pcount <= 1)) - { - if ((leveltime < starttime) || (pcount <= 1)) // Start of the map? - { - if (leveltime > 2) // Reset those bumpers! - { - p->bumpers = K_StartingBumperCount(); - K_SpawnPlayerBattleBumpers(p); - } - else // temp, will get overwritten in K_BattleInit - { - p->bumpers = 1; - } - } - } - else if (p->bumpers <= 0) - { - p->bumpers = K_StartingBumperCount(); + mobj->health = K_BumpersToHealth(K_StartingBumperCount()); K_SpawnPlayerBattleBumpers(p); } - - if (p->bumpers > 0) - { - mobj->health = p->bumpers; - } } // I'm not refactoring the loop at the top of this file. diff --git a/src/p_saveg.c b/src/p_saveg.c index 3c130b453..21ceade91 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -381,7 +381,6 @@ static void P_NetArchivePlayers(savebuffer_t *save) WRITEUINT32(save->p, players[i].roundscore); WRITEUINT8(save->p, players[i].emeralds); - WRITEUINT8(save->p, players[i].bumpers); WRITEINT16(save->p, players[i].karmadelay); WRITEINT16(save->p, players[i].spheres); WRITEUINT32(save->p, players[i].spheredigestion); @@ -757,7 +756,6 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) players[i].roundscore = READUINT32(save->p); players[i].emeralds = READUINT8(save->p); - players[i].bumpers = READUINT8(save->p); players[i].karmadelay = READINT16(save->p); players[i].spheres = READINT16(save->p); players[i].spheredigestion = READUINT32(save->p); diff --git a/src/p_user.c b/src/p_user.c index 7b9d4205c..6175db565 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -526,10 +526,6 @@ INT32 P_GivePlayerSpheres(player_t *player, INT32 num_spheres) if (!(gametyperules & GTR_SPHERES)) // No spheres in Race mode) return 0; - // Not alive - if ((gametyperules & GTR_BUMPERS) && (player->bumpers <= 0)) - return 0; - if (num_spheres > 40) // Reached the cap, don't waste 'em! num_spheres = 40; else if (num_spheres < 0) @@ -4387,7 +4383,7 @@ void P_PlayerThink(player_t *player) || player->growshrinktimer > 0 // Grow doesn't flash either. || (player->respawn.state != RESPAWNST_NONE && player->respawn.truedeath == true) // Respawn timer (for drop dash effect) || (player->pflags & PF_NOCONTEST) // NO CONTEST explosion - || ((gametyperules & GTR_BUMPERS) && player->bumpers <= 0 && player->karmadelay))) + || player->karmadelay)) { if (player->flashing > 1 && player->flashing < K_GetKartFlashing(player) && (leveltime & 1)) From f6c8dd655a0977a654136b0a13404c5f521be252 Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 8 Mar 2023 18:11:50 -0800 Subject: [PATCH 06/27] Do not kill player because of damage outside of GTR_BUMPERS Removes more Karma Bomb crap. --- src/p_inter.c | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/src/p_inter.c b/src/p_inter.c index ede06be36..ea14261b4 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -2145,9 +2145,6 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da 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; - // Do not die from damage outside of bumpers health system - damage = 0; - // Check if the player is allowed to be damaged! // If not, then spawn the instashield effect instead. if (!force) @@ -2227,28 +2224,27 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da } } - // We successfully damaged them! Give 'em some bumpers! - if (type != DMG_STING && type != DMG_STUMBLE) + if (gametyperules & GTR_BUMPERS) { - damage = 1; - if (damagetype & DMG_STEAL) { + // Steals 2 bumpers damage = 2; + } + } + else + { + // Do not die from damage outside of bumpers health system + damage = 0; + } - if (type == DMG_KARMA) - { - damage = K_Bumpers(player); - } - } - else - { - if (type == DMG_KARMA) - { - // Take half of their bumpers for karma comeback damage - damage = max(1, K_Bumpers(player) / 2); - } - } + if (type == DMG_STING || type == DMG_STUMBLE) + { + damage = 0; + } + else + { + // We successfully damaged them! Give 'em some bumpers! if (source && source != player->mo && source->player) { From f69d70043c0b2ec94bbee9a3b0a45038b916ed29 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 7 Mar 2023 19:38:06 -0800 Subject: [PATCH 07/27] Keep player alive at zero bumpers Player have one more hit point than number of bumpers. Break the Capsules gives zero bumpers. --- src/k_battle.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/k_battle.c b/src/k_battle.c index 9b14491a9..550d1a140 100644 --- a/src/k_battle.c +++ b/src/k_battle.c @@ -39,7 +39,7 @@ UINT8 numtargets = 0; // Capsules busted INT32 K_StartingBumperCount(void) { if (battlecapsules) - return 1; // always 1 hit in Break the Capsules + return 0; // always 1 hit in Break the Capsules return cv_kartbumpers.value; } @@ -827,10 +827,10 @@ UINT8 K_Bumpers(player_t *player) return UINT8_MAX; } - return player->mo->health; + return (player->mo->health - 1); } INT32 K_BumpersToHealth(UINT8 bumpers) { - return bumpers; + return (bumpers + 1); } From 7669ae7bdea43d1427dd82b8aa2445559c6eb7c9 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 7 Mar 2023 23:01:56 -0800 Subject: [PATCH 08/27] Rankings HUD and minimap check for PF_ELIMINATED Dying to the Barrier: - crosses out your rankings icon - hides your minimap icon --- src/k_hud.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/k_hud.c b/src/k_hud.c index c68bbaa07..d4e305156 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -2025,7 +2025,7 @@ static boolean K_drawKartPositionFaces(void) if (i == strank) V_DrawScaledPatch(FACE_X, Y, V_HUDTRANS|V_SLIDEIN|V_SNAPTOLEFT, kp_facehighlight[(leveltime / 4) % 8]); - if ((gametyperules & GTR_BUMPERS) && players[rankplayer[i]].mo->health <= 0) + if ((gametyperules & GTR_BUMPERS) && (players[rankplayer[i]].pflags & PF_ELIMINATED)) V_DrawScaledPatch(FACE_X-4, Y-3, V_HUDTRANS|V_SLIDEIN|V_SNAPTOLEFT, kp_ranknobumpers); else { @@ -2358,7 +2358,7 @@ void K_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, IN if (tab[i].num == whiteplayer) V_DrawScaledPatch(x, y-4, 0, kp_facehighlight[(leveltime / 4) % 8]); - if ((gametyperules & GTR_BUMPERS) && players[tab[i].num].mo->health <= 0) + if ((gametyperules & GTR_BUMPERS) && (players[tab[i].num].pflags & PF_ELIMINATED)) V_DrawScaledPatch(x-4, y-7, 0, kp_ranknobumpers); else { @@ -3640,6 +3640,10 @@ static void K_drawKartMinimap(void) if (!players[i].mo || players[i].spectator || !players[i].mo->skin || players[i].exiting) continue; + // This player is out of the game! + if ((gametyperules & GTR_BUMPERS) && (players[i].pflags & PF_ELIMINATED)) + continue; + if (i == displayplayers[0] || i == displayplayers[1] || i == displayplayers[2] || i == displayplayers[3]) { // Draw display players on top of everything else @@ -3647,10 +3651,6 @@ static void K_drawKartMinimap(void) continue; } - // Now we know it's not a display player, handle non-local player exceptions. - if ((gametyperules & GTR_BUMPERS) && players[i].mo->health <= 0) - continue; - if (players[i].hyudorotimer > 0) { if (!((players[i].hyudorotimer < TICRATE/2 From 85a101cbd79e5f6018fc0470e93db567f57bd91c Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 8 Mar 2023 00:01:19 -0800 Subject: [PATCH 09/27] Replace battlecapsules || bossinfo.valid checks with K_Cooperative --- src/k_battle.c | 6 ++---- src/k_hud_track.cpp | 4 ++-- src/k_kart.c | 17 ++++++++++++++++- src/k_kart.h | 2 ++ src/p_inter.c | 2 +- 5 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/k_battle.c b/src/k_battle.c index 550d1a140..301eb81ab 100644 --- a/src/k_battle.c +++ b/src/k_battle.c @@ -95,8 +95,6 @@ void K_CheckBumpers(void) UINT8 nobumpers = 0; UINT8 eliminated = 0; - const boolean singleplayer = (battlecapsules || bossinfo.valid); - if (!(gametyperules & GTR_BUMPERS)) return; @@ -124,7 +122,7 @@ void K_CheckBumpers(void) } } - if (singleplayer + if (K_Cooperative() ? nobumpers > 0 && nobumpers >= numingame : eliminated >= numingame - 1) { @@ -135,7 +133,7 @@ void K_CheckBumpers(void) if (players[i].spectator) continue; - if (singleplayer) + if (K_Cooperative()) players[i].pflags |= PF_NOCONTEST; P_DoPlayerExit(&players[i]); diff --git a/src/k_hud_track.cpp b/src/k_hud_track.cpp index d16de57fd..4a5b34826 100644 --- a/src/k_hud_track.cpp +++ b/src/k_hud_track.cpp @@ -5,8 +5,8 @@ #include "core/static_vec.hpp" #include "k_battle.h" -#include "k_boss.h" #include "k_hud.h" +#include "k_kart.h" #include "k_objects.h" #include "m_fixed.h" #include "p_local.h" @@ -314,7 +314,7 @@ bool is_player_tracking_target(player_t *player = stplyr) return false; } - if (battlecapsules || bossinfo.valid) + if (K_Cooperative()) { return false; } diff --git a/src/k_kart.c b/src/k_kart.c index 1bb745f41..c1bbfebaf 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -11577,7 +11577,7 @@ UINT32 K_PointLimitForGametype(void) return cv_pointlimit.value; } - if (battlecapsules || bossinfo.valid) + if (K_Cooperative()) { return 0; } @@ -11601,4 +11601,19 @@ UINT32 K_PointLimitForGametype(void) return ptsCap; } +boolean K_Cooperative(void) +{ + if (battlecapsules) + { + return true; + } + + if (bossinfo.valid) + { + return true; + } + + return false; +} + //} diff --git a/src/k_kart.h b/src/k_kart.h index 4d456320b..b1f7ae42e 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -208,6 +208,8 @@ void K_EggmanTransfer(player_t *source, player_t *victim); tic_t K_TimeLimitForGametype(void); UINT32 K_PointLimitForGametype(void); +boolean K_Cooperative(void); + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/p_inter.c b/src/p_inter.c index ea14261b4..44fcc516b 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1382,7 +1382,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget P_PlayDeathSound(target); } - if (battlecapsules || bossinfo.valid) + if (K_Cooperative()) { target->player->pflags |= (PF_NOCONTEST|PF_ELIMINATED); } From 3dd7ece9dbb05e10ac63cb0b035b244224dbd42c Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 8 Mar 2023 00:04:33 -0800 Subject: [PATCH 10/27] Don't get points from killing other players in Break the Capsules or boss mode --- src/p_inter.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/p_inter.c b/src/p_inter.c index 44fcc516b..53a00df37 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -2263,7 +2263,11 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da K_TryHurtSoundExchange(target, source); - K_BattleAwardHit(source->player, player, inflictor, damage); + if (K_Cooperative() == false) + { + K_BattleAwardHit(source->player, player, inflictor, damage); + } + K_TakeBumpersFromPlayer(source->player, player, damage); if (damagetype & DMG_STEAL) From 6d305deacc6768c74f54f13b6067090ffedf2614 Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 8 Mar 2023 00:30:03 -0800 Subject: [PATCH 11/27] Fix 2P FINISH text scrolling Was using the wrong center offset. --- src/k_hud.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/k_hud.c b/src/k_hud.c index d4e305156..aaff0429f 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -3862,19 +3862,22 @@ static void K_drawKartFinish(boolean finish) //else -- 1/2p, scrolling FINISH { - INT32 x, xval, ox, interpx; + INT32 x, xval, ox, interpx, pwidth; x = ((vid.width<width)< x ? xval : x))/TICRATE; - ox = ((TICRATE - (timer - 1))*(xval > x ? xval : x))/TICRATE; + + pwidth = max(xval, x); + + x = ((TICRATE - timer) * pwidth) / TICRATE; + ox = ((TICRATE - (timer - 1)) * pwidth) / TICRATE; interpx = R_InterpolateFixed(ox, x); if (r_splitscreen && stplyr == &players[displayplayers[1]]) interpx = -interpx; - V_DrawFixedPatch(interpx + (STCD_X<>1), + V_DrawFixedPatch(interpx + (STCD_X<height)<<(FRACBITS-1)), FRACUNIT, splitflags, kptodraw[pnum], NULL); From 8ce90d77373575613af2b459c8bfa09fdcd17988 Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 8 Mar 2023 00:50:24 -0800 Subject: [PATCH 12/27] Battle: fix emerald win condition - winning player ALSO exits - winner gets 100 points --- src/k_battle.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/k_battle.c b/src/k_battle.c index 301eb81ab..9aef1c107 100644 --- a/src/k_battle.c +++ b/src/k_battle.c @@ -168,7 +168,7 @@ void K_CheckEmeralds(player_t *player) return; } - player->roundscore++; // lol + player->roundscore = 100; // lmao for (i = 0; i < MAXPLAYERS; i++) { @@ -177,11 +177,6 @@ void K_CheckEmeralds(player_t *player) continue; } - if (&players[i] == player) - { - continue; - } - P_DoPlayerExit(&players[i]); } } From 700331fdd6732f859a3e6a9dd39e56a50dfea052 Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 8 Mar 2023 01:02:52 -0800 Subject: [PATCH 13/27] Drop Targets collide with Battle Monitors and Break the Capsules Doesn't alter Capsule's path along its waypoints. I think the interaction with capsules is funny even if it wouldn't happen normally. --- src/p_map.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/p_map.c b/src/p_map.c index 37862c2da..72f40f41e 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -956,6 +956,8 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing) || tm.thing->type == MT_BANANA || tm.thing->type == MT_EGGMANITEM || tm.thing->type == MT_BALLHOG || tm.thing->type == MT_SSMINE || tm.thing->type == MT_LANDMINE || tm.thing->type == MT_SINK || tm.thing->type == MT_GARDENTOP + || tm.thing->type == MT_MONITOR + || tm.thing->type == MT_BATTLECAPSULE || (tm.thing->type == MT_PLAYER))) { // see if it went over / under @@ -971,6 +973,8 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing) || thing->type == MT_BANANA || thing->type == MT_EGGMANITEM || thing->type == MT_BALLHOG || thing->type == MT_SSMINE || thing->type == MT_LANDMINE || thing->type == MT_SINK || thing->type == MT_GARDENTOP + || thing->type == MT_MONITOR + || thing->type == MT_BATTLECAPSULE || (thing->type == MT_PLAYER))) { // see if it went over / under From 5cebe7ab35710a0568d3b0a27bb21118412d82b8 Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 8 Mar 2023 01:12:34 -0800 Subject: [PATCH 14/27] Battle: count emeralds inside of monitors during Overtime too Don't spawn extra emeralds during Overtime since some monitors could survive. --- src/k_battle.c | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/k_battle.c b/src/k_battle.c index 9aef1c107..c2fa396e1 100644 --- a/src/k_battle.c +++ b/src/k_battle.c @@ -312,6 +312,21 @@ static inline boolean IsOnInterval(tic_t interval) return ((leveltime - starttime) % interval) == 0; } +static UINT32 CountEmeraldsSpawned(const mobj_t *mo) +{ + switch (mo->type) + { + case MT_EMERALD: + return mo->extravalue1; + + case MT_MONITOR: + return Obj_MonitorGetEmerald(mo); + + default: + return 0U; + } +} + void K_RunPaperItemSpawners(void) { const boolean overtime = (battleovertime.enabled >= 10*TICRATE); @@ -375,10 +390,7 @@ void K_RunPaperItemSpawners(void) mo = (mobj_t *)th; - if (mo->type == MT_EMERALD) - { - emeraldsSpawned |= mo->extravalue1; - } + emeraldsSpawned |= CountEmeraldsSpawned(mo); } if (canmakeemeralds) @@ -437,15 +449,7 @@ void K_RunPaperItemSpawners(void) mo = (mobj_t *)th; - if (mo->type == MT_EMERALD) - { - emeraldsSpawned |= mo->extravalue1; - } - - if (mo->type == MT_MONITOR) - { - emeraldsSpawned |= Obj_MonitorGetEmerald(mo); - } + emeraldsSpawned |= CountEmeraldsSpawned(mo); if (mo->type != MT_PAPERITEMSPOT) continue; From c781780ff8bf90addf2db9ef5360a674e2fb015c Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 8 Mar 2023 01:18:31 -0800 Subject: [PATCH 15/27] Lose bumpers when using the respawn command --- src/d_netcmd.c | 2 +- src/g_demo.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index a1fae65f2..bed75ff2f 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -3370,7 +3370,7 @@ static void Got_Respawn(UINT8 **cp, INT32 playernum) if (!P_IsObjectOnGround(players[respawnplayer].mo)) return; - K_DoIngameRespawn(&players[respawnplayer]); + P_DamageMobj(players[respawnplayer].mo, NULL, NULL, 1, DMG_DEATHPIT); demo_extradata[playernum] |= DXD_RESPAWN; } } diff --git a/src/g_demo.c b/src/g_demo.c index 37504a6c1..2589ade80 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -356,7 +356,7 @@ void G_ReadDemoExtraData(void) if (players[p].mo) { // Is this how this should work..? - K_DoIngameRespawn(&players[p]); + P_DamageMobj(players[p].mo, NULL, NULL, 1, DMG_DEATHPIT); } } if (extradata & DXD_WEAPONPREF) From b04433bc7bfd58ae502526589eadb0cd7dc2d8a2 Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 8 Mar 2023 17:35:19 -0800 Subject: [PATCH 16/27] Fix TARGET visibility on players holding emeralds --- src/k_hud_track.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/k_hud_track.cpp b/src/k_hud_track.cpp index 4a5b34826..977c65ffa 100644 --- a/src/k_hud_track.cpp +++ b/src/k_hud_track.cpp @@ -338,6 +338,12 @@ bool is_player_tracking_target(player_t *player = stplyr) return false; } + // WANTED player sees TARGETs on players holding emeralds + if (player->emeralds != 0 && K_IsPlayerWanted(stplyr)) + { + return true; + } + return K_IsPlayerWanted(player); } From cdecada971566d48cd61473966783e5a5ec53934 Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 8 Mar 2023 23:31:34 -0800 Subject: [PATCH 17/27] Battle: TARGET on the player with 6 emeralds --- src/k_hud_track.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/k_hud_track.cpp b/src/k_hud_track.cpp index 977c65ffa..e6a7df1ed 100644 --- a/src/k_hud_track.cpp +++ b/src/k_hud_track.cpp @@ -338,10 +338,21 @@ bool is_player_tracking_target(player_t *player = stplyr) return false; } - // WANTED player sees TARGETs on players holding emeralds if (player->emeralds != 0 && K_IsPlayerWanted(stplyr)) { - return true; + // The player who is about to win because of emeralds + // gets a TARGET on them + if (K_NumEmeralds(player) == 6) // 6 out of 7 + { + return true; + } + + // WANTED player sees TARGETs on players holding + // emeralds + if (K_IsPlayerWanted(stplyr)) + { + return true; + } } return K_IsPlayerWanted(player); From b2a6ffecf92cebfb096a974bbbd3bc8122291d63 Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 11 Mar 2023 05:11:35 -0800 Subject: [PATCH 18/27] Refactor player->invulnhitlag -> player->nullHitlag - Move condition for whether hitlag came from a constant damage source into P_DamageMobj directly. Should be more accurate if a player is dealt brand new damage, the constant damage still won't count. - player->invulnhitlag renamed to player->nullHitlag --- src/d_player.h | 2 +- src/k_kart.c | 24 ++++----------------- src/lua_playerlib.c | 8 +++---- src/p_inter.c | 52 ++++++++++++++++++++++++++++++++++----------- src/p_saveg.c | 4 ++-- 5 files changed, 51 insertions(+), 39 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index be2708614..baa83d1a7 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -490,7 +490,7 @@ struct player_t UINT16 spinouttimer; // Spin-out from a banana peel or oil slick (was "pw_bananacam") UINT8 spinouttype; // Determines the mode of spinout/wipeout, see kartspinoutflags_t UINT8 instashield; // Instashield no-damage animation timer - INT32 invulnhitlag; // Numbers of tics of hitlag added this tic for "potential" damage -- not real damage + INT32 nullHitlag; // Numbers of tics of hitlag that will ultimately be ignored by subtracting from hitlag UINT8 wipeoutslow; // Timer before you slowdown when getting wiped out UINT8 justbumped; // Prevent players from endlessly bumping into each other UINT8 tumbleBounces; diff --git a/src/k_kart.c b/src/k_kart.c index c1bbfebaf..9a2a886eb 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -8290,30 +8290,14 @@ void K_KartPlayerAfterThink(player_t *player) K_LookForRings(player->mo); } - if (player->invulnhitlag > 0) + if (player->nullHitlag > 0) { - // Hitlag from what would normally be damage but the - // player was invulnerable. - // - // If we're constantly getting hit the same number of - // times, we're probably standing on a damage floor. - // - // Checking if we're hit more than before ensures - // that: - // - // 1) repeating damage doesn't count - // 2) new damage sources still count - - if (player->timeshit <= player->timeshitprev) + if (!P_MobjWasRemoved(player->mo)) { - if (!P_MobjWasRemoved(player->mo)) - { - player->mo->hitlag -= player->invulnhitlag; - player->mo->eflags &= ~(MFE_DAMAGEHITLAG); - } + player->mo->hitlag -= player->nullHitlag; } - player->invulnhitlag = 0; + player->nullHitlag = 0; } } diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index d9958c738..327641362 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -232,8 +232,8 @@ static int player_get(lua_State *L) lua_pushinteger(L, plr->spinouttimer); else if (fastcmp(field,"instashield")) lua_pushinteger(L, plr->instashield); - else if (fastcmp(field,"invulnhitlag")) - lua_pushinteger(L, plr->invulnhitlag); + else if (fastcmp(field,"nullHitlag")) + lua_pushinteger(L, plr->nullHitlag); else if (fastcmp(field,"wipeoutslow")) lua_pushinteger(L, plr->wipeoutslow); else if (fastcmp(field,"justbumped")) @@ -614,8 +614,8 @@ static int player_set(lua_State *L) plr->spinouttimer = luaL_checkinteger(L, 3); else if (fastcmp(field,"instashield")) plr->instashield = luaL_checkinteger(L, 3); - else if (fastcmp(field,"invulnhitlag")) - plr->invulnhitlag = luaL_checkinteger(L, 3); + else if (fastcmp(field,"nullHitlag")) + plr->nullHitlag = luaL_checkinteger(L, 3); else if (fastcmp(field,"wipeoutslow")) plr->wipeoutslow = luaL_checkinteger(L, 3); else if (fastcmp(field,"justbumped")) diff --git a/src/p_inter.c b/src/p_inter.c index 53a00df37..e0bc2eed1 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -2003,6 +2003,43 @@ static boolean P_KillPlayer(player_t *player, mobj_t *inflictor, mobj_t *source, return true; } +static void AddTimesHit(player_t *player) +{ + const INT32 oldtimeshit = player->timeshit; + + player->timeshit++; + + // overflow prevention + if (player->timeshit < oldtimeshit) + { + player->timeshit = oldtimeshit; + } +} + +static void AddNullHitlag(player_t *player, tic_t oldHitlag) +{ + if (player == NULL) + { + return; + } + + // Hitlag from what would normally be damage but the + // player was invulnerable. + // + // If we're constantly getting hit the same number of + // times, we're probably standing on a damage floor. + // + // Checking if we're hit more than before ensures that: + // + // 1) repeating damage doesn't count + // 2) new damage sources still count + + if (player->timeshit <= player->timeshitprev) + { + player->nullHitlag += (player->mo->hitlag - oldHitlag); + } +} + /** Damages an object, which may or may not be a player. * For melee attacks, source and inflictor are the same. * @@ -2104,17 +2141,8 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da if (player) // Player is the target { - { - const INT32 oldtimeshit = player->timeshit; - player->timeshit++; - - // overflow prevention - if (player->timeshit < oldtimeshit) - { - player->timeshit = oldtimeshit; - } - } + AddTimesHit(player); if (player->pflags & PF_GODMODE) return false; @@ -2179,12 +2207,12 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da if (invincible && type != DMG_STUMBLE) { - const INT32 oldhitlag = target->hitlag; + const INT32 oldHitlag = target->hitlag; laglength = max(laglength / 2, 1); K_SetHitLagForObjects(target, inflictor, laglength, false); - player->invulnhitlag += (target->hitlag - oldhitlag); + AddNullHitlag(player, oldHitlag); if (player->timeshit > player->timeshitprev) { diff --git a/src/p_saveg.c b/src/p_saveg.c index 21ceade91..8f142fa20 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -276,7 +276,7 @@ static void P_NetArchivePlayers(savebuffer_t *save) WRITEUINT16(save->p, players[i].spinouttimer); WRITEUINT8(save->p, players[i].spinouttype); WRITEUINT8(save->p, players[i].instashield); - WRITEINT32(save->p, players[i].invulnhitlag); + WRITEINT32(save->p, players[i].nullHitlag); WRITEUINT8(save->p, players[i].wipeoutslow); WRITEUINT8(save->p, players[i].justbumped); WRITEUINT8(save->p, players[i].tumbleBounces); @@ -651,7 +651,7 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) players[i].spinouttimer = READUINT16(save->p); players[i].spinouttype = READUINT8(save->p); players[i].instashield = READUINT8(save->p); - players[i].invulnhitlag = READINT32(save->p); + players[i].nullHitlag = READINT32(save->p); players[i].wipeoutslow = READUINT8(save->p); players[i].justbumped = READUINT8(save->p); players[i].tumbleBounces = READUINT8(save->p); From 622bfa9512147624ac949ae584a41f8ba0476544 Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 11 Mar 2023 05:14:37 -0800 Subject: [PATCH 19/27] Count nullHitlag for inflictor player too Cancel hitlag of inflictor player too if the inflictor is a constant damage source and the target is invincible. --- src/d_player.h | 6 ++++-- src/p_inter.c | 10 +++++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index baa83d1a7..6641b30d6 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -641,8 +641,10 @@ struct player_t INT16 lastsidehit, lastlinehit; - // These track how many things tried to damage you, not - // whether you actually took damage. + // TimesHit tracks how many times something tried to + // damage you or how many times you tried to damage + // something else. It does not track whether damage was + // actually dealt. UINT8 timeshit; // times hit this tic UINT8 timeshitprev; // times hit before // That's TIMES HIT, not TIME SHIT, you doofus! -- in memoriam diff --git a/src/p_inter.c b/src/p_inter.c index e0bc2eed1..df9e69b71 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -2060,6 +2060,7 @@ static void AddNullHitlag(player_t *player, tic_t oldHitlag) boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype) { player_t *player; + player_t *playerInflictor; boolean force = false; INT32 laglength = 6; @@ -2138,10 +2139,15 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da } player = target->player; + playerInflictor = inflictor ? inflictor->player : NULL; + + if (playerInflictor) + { + AddTimesHit(playerInflictor); + } if (player) // Player is the target { - AddTimesHit(player); if (player->pflags & PF_GODMODE) @@ -2208,11 +2214,13 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da if (invincible && type != DMG_STUMBLE) { const INT32 oldHitlag = target->hitlag; + const INT32 oldHitlagInflictor = inflictor ? inflictor->hitlag : 0; laglength = max(laglength / 2, 1); K_SetHitLagForObjects(target, inflictor, laglength, false); AddNullHitlag(player, oldHitlag); + AddNullHitlag(playerInflictor, oldHitlagInflictor); if (player->timeshit > player->timeshitprev) { From ee4011cca6148908cc7a0e35815241e027833344 Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 11 Mar 2023 05:20:01 -0800 Subject: [PATCH 20/27] Don't pause mobj thinker for player whose hitlag will be cancelled anyway --- src/p_mobj.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index 24f4c5d25..8799901da 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -9843,7 +9843,7 @@ void P_MobjThinker(mobj_t *mobj) return; // Don't run any thinker code while in hitlag - if (mobj->hitlag > 0) + if ((mobj->player ? mobj->hitlag - mobj->player->nullHitlag : mobj->hitlag) > 0) { mobj->eflags |= MFE_PAUSED; mobj->hitlag--; From 8c1771112cd9f6436b3a4a74baee3df73368cbef Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 11 Mar 2023 04:35:59 -0800 Subject: [PATCH 21/27] Fix Bubble Shield duplicate collisions Bubble Shield could collide with the same object up to 5 times per tic! (3 times at least!) 1) P_CheckPosition from MFE_ONGROUND being unset. 2) P_CheckPosition AGAIN from MFE_ONGROUND being unset while literally being on the ground. This one's probably a bug in general but it's beyond the scope of this commit. It's also scary movement code, yiiiikes... 3) P_MoveOrigin to teleport the Bubble to its holder's position. 4) If something moves into the Bubble. 5) If something moves into the player holding the Bubble. This generated extra unwated hitlag, especially noticeable against invincible players. To reduce these to one collision only, the Bubble is now MF_NOCLIPTHING except while calling P_MoveOrigin. The player's own hitbox is also disabled for Bubble collisions. --- src/info.c | 2 +- src/k_collide.c | 13 ++++++++++++- src/p_mobj.c | 2 ++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/info.c b/src/info.c index cb00a3bff..aa2befa3d 100644 --- a/src/info.c +++ b/src/info.c @@ -23907,7 +23907,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 16, // mass 0, // damage sfx_None, // activesound - MF_SOLID|MF_NOCLIP|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags + MF_SOLID|MF_NOCLIP|MF_NOCLIPTHING|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags S_NULL // raisestate }, diff --git a/src/k_collide.c b/src/k_collide.c index 7964c3140..307d7c85d 100644 --- a/src/k_collide.c +++ b/src/k_collide.c @@ -678,6 +678,17 @@ void K_LightningShieldAttack(mobj_t *actor, fixed_t size) boolean K_BubbleShieldCollide(mobj_t *t1, mobj_t *t2) { + if (t1->type == MT_PLAYER) + { + // Bubble Shield already has a hitbox, and it gets + // teleported every tic so the Bubble itself will + // always make contact with other objects. + // + // Therefore, we don't need a second, smaller hitbox + // on the player. It'll just cause unwanted hitlag. + return true; + } + if (t2->type == MT_PLAYER) { // Counter desyncs @@ -697,7 +708,7 @@ boolean K_BubbleShieldCollide(mobj_t *t1, mobj_t *t2) } // Player Damage - P_DamageMobj(t2, ((t1->type == MT_BUBBLESHIELD) ? t1->target : t1), t1, 1, DMG_NORMAL|DMG_WOMBO); + P_DamageMobj(t2, t1->target, t1, 1, DMG_NORMAL|DMG_WOMBO); S_StartSound(t1, sfx_s3k44); } else diff --git a/src/p_mobj.c b/src/p_mobj.c index 8799901da..c5d70ead5 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -8246,7 +8246,9 @@ static boolean P_MobjRegularThink(mobj_t *mobj) desty = mobj->target->y; } + mobj->flags &= ~(MF_NOCLIPTHING); P_MoveOrigin(mobj, destx, desty, mobj->target->z); + mobj->flags |= MF_NOCLIPTHING; break; } case MT_FLAMESHIELD: From d28b1615cfb027a9b17e1d927a97ba77894cef76 Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 9 Mar 2023 03:52:56 -0800 Subject: [PATCH 22/27] Don't let mine explosion's own hitbox receive hitlag Don't extend the time that its hitbox remains. --- src/k_collide.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/k_collide.c b/src/k_collide.c index 307d7c85d..b6ba1aa63 100644 --- a/src/k_collide.c +++ b/src/k_collide.c @@ -313,6 +313,10 @@ tic_t K_MineExplodeAttack(mobj_t *actor, fixed_t size, boolean spin) // Set this flag to ensure that the inital action won't be triggered twice. actor->flags2 |= MF2_DEBRIS; + // Set this flag to ensure the hitbox timer doesn't get extended with every player hit + actor->flags |= MF_NOHITLAGFORME; + actor->hitlag = 0; // same deal + if (!spin) { if (minehitlag == 0) From ec6ffbf0726857b23d9a278b25407e974576669b Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 10 Mar 2023 05:58:26 -0800 Subject: [PATCH 23/27] Let wombo damage work again Damage can't be ignored entirely while in hitlag because that defeats stacked hitlag (wombo combo). But it should be ignored if in an invincible state. --- src/p_inter.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/p_inter.c b/src/p_inter.c index df9e69b71..7e5cc68ae 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -2118,9 +2118,6 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da if (!(target->flags & MF_SHOOTABLE)) return false; // shouldn't happen... } - - if (!(damagetype & DMG_DEATHMASK) && (target->eflags & MFE_PAUSED)) - return false; } if (target->flags2 & MF2_SKULLFLY) @@ -2216,6 +2213,24 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da const INT32 oldHitlag = target->hitlag; const INT32 oldHitlagInflictor = inflictor ? inflictor->hitlag : 0; + // Damage during hitlag should be a no-op + // for invincibility states because there + // are no flashing tics. If the damage is + // from a constant source, a deadlock + // would occur. + + if (target->eflags & MFE_PAUSED) + { + player->timeshit--; // doesn't count + + if (playerInflictor) + { + playerInflictor->timeshit--; + } + + return false; + } + laglength = max(laglength / 2, 1); K_SetHitLagForObjects(target, inflictor, laglength, false); From 264e445f0c1a0476af8f1806deb098c04ed8113b Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 8 Mar 2023 19:42:21 -0800 Subject: [PATCH 24/27] Add MF2_ALREADYHIT; don't deal damage for multiple wombos within the same tic These wombos still create hitlag. --- src/deh_tables.c | 2 +- src/p_inter.c | 7 +++++++ src/p_mobj.c | 2 ++ src/p_mobj.h | 2 +- 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/deh_tables.c b/src/deh_tables.c index 9cae9323e..57ef8d885 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -5730,7 +5730,7 @@ const char *const MOBJFLAG2_LIST[] = { "JUSTATTACKED", // can be pushed by other moving mobjs "FIRING", // turret fire "SUPERFIRE", // Firing something with Super Sonic-stopping properties. Or, if mobj has MF_MISSILE, this is the actual fire from it. - "\x01", // free: 1<<20 (name un-matchable) + "ALREADYHIT", // This object was already damaged THIS tic, resets even during hitlag "STRONGBOX", // Flag used for "strong" random monitors. "OBJECTFLIP", // Flag for objects that always have flipped gravity. "SKULLFLY", // Special handling: skull in flight. diff --git a/src/p_inter.c b/src/p_inter.c index 7e5cc68ae..7d558c882 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -2272,6 +2272,11 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da K_DoInstashield(player); return false; } + else if (target->flags2 & MF2_ALREADYHIT) // do not deal extra damage in the same tic + { + K_SetHitLagForObjects(target, inflictor, laglength, true); + return false; + } } } @@ -2452,6 +2457,8 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da K_SetHitLagForObjects(target, inflictor, laglength, true); + target->flags2 |= MF2_ALREADYHIT; + if (target->health <= 0) { P_KillMobj(target, inflictor, source, damagetype); diff --git a/src/p_mobj.c b/src/p_mobj.c index c5d70ead5..906d95cc5 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -9844,6 +9844,8 @@ void P_MobjThinker(mobj_t *mobj) if ((mobj->flags & MF_BOSS) && mobj->spawnpoint && (bossdisabled & (1<spawnpoint->args[0]))) return; + mobj->flags2 &= ~(MF2_ALREADYHIT); + // Don't run any thinker code while in hitlag if ((mobj->player ? mobj->hitlag - mobj->player->nullHitlag : mobj->hitlag) > 0) { diff --git a/src/p_mobj.h b/src/p_mobj.h index d5dc67a3e..429f48812 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -191,7 +191,7 @@ typedef enum MF2_JUSTATTACKED = 1<<16, // can be pushed by other moving mobjs MF2_FIRING = 1<<17, // turret fire MF2_SUPERFIRE = 1<<18, // Firing something with Super Sonic-stopping properties. Or, if mobj has MF_MISSILE, this is the actual fire from it. - // free: 1<<19 + MF2_ALREADYHIT = 1<<19, // This object was already damaged THIS tic, resets even during hitlag MF2_STRONGBOX = 1<<20, // Flag used for "strong" random monitors. MF2_OBJECTFLIP = 1<<21, // Flag for objects that always have flipped gravity. MF2_SKULLFLY = 1<<22, // Special handling: skull in flight. From e5e23e788a6edc56c3ea2a22e4dd4c2607e2c8a3 Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 8 Mar 2023 19:43:04 -0800 Subject: [PATCH 25/27] Remove hitlag threshold condition from items This condition blocked items from doing damage after just being thrown. The intention was to not let shotgun Ballhog instakill the player. This is now prevented by MF2_ALREADYHIT instead. blame cab1af549 --- src/k_collide.c | 18 ------------------ src/objects/orbinaut.c | 6 ------ 2 files changed, 24 deletions(-) diff --git a/src/k_collide.c b/src/k_collide.c index b6ba1aa63..95d62b419 100644 --- a/src/k_collide.c +++ b/src/k_collide.c @@ -43,9 +43,6 @@ boolean K_BananaBallhogCollide(mobj_t *t1, mobj_t *t2) { boolean damageitem = false; - if ((t1->threshold > 0 && t2->hitlag > 0) || (t2->threshold > 0 && t1->hitlag > 0)) - return true; - 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; @@ -133,9 +130,6 @@ boolean K_BananaBallhogCollide(mobj_t *t1, mobj_t *t2) boolean K_EggItemCollide(mobj_t *t1, mobj_t *t2) { - if ((t1->threshold > 0 && t2->hitlag > 0) || (t2->threshold > 0 && t1->hitlag > 0)) - return true; - // Push fakes out of other item boxes if (t2->type == MT_RANDOMITEM || t2->type == MT_EGGMANITEM) { @@ -334,9 +328,6 @@ tic_t K_MineExplodeAttack(mobj_t *actor, fixed_t size, boolean spin) boolean K_MineCollide(mobj_t *t1, mobj_t *t2) { - if ((t1->threshold > 0 && t2->hitlag > 0) || (t2->threshold > 0 && t1->hitlag > 0)) - return true; - 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; @@ -388,9 +379,6 @@ boolean K_MineCollide(mobj_t *t1, mobj_t *t2) boolean K_LandMineCollide(mobj_t *t1, mobj_t *t2) { - if ((t1->threshold > 0 && t2->hitlag > 0) || (t2->threshold > 0 && t1->hitlag > 0)) - return true; - 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; @@ -469,9 +457,6 @@ boolean K_DropTargetCollide(mobj_t *t1, mobj_t *t2) { mobj_t *draggeddroptarget = (t1->type == MT_DROPTARGET_SHIELD) ? t1->target : NULL; - if ((t1->threshold > 0 && t2->hitlag > 0) || (t2->threshold > 0 && t1->hitlag > 0)) - return true; - if (((t1->target == t2) || (t1->target == t2->target)) && ((t1->threshold > 0 && t2->type == MT_PLAYER) || (t2->type != MT_PLAYER && t2->threshold > 0))) return true; @@ -743,9 +728,6 @@ boolean K_BubbleShieldCollide(mobj_t *t1, mobj_t *t2) boolean K_KitchenSinkCollide(mobj_t *t1, mobj_t *t2) { - if ((t1->threshold > 0 && t2->hitlag > 0) || (t2->threshold > 0 && t1->hitlag > 0)) - return true; - 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; diff --git a/src/objects/orbinaut.c b/src/objects/orbinaut.c index 6f9ba3788..caed22068 100644 --- a/src/objects/orbinaut.c +++ b/src/objects/orbinaut.c @@ -167,12 +167,6 @@ boolean Obj_OrbinautJawzCollide(mobj_t *t1, mobj_t *t2) boolean tumbleitem = false; boolean sprung = false; - if ((orbinaut_selfdelay(t1) > 0 && t2->hitlag > 0) - || (orbinaut_selfdelay(t2) > 0 && t1->hitlag > 0)) - { - return true; - } - if (t1->health <= 0 || t2->health <= 0) { return true; From e85b769dc5670a5c7e84dca089903fbf5cb9e13a Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 11 Mar 2023 18:54:31 -0800 Subject: [PATCH 26/27] Fix Bubble blowup vs Invincibility sfx spam --- src/k_collide.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/k_collide.c b/src/k_collide.c index 95d62b419..fe4a8ae6b 100644 --- a/src/k_collide.c +++ b/src/k_collide.c @@ -698,7 +698,12 @@ boolean K_BubbleShieldCollide(mobj_t *t1, mobj_t *t2) // Player Damage P_DamageMobj(t2, t1->target, t1, 1, DMG_NORMAL|DMG_WOMBO); - S_StartSound(t1, sfx_s3k44); + + if (t2->player->timeshit > t2->player->timeshitprev) + { + // Don't play from t1 else it gets cut out... for some reason. + S_StartSound(t2, sfx_s3k44); + } } else { From 34e322b7a1dd8578c4e568a91bc071530fed1c93 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 12 Mar 2023 20:55:07 -0700 Subject: [PATCH 27/27] Ignore non-combo damage during hitlag Fixes stumble deadlock --- src/p_inter.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/p_inter.c b/src/p_inter.c index 7d558c882..f7b3baa00 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -2265,6 +2265,11 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da allowcombo = false; } + if (allowcombo == false && (target->eflags & MFE_PAUSED)) + { + return false; + } + // DMG_EXPLODE excluded from flashtic checks to prevent dodging eggbox/SPB with weak spinout if ((target->hitlag == 0 || allowcombo == false) && player->flashing > 0 && type != DMG_EXPLODE && type != DMG_STUMBLE) {