diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 258521d27..4e38617b6 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -3374,7 +3374,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/d_player.h b/src/d_player.h index 4a8013f89..6641b30d6 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; @@ -602,9 +602,7 @@ struct player_t UINT32 roundscore; // battle score this round UINT8 emeralds; - UINT8 bumpers; INT16 karmadelay; - tic_t overtimekarma; // time to live in overtime comeback INT16 spheres; tic_t spheredigestion; @@ -643,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/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/g_demo.c b/src/g_demo.c index 617dc386b..2589ade80 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; @@ -355,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) @@ -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 diff --git a/src/g_game.c b/src/g_game.c index 93a318e40..452740936 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()) { @@ -2439,7 +2432,6 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) INT32 itemtype; INT32 itemamount; INT32 growshrinktimer; - INT32 bumper; boolean songcredit = false; UINT16 nocontrol; INT32 khudfault; @@ -2511,7 +2503,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; @@ -2556,7 +2547,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; @@ -2654,9 +2644,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) p->itemtype = itemtype; p->itemamount = itemamount; p->growshrinktimer = growshrinktimer; - p->bumpers = bumper; - p->karmadelay = comebacktime; - p->overtimekarma = 0; + p->karmadelay = 0; p->eggmanblame = -1; p->lastdraft = -1; p->karthud[khud_fault] = khudfault; diff --git a/src/info.c b/src/info.c index 60516c822..b462a8ba9 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_battle.c b/src/k_battle.c index 38b86abaf..c2fa396e1 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; } @@ -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; @@ -113,7 +111,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++; } @@ -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]); @@ -170,7 +168,7 @@ void K_CheckEmeralds(player_t *player) return; } - player->roundscore++; // lol + player->roundscore = 100; // lmao for (i = 0; i < MAXPLAYERS; i++) { @@ -179,11 +177,6 @@ void K_CheckEmeralds(player_t *player) continue; } - if (&players[i] == player) - { - continue; - } - P_DoPlayerExit(&players[i]); } } @@ -319,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); @@ -362,7 +370,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; } @@ -382,10 +390,7 @@ void K_RunPaperItemSpawners(void) mo = (mobj_t *)th; - if (mo->type == MT_EMERALD) - { - emeraldsSpawned |= mo->extravalue1; - } + emeraldsSpawned |= CountEmeraldsSpawned(mo); } if (canmakeemeralds) @@ -444,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; @@ -738,16 +735,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 +785,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 - 1); +} + +INT32 K_BumpersToHealth(UINT8 bumpers) +{ + return (bumpers + 1); +} 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 12757a2a3..fe4a8ae6b 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) { @@ -154,14 +148,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) { @@ -182,10 +169,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) { @@ -323,6 +307,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) @@ -340,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; @@ -394,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; @@ -475,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; @@ -688,6 +667,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 @@ -707,8 +697,13 @@ 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); - S_StartSound(t1, sfx_s3k44); + P_DamageMobj(t2, t1->target, t1, 1, DMG_NORMAL|DMG_WOMBO); + + 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 { @@ -738,9 +733,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/k_hud.c b/src/k_hud.c index 7eeb9f596..bfa92b4c8 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]].pflags & PF_ELIMINATED)) 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].pflags & PF_ELIMINATED)) 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,17 +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); - 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", bumpers, maxbumper)); } } } @@ -3366,12 +3368,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 @@ -3644,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 @@ -3651,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].bumpers <= 0) - continue; - if (players[i].hyudorotimer > 0) { if (!((players[i].hyudorotimer < TICRATE/2 @@ -3866,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); @@ -4160,7 +4159,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 @@ -4894,8 +4893,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_hud_track.cpp b/src/k_hud_track.cpp index d16de57fd..e6a7df1ed 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; } @@ -338,6 +338,23 @@ bool is_player_tracking_target(player_t *player = stplyr) return false; } + if (player->emeralds != 0 && K_IsPlayerWanted(stplyr)) + { + // 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); } diff --git a/src/k_kart.c b/src/k_kart.c index b6f82f113..09d552b1a 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; @@ -3262,11 +3259,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 +3318,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) { @@ -3647,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; @@ -3685,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; @@ -4330,81 +4316,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) { - if (!(gametyperules & GTR_BUMPERS)) - { - // Bumpers aren't being used - 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 (battlecapsules || bossinfo.valid) - { - player->pflags |= (PF_NOCONTEST|PF_ELIMINATED); - } - } - - 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; @@ -4446,24 +4373,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 @@ -5127,8 +5044,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) @@ -7080,12 +6996,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)) { @@ -7801,13 +7711,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 @@ -7852,12 +7755,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 } @@ -8021,14 +7924,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; @@ -8058,7 +7953,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 { @@ -8399,30 +8294,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; } } @@ -9583,7 +9462,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++; } @@ -11207,18 +11086,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; @@ -11718,7 +11585,7 @@ UINT32 K_PointLimitForGametype(void) return cv_pointlimit.value; } - if (battlecapsules || bossinfo.valid) + if (K_Cooperative()) { return 0; } @@ -11742,4 +11609,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 c789eb98d..b015c000e 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); @@ -210,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/lua_playerlib.c b/src/lua_playerlib.c index 98203ed51..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")) @@ -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")) @@ -616,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")) @@ -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/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; 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 ff4b9f916..f7b3baa00 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") : "" @@ -114,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 @@ -285,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; @@ -320,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; @@ -350,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; @@ -452,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); @@ -1395,6 +1381,11 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget P_PlayDeathSound(target); } + + if (K_Cooperative()) + { + target->player->pflags |= (PF_NOCONTEST|PF_ELIMINATED); + } break; case MT_METALSONIC_RACE: @@ -1937,13 +1928,23 @@ static boolean P_KillPlayer(player_t *player, mobj_t *inflictor, mobj_t *source, switch (type) { case DMG_DEATHPIT: - // Respawn kill types - K_DoIngameRespawn(player); - player->mo->health -= K_DestroyBumpers(player, 1); - return false; + if (gametyperules & GTR_BUMPERS) + { + player->mo->health--; + } + + 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) @@ -1999,11 +2000,46 @@ static boolean P_KillPlayer(player_t *player, mobj_t *inflictor, mobj_t *source, player->pflags |= PF_ELIMINATED; } - K_DestroyBumpers(player, player->bumpers); - 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. * @@ -2024,6 +2060,7 @@ static boolean P_KillPlayer(player_t *player, mobj_t *inflictor, mobj_t *source, 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; @@ -2081,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) @@ -2102,20 +2136,16 @@ 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 { - { - 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; @@ -2146,9 +2176,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) @@ -2156,16 +2183,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) { @@ -2192,12 +2210,32 @@ 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; + 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); - player->invulnhitlag += (target->hitlag - oldhitlag); + AddNullHitlag(player, oldHitlag); + AddNullHitlag(playerInflictor, oldHitlagInflictor); if (player->timeshit > player->timeshitprev) { @@ -2227,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) { @@ -2234,31 +2277,35 @@ 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; + } } } - // We successfully damaged them! Give 'em some bumpers! - if (type != DMG_STING && type != DMG_STUMBLE) + if (gametyperules & GTR_BUMPERS) { - UINT8 takeBumpers = 1; - if (damagetype & DMG_STEAL) { - takeBumpers = 2; + // Steals 2 bumpers + damage = 2; + } + } + else + { + // Do not die from damage outside of bumpers health system + damage = 0; + } - if (type == DMG_KARMA) - { - takeBumpers = player->bumpers; - } - } - else - { - if (type == DMG_KARMA) - { - // Take half of their bumpers for karma comeback damage - takeBumpers = max(1, player->bumpers / 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) { @@ -2277,18 +2324,12 @@ 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 (K_Cooperative() == false) + { + K_BattleAwardHit(source->player, player, inflictor, damage); + } - if (type == DMG_KARMA) - { - // Destroy any remainder bumpers from the player for karma comeback damage - damage = K_DestroyBumpers(player, player->bumpers); - } - else - { - source->player->overtimekarma += 5*TICRATE; - } + K_TakeBumpersFromPlayer(source->player, player, damage); if (damagetype & DMG_STEAL) { @@ -2304,10 +2345,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)) { @@ -2425,6 +2462,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_map.c b/src/p_map.c index be4e0171b..ed83fd441 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 @@ -1390,13 +1394,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; - } - if (!P_MobjWasRemoved(thing) && !P_MobjWasRemoved(tm.thing)) { if (thing->player->eggmanexplode) diff --git a/src/p_mobj.c b/src/p_mobj.c index 6f60240ef..4e466e937 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 @@ -8247,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: @@ -8428,7 +8429,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; @@ -9843,8 +9844,10 @@ 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->hitlag > 0) + if ((mobj->player ? mobj->hitlag - mobj->player->nullHitlag : mobj->hitlag) > 0) { mobj->eflags |= MFE_PAUSED; mobj->hitlag--; @@ -11876,38 +11879,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_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. diff --git a/src/p_saveg.c b/src/p_saveg.c index e0db5fdb0..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); @@ -381,9 +381,7 @@ 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); - WRITEUINT32(save->p, players[i].overtimekarma); WRITEINT16(save->p, players[i].spheres); WRITEUINT32(save->p, players[i].spheredigestion); @@ -653,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); @@ -758,9 +756,7 @@ 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].overtimekarma = READUINT32(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 2fb93d1db..dc6490fcf 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))