diff --git a/src/g_game.c b/src/g_game.c index 9530fc28f..d9a4eb195 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -255,7 +255,7 @@ INT32 stealtime = TICRATE/2; INT32 sneakertime = TICRATE + (TICRATE/3); INT32 waterpaneltime = TICRATE*2; INT32 itemtime = 8*TICRATE; -INT32 bubbletime = TICRATE/4; +INT32 bubbletime = TICRATE/2; INT32 comebacktime = 10*TICRATE; INT32 bumptime = 6; INT32 greasetics = 3*TICRATE; diff --git a/src/k_botitem.cpp b/src/k_botitem.cpp index bcaccaea9..4ff9197cd 100644 --- a/src/k_botitem.cpp +++ b/src/k_botitem.cpp @@ -922,7 +922,7 @@ static void K_BotItemBubble(botdata_t *bd, const player_t *player) boolean hold = false; // We are holding this thing too long, lets get rid of it. - if (bd->itemconfirm > 20*TICRATE) + if (bd->itemconfirm > 15*TICRATE) { bd->itemconfirm++; @@ -934,17 +934,13 @@ static void K_BotItemBubble(botdata_t *bd, const player_t *player) return; } - if (player->bubbleblowup <= 0) + if (player->bubblecool <= 0) { - if (player->bubblecool <= 0) - { - fixed_t radius = 192 * player->mo->scale; - radius = Easing_Linear(FRACUNIT * player->botvars.difficulty / MAXBOTDIFFICULTY, 2*radius, radius); - - hold = K_GetBlockedBubbleItem(player, radius); - } + fixed_t radius = 192 * player->mo->scale; + radius = Easing_Linear(FRACUNIT * player->botvars.difficulty / MAXBOTDIFFICULTY, 2*radius, radius); + hold = K_GetBlockedBubbleItem(player, radius); } - else if (player->bubbleblowup < bubbletime) + else if (player->bubblecool < bubbletime) { hold = true; } diff --git a/src/k_collide.c b/src/k_collide.c index 72badbcce..0ff5b3ea1 100644 --- a/src/k_collide.c +++ b/src/k_collide.c @@ -547,12 +547,7 @@ static void K_BubbleShieldCollideDrain(player_t *player, mobj_t *bubble, INT16 d // Apply a cooldown if the Bubble Shield took damage without shattering. if ((player->bubblehealth > 0) && (dmg > 0)) - { - player->bubblecool = 15*4; - player->itemflags &= ~IF_HOLDREADY; - bubble->extravalue1 = 4; - bubble->cvmem = 1; - } + player->bubbleblowup = 0; } static boolean K_BubbleReflectingTrapItem(const mobj_t *t) @@ -561,6 +556,12 @@ static boolean K_BubbleReflectingTrapItem(const mobj_t *t) t->type == MT_EGGMANITEM || t->type == MT_SSMINE || t->type == MT_SSMINE_SHIELD || t->type == MT_LANDMINE; } +static boolean K_StrongPlayerBump(const player_t *player) +{ + return (((K_GetKartInvinType() == KARTINVIN_LEGACY) && (player->invincibilitytimer)) + || (player->growshrinktimer > 0)); +} + boolean K_BubbleShieldReflect(mobj_t *t1, mobj_t *t2) { if (t2->threshold && !t2->player) @@ -572,9 +573,16 @@ boolean K_BubbleShieldReflect(mobj_t *t1, mobj_t *t2) fixed_t momentum = max(FixedHypot(owner->momx, owner->momy), FixedHypot(t2->momx, t2->momy)); momentum = max(3*momentum/4, 16*mapobjectscale); // do SOMETHING! - P_InstaThrust(t2, angle, momentum); - if (!t2->player) - t2->angle = angle; + if (t2->player && !K_StrongPlayerBump(t2->player)) + { + P_Thrust(t2, angle, momentum/2); + } + else + { + P_InstaThrust(t2, angle, momentum); + if (!t2->player) + t2->angle = angle; + } if (K_BubbleReflectingTrapItem(t2)) { @@ -598,19 +606,13 @@ boolean K_BubbleShieldReflect(mobj_t *t1, mobj_t *t2) return true; } -boolean K_BubbleShieldCanReflect(mobj_t *t1, mobj_t *t2) +boolean K_BubbleShieldCanReflect(mobj_t *t) { - return (t2->type == MT_ORBINAUT || t2->type == MT_JAWZ || t2->type == MT_JAWZ_DUD - || t2->type == MT_BANANA || t2->type == MT_EGGMANITEM || t2->type == MT_BALLHOG - || t2->type == MT_SSMINE || t2->type == MT_LANDMINE || t2->type == MT_SINK - || t2->type == MT_KART_LEFTOVER - || (t2->type == MT_PLAYER && t1->target != t2)); -} - -static boolean K_PlayerCanBeBubbleBumped(player_t *player) -{ - return (((K_GetKartInvinType() == KARTINVIN_LEGACY) && (player->invincibilitytimer)) - || (player->growshrinktimer > 0)); + return (t->type == MT_ORBINAUT || t->type == MT_JAWZ || t->type == MT_JAWZ_DUD + || t->type == MT_BANANA || t->type == MT_EGGMANITEM || t->type == MT_BALLHOG + || t->type == MT_SSMINE || t->type == MT_LANDMINE || t->type == MT_SINK + || t->type == MT_KART_LEFTOVER + || t->type == MT_PLAYER); } static INT16 K_GetBubbleDamage(mobj_t* itm) @@ -665,19 +667,11 @@ static INT16 K_GetBubbleDamage(mobj_t* itm) boolean K_BubbleShieldCollide(mobj_t *t1, mobj_t *t2) { - if (t2->type == MT_PLAYER) - { + // don't bump yourself + if (t1->target == t2) + return true; - boolean bumpme = ((t2->player) && (K_PlayerCanBeBubbleBumped(t2->player))); - - if (!bumpme) - { - // Ignore non-bumpable players. - return true; - } - } - - if (K_BubbleShieldCanReflect(t1, t2)) + if (K_BubbleShieldCanReflect(t2)) { boolean reflected = K_BubbleShieldReflect(t1, t2); diff --git a/src/k_collide.h b/src/k_collide.h index 2677f409b..367855e14 100644 --- a/src/k_collide.h +++ b/src/k_collide.h @@ -18,7 +18,7 @@ boolean K_LandMineCollide(mobj_t *t1, mobj_t *t2); void K_ThunderShieldAttack(mobj_t *actor, fixed_t size); boolean K_BubbleShieldReflect(mobj_t *t1, mobj_t *t2); -boolean K_BubbleShieldCanReflect(mobj_t *t1, mobj_t *t2); +boolean K_BubbleShieldCanReflect(mobj_t *t); boolean K_BubbleShieldCollide(mobj_t *t1, mobj_t *t2); boolean K_KitchenSinkCollide(mobj_t *t1, mobj_t *t2); diff --git a/src/k_hud.c b/src/k_hud.c index 22c73b666..18a3dd662 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -1448,7 +1448,7 @@ static void K_drawKartItem(void) localpatch = kp_bubbleshield[offset]; dark = true; - if ((stplyr->bubbleblowup > bubbletime) && (leveltime & 1)) + if (stplyr->bubbleblowup > 0 && leveltime & 1) { colormode = TC_BLINK; localcolor = SKINCOLOR_WHITE; @@ -1588,7 +1588,7 @@ static void K_drawKartItem(void) //V_ClearClipRect(); // Extensible meter, currently used by Invincibilty, Grow, Rocket Sneakers and Flame Shield - if (itembar) + if (itembar || K_GetShieldFromPlayer(stplyr) == KSHIELD_BUBBLE) { const INT32 fill = ((itembar*barlength)/maxl); const INT32 length = min(barlength, fill); diff --git a/src/k_kart.c b/src/k_kart.c index 226949569..fe4afadc8 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -485,7 +485,7 @@ static fixed_t K_PlayerWeight(mobj_t *mobj, mobj_t *against) if (against && !P_MobjWasRemoved(against) && against->player && ((!P_PlayerInPain(against->player) && P_PlayerInPain(mobj->player)) // You're hurt - || (against->player->itemtype == KITEM_BUBBLESHIELD && mobj->player->itemtype != KITEM_BUBBLESHIELD))) // They have a Bubble Shield + || (K_GetShieldFromPlayer(against->player) == KSHIELD_BUBBLE && K_GetShieldFromPlayer(mobj->player) != KSHIELD_BUBBLE))) // They have a Bubble Shield { weight = 0; // This player does not cause any bump action } @@ -497,7 +497,7 @@ static fixed_t K_PlayerWeight(mobj_t *mobj, mobj_t *against) weight = (mobj->player->kartweight) * FRACUNIT; - if (mobj->player->itemtype == KITEM_BUBBLESHIELD) + if (K_GetShieldFromPlayer(mobj->player) == KSHIELD_BUBBLE) { weight = max(BUBBLEMINWEIGHT, weight); weight = FixedMul(weight, FRACUNIT/16); @@ -7461,12 +7461,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) } } - if (player->itemtype == KITEM_BUBBLESHIELD) - { - if (player->bubblecool) - player->bubblecool--; - } - else + if (player->itemtype != KITEM_BUBBLESHIELD) { player->bubbleblowup = 0; player->bubblecool = 0; @@ -10218,8 +10213,12 @@ void K_MoveKartPlayer(player_t *player, boolean onground) player->itemflags &= ~IF_USERINGS; } - if (player->exiting && player->bubbleblowup > 0) - player->bubbleblowup--; + if (player->exiting) + { + player->bubbleblowup = 0; + if (player->bubblecool > 0) + player->bubblecool--; + } if (player && player->mo && player->mo->health > 0 && !player->spectator && !P_PlayerInPain(player) && player->respawn == 0 && !(player->exiting || mapreset)) { @@ -10785,16 +10784,16 @@ void K_MoveKartPlayer(player_t *player, boolean onground) { if ((buttons & BT_ATTACK) && (player->itemflags & IF_HOLDREADY)) { - if (player->bubbleblowup == 0) + if (player->bubblecool == 0) S_StartSound(player->mo, sfx_s3k75); - player->bubbleblowup++; + if (player->bubblecool < bubbletime && player->bubblehealth > 0) + { + player->bubbleblowup += 3; + player->bubblehealth--; + } - // Reduce cooldown for easier itemset chaining. - // (was blowup x4 before) - player->bubblecool = player->bubbleblowup*2; - - if (player->bubbleblowup > bubbletime*4) + if (++player->bubblecool >= bubbletime + TICRATE/2) { // If you overcharge the Bubble Shield at // any point, it pops and gives you a boost. @@ -10811,43 +10810,41 @@ void K_MoveKartPlayer(player_t *player, boolean onground) } player->bubbleboost = 7 * sneakertime / 10; + player->flashing = 4*TICRATE/7; } } else { + if (player->bubblecool > bubbletime) + player->bubblecool = bubbletime; + + if (player->bubbleblowup > 0) { - boolean popped = false; - if (player->bubbleblowup > bubbletime) - player->bubbleblowup = bubbletime; + player->bubbleblowup--; + if (player->bubbleblowup == 0) + K_BotResetItemConfirm(player, false); + if (player->bubblecool < bubbletime) + player->bubblecool++; + } - if (player->bubbleblowup) + if (player->bubbleblowup == 0 && player->bubblecool > 0) + { + player->bubblecool--; + + if (player->bubblecool == 0 && player->bubblehealth <= 0) { - player->bubbleblowup--; - - if (!player->bubbleblowup) - { - // Each use costs some points of health. - K_RemoveBubbleHealth(player, BUBBLEUSECOST); - - if (player->bubblehealth <= 0) - { - // Bubble popped! - popped = true; - } - } + K_BreakBubbleShield(player); + K_PopPlayerShield(player); } + } - if (!popped) - { - if (buttons & BT_ATTACK || player->bubblecool) - { - player->itemflags &= ~IF_HOLDREADY; - } - else - { - player->itemflags |= IF_HOLDREADY; - } - } + if (buttons & BT_ATTACK || player->bubblecool > 0) + { + player->itemflags &= ~IF_HOLDREADY; + } + else + { + player->itemflags |= IF_HOLDREADY; } } } diff --git a/src/k_kart.h b/src/k_kart.h index 22ad14eba..a6d89e115 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -68,17 +68,14 @@ extern boolean clusterplayer[MAXPLAYERS]; extern UINT32 clusterid; // ID of the "cluster player", the one closest to the cluster point. extern vector3_t clusterpoint, clusterdtf; -// Bubble Shield's maximum health -#define MAXBUBBLEHEALTH 12 +// Bubble Shield's maximum health (determines how many times it can be inflated) +#define MAXBUBBLEHEALTH (bubbletime * 3) -// Chip damage done to a Bubble Shield upon bumping a player -#define BUBBLEBUMPCHIP (MAXBUBBLEHEALTH / 4) +// Chip damage done to a Bubble Shield upon bumping a player when not inflated (rounded up) +#define BUBBLEBUMPCHIP ((MAXBUBBLEHEALTH + 1) / 2) -// HP cost for a Bubble Shield to inflate -#define BUBBLEUSECOST (MAXBUBBLEHEALTH / 3) - -// Damage done to an inflated Bubble Shield by items. -#define BUBBLEITMDAMAGE (MAXBUBBLEHEALTH / 6) +// Damage done to an inflated Bubble Shield (rounded up) +#define BUBBLEITMDAMAGE ((MAXBUBBLEHEALTH + 5) / 6) // Bump weight for a Bubble Shield #define BUBBLEMINWEIGHT (5 * FRACUNIT) diff --git a/src/p_inter.c b/src/p_inter.c index 0cb66ce6a..dd4df34bb 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -2133,8 +2133,6 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da boolean force = false; boolean spbpop = false; boolean painsound = false; - boolean bubbleinvuln = false; - INT32 myShield = KSHIELD_NONE; if (objectplacing) return false; @@ -2232,8 +2230,9 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da else { const UINT8 type = (damagetype & DMG_TYPEMASK); - const boolean explosioncombo = (type == DMG_EXPLODE); // This damage type can do evil stuff like ALWAYS combo - myShield = K_GetShieldFromPlayer(player); + + const boolean explosioncombo = type == DMG_EXPLODE // This damage type can do evil stuff like ALWAYS combo + && player->bubbleboost == 0; // ...but popping a Bubble Shield protects you! // Check if the player is allowed to be damaged! // If not, then spawn the instashield effect instead. @@ -2265,32 +2264,6 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da return false; } - if ((myShield == KSHIELD_BUBBLE) && (player->bubbleblowup)) - { - // This player is on the defensive; protect them from certain attacks. - switch (type) - { - case DMG_WIPEOUT: - bubbleinvuln = true; - break; - case DMG_VOLTAGE: - case DMG_NORMAL: - bubbleinvuln = true; - break; - default: - break; - } - - if (bubbleinvuln) - { - // This attack likely penetrated. Defense! - player->flashing = K_GetKartFlashing(player); - player->bubblecool = player->bubbleblowup*2; - K_DoInstashield(player); - return false; - } - } - // Check if we should allow explosion combos. if ((explosioncombo == false) && (player->flashing > 0 || player->squishedtimer > 0)) { @@ -2298,6 +2271,14 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da K_DoInstashield(player); return false; } + + // Bubble Shield protects you from spinout, but not knockback + if (source != NULL && K_GetShieldFromPlayer(player) == KSHIELD_BUBBLE) + { + K_PopPlayerShield(player); + K_DoInstashield(player); + return false; + } } // We successfully damaged them! Give 'em some bumpers! @@ -2454,7 +2435,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da } // Have a shield? You get hit, but don't lose your rings! - if (myShield != KSHIELD_NONE) + if (K_GetShieldFromPlayer(player) != KSHIELD_NONE) { ringburst = 0; K_PopPlayerShield(player); diff --git a/src/p_mobj.c b/src/p_mobj.c index 81d423a5c..c0ea81917 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -9178,77 +9178,60 @@ static boolean P_MobjRegularThink(mobj_t *mobj) { fixed_t scale; statenum_t curstate, overlaystate; + const player_t *player = P_MobjWasRemoved(mobj->target) ? NULL : mobj->target->player; - if (!mobj->target || !mobj->target->health || !mobj->target->player - || K_GetShieldFromPlayer(mobj->target->player) != KSHIELD_BUBBLE) + if (player == NULL || player->mo->health == 0 || K_GetShieldFromPlayer(player) != KSHIELD_BUBBLE) { P_RemoveMobj(mobj); return false; } - scale = (5*mobj->target->scale)>>2; - curstate = ((mobj->tics == 1) ? (mobj->state->nextstate) : ((statenum_t)(mobj->state-states))); + scale = 5*player->mo->scale/4; + curstate = mobj->tics == 1 ? mobj->state->nextstate : (statenum_t)(mobj->state - states); + mobj->color = SKINCOLOR_BLUE; + mobj->colorized = false; // Lookup for rescaling the "bubble damage" overlay - UINT8 frame, scale_idx; + UINT8 scale_idx = player->bubblecool > bubbletime ? player->bubblecool % 4 : 0; + if (player->bubbleblowup > 0 && player->bubblecool < 8) + scale_idx = player->bubblecool/2; - frame = scale_idx = 0; - - if (mobj->target->player->bubbleblowup) + if (player->bubblecool) { - INT32 blow = mobj->target->player->bubbleblowup; - if (blow > bubbletime) - blow = bubbletime; + INT32 blow = player->bubbleblowup + min(player->bubblecool, bubbletime)*10; + scale += (blow * scale) / (bubbletime * 10); if (curstate != S_BUBBLESHIELDBLOWUP) P_SetMobjState(mobj, S_BUBBLESHIELDBLOWUP); mobj->angle += ANGLE_22h; - mobj->renderflags &= ~RF_GHOSTLYMASK; - scale += (blow * ((3*scale)>>1)) / bubbletime; + if (player->bubbleblowup == 0 && player->bubblecool > 0) + mobj->renderflags |= RF_GHOSTLY; + else + mobj->renderflags &= ~RF_GHOSTLYMASK; - mobj->frame = CLAMP(states[S_BUBBLESHIELDBLOWUP].frame + mobj->extravalue1, states[S_BUBBLESHIELDBLOWUP].frame, states[S_BUBBLESHIELDBLOWUP].frame + 3); + mobj->frame = states[S_BUBBLESHIELDBLOWUP].frame + scale_idx; - frame = mobj->frame & FF_FRAMEMASK; - scale_idx = CLAMP(frame - 9, 0, 3); - - if ((mobj->target->player->bubbleblowup > bubbletime) && (leveltime & 1)) + if (player->bubbleblowup > 0 && leveltime & 1) { mobj->color = SKINCOLOR_WHITE; mobj->colorized = true; } - else - { - mobj->color = SKINCOLOR_BLUE; - mobj->colorized = false; - } - if (mobj->extravalue1 < 4 && mobj->extravalue2 < blow && !mobj->cvmem && (leveltime & 1)) // Growing - { - mobj->extravalue1++; - if (mobj->extravalue1 >= 4) - mobj->cvmem = 1; // shrink back down - } - else if ((mobj->extravalue1 > -4 && mobj->extravalue2 > blow) - || (mobj->cvmem && mobj->extravalue1 > 0)) // Shrinking - mobj->extravalue1--; - - if (P_IsObjectOnGround(mobj->target)) + if (P_IsObjectOnGround(player->mo) && player->bubbleblowup > 0) { UINT8 i; for (i = 0; i < 2; i++) { angle_t a = mobj->angle + ((i & 1) ? ANGLE_180 : 0); - fixed_t ws = (mobj->target->scale>>1); + fixed_t ws = player->mo->scale/2 + scale/5; mobj_t *wave; - ws += (blow * ws) / bubbletime; - wave = P_SpawnMobj( - (mobj->target->x - mobj->target->momx) + P_ReturnThrustX(NULL, a, mobj->radius - (21*ws)), - (mobj->target->y - mobj->target->momy) + P_ReturnThrustY(NULL, a, mobj->radius - (21*ws)), - (mobj->target->z - mobj->target->momz), MT_THOK); + (player->mo->x - player->mo->momx) + P_ReturnThrustX(NULL, a, mobj->radius - (21*ws)), + (player->mo->y - player->mo->momy) + P_ReturnThrustY(NULL, a, mobj->radius - (21*ws)), + (player->mo->z - player->mo->momz), MT_THOK); wave->colorized = true; wave->color = SKINCOLOR_BLUE; @@ -9257,47 +9240,21 @@ static boolean P_MobjRegularThink(mobj_t *mobj) P_SetMobjState(wave, S_SPLISH1); - wave->momx = mobj->target->momx; - wave->momy = mobj->target->momy; - wave->momz = mobj->target->momz; + wave->momx = player->mo->momx; + wave->momy = player->mo->momy; + wave->momz = player->mo->momz; } } } else { - mobj->cvmem = 0; - mobj->angle = mobj->target->angle; + mobj->angle = player->mo->angle; + mobj->renderflags &= ~RF_GHOSTLYMASK; if (curstate == S_BUBBLESHIELDBLOWUP) - { - if (mobj->extravalue1 != 0) - { - mobj->frame = (states[S_BUBBLESHIELDBLOWUP].frame + mobj->extravalue1); - - frame = mobj->frame & FF_FRAMEMASK; - scale_idx = CLAMP(frame - 9, 0, 3); - - if (mobj->extravalue1 < 0 && (leveltime & 1)) - mobj->extravalue1++; - else if (mobj->extravalue1 > 0) - mobj->extravalue1--; - } - else - { - P_SetMobjState(mobj, S_BUBBLESHIELD1); - mobj->extravalue1 = 0; - } - } - else - { - if (mobj->target->player->bubblecool && ((curstate-S_BUBBLESHIELD1) & 1)) - mobj->renderflags |= RF_GHOSTLY; - else - mobj->renderflags &= ~RF_GHOSTLYMASK; - } + P_SetMobjState(mobj, S_BUBBLESHIELD1); } - mobj->extravalue2 = mobj->target->player->bubbleblowup; P_SetScale(mobj, (mobj->destscale = scale)); if (mobj->tracer && (!P_MobjWasRemoved(mobj->tracer))) @@ -9313,7 +9270,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj) mobj->tracer->spritexscale = FixedMul(mobj->spritexscale, bubbleoverlayscales[scale_idx][0]); mobj->tracer->spriteyscale = FixedMul(mobj->spriteyscale, bubbleoverlayscales[scale_idx][1]); - health_scalar = mobj->target->player->bubblehealth * FRACUNIT / MAXBUBBLEHEALTH; + health_scalar = player->bubblehealth * FRACUNIT / MAXBUBBLEHEALTH; health_scalar = 5 - CLAMP(health_scalar / (FRACUNIT / 5), 1, 5); overlaystate = ((mobj->tracer->tics == 1) ? (mobj->tracer->state->nextstate) : ((statenum_t)(mobj->tracer->state-states))); @@ -9346,7 +9303,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj) mobj->sloperoll = 0; mobj->flags &= ~(MF_NOCLIP|MF_NOCLIPTHING); - P_MoveOrigin(mobj, mobj->target->x, mobj->target->y, mobj->target->z); + P_MoveOrigin(mobj, player->mo->x, player->mo->y, player->mo->z); mobj->flags |= MF_NOCLIP|MF_NOCLIPTHING; break; }