diff --git a/src/k_collide.c b/src/k_collide.c index 0ff5b3ea1..b6c1b6da0 100644 --- a/src/k_collide.c +++ b/src/k_collide.c @@ -343,7 +343,8 @@ boolean K_MineExplosionCollide(mobj_t *t1, mobj_t *t2) { if (t2->player) { - if (t2->player->flashing > 0) + // don't sideswipe anyone boosting with a bubble shield! + if (t2->player->flashing > 0 || t2->player->bubbleboost > 0) return true; if (t1->state == &states[S_MINEEXPLOSION1]) diff --git a/src/k_hud.c b/src/k_hud.c index 18a3dd662..4f46dcb4f 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -1306,6 +1306,22 @@ void K_getMinimapDrawinfo(drawinfo_t *out) out->flags = fflags; } +static void K_DrawItemBar(INT32 fx, INT32 fy, INT32 fflags, fixed_t itembar, UINT8 colors[static 3]) +{ + const boolean fourp = r_splitscreen > 1; + const INT32 barlength = (fourp ? 12 : 26)*FRACUNIT; + const INT32 length = min(barlength, FixedMul(itembar, barlength)); + const INT32 height = (fourp ? 1 : 2)*FRACUNIT; + const INT32 x = (fx + (fourp ? 17 : 11)) * FRACUNIT, y = (fy + (fourp ? 27 : 35)) * FRACUNIT; + + V_DrawSciencePatch(x, y, V_HUDTRANS|fflags, kp_itemtimer[fourp ? 1 : 0], FRACUNIT); + V_DrawFixedFill(x+FRACUNIT, y+FRACUNIT, min(FRACUNIT, length), height, colors[2]|fflags); // the left edge + V_DrawFixedFill(x+max(FRACUNIT, length), y+FRACUNIT, min(FRACUNIT, length), height, colors[2]|fflags); // the right edge + if (!fourp) + V_DrawFixedFill(x+2*FRACUNIT, y+2*FRACUNIT, max(0, length - 2*FRACUNIT), FRACUNIT, colors[1]|fflags); // the dulled underside + V_DrawFixedFill(x+2*FRACUNIT, y+FRACUNIT, max(0, length - 2*FRACUNIT), FRACUNIT, colors[0]|fflags); // the shine +} + // see also MT_PLAYERARROW mobjthinker in p_mobj.c static void K_drawKartItem(void) { @@ -1321,11 +1337,7 @@ static void K_drawKartItem(void) boolean dark = false; INT32 fx = 0, fy = 0, fflags = 0; // final coords for hud and flags... INT32 numberdisplaymin = 2; - INT32 itembar = 0; - INT32 maxl = 0; // itembar's normal highest value - INT32 flamebar = 0; - INT32 flamemaxl = 0; // flamebar's normal highest value - const INT32 barlength = (r_splitscreen > 1 ? 12 : 26); + fixed_t itembar = -1, flamebar = -1; UINT16 localcolor = SKINCOLOR_NONE; SINT8 colormode = TC_RAINBOW; UINT8 *colmap = NULL; @@ -1386,8 +1398,7 @@ static void K_drawKartItem(void) } else if (stplyr->rocketsneakertimer > 1) { - itembar = stplyr->rocketsneakertimer; - maxl = (itemtime*3) - barlength; + itembar = FixedDiv(stplyr->rocketsneakertimer, itemtime*3); if (leveltime & 1) localpatch = kp_rocketsneaker[offset]; @@ -1396,10 +1407,8 @@ static void K_drawKartItem(void) } else if (stplyr->flametimer > 1) { - itembar = stplyr->flametimer; - maxl = (itemtime*3) - barlength; - flamebar = stplyr->flamestore; - flamemaxl = FLAMESTOREMAX; + itembar = FixedDiv(stplyr->flametimer, itemtime*3); + flamebar = FixedDiv(stplyr->flamestore, FLAMESTOREMAX); localbg = kp_itembg[offset+1]; dark = true; @@ -1417,10 +1426,7 @@ static void K_drawKartItem(void) else if (stplyr->growshrinktimer > 0) { if (stplyr->growcancel > 0) - { - itembar = stplyr->growcancel; - maxl = 26; - } + itembar = FixedDiv(stplyr->growcancel, 26); if (leveltime & 1) localpatch = kp_grow[offset]; @@ -1429,14 +1435,10 @@ static void K_drawKartItem(void) } else if ((stplyr->invincibilitytimer) && (K_GetKartInvinType() == KARTINVIN_ALTERN)) { - itembar = stplyr->invincibilitytimer; - maxl = max(1, stplyr->maxinvincibilitytime); + itembar = FixedDiv(stplyr->invincibilitytimer, max(1, stplyr->maxinvincibilitytime)); if (stplyr->invincibilitycancel > 0) - { - flamebar = stplyr->invincibilitycancel; - flamemaxl = 26; - } + flamebar = FixedDiv(stplyr->invincibilitycancel, 26); if (leveltime & 1) localpatch = localinv; @@ -1454,8 +1456,7 @@ static void K_drawKartItem(void) localcolor = SKINCOLOR_WHITE; } - itembar = stplyr->bubblehealth; - maxl = MAXBUBBLEHEALTH; + itembar = FixedDiv(stplyr->bubblehealth, MAXBUBBLEHEALTH); } else if (stplyr->sadtimer > 0) { @@ -1588,45 +1589,12 @@ static void K_drawKartItem(void) //V_ClearClipRect(); // Extensible meter, currently used by Invincibilty, Grow, Rocket Sneakers and Flame Shield - if (itembar || K_GetShieldFromPlayer(stplyr) == KSHIELD_BUBBLE) - { - const INT32 fill = ((itembar*barlength)/maxl); - const INT32 length = min(barlength, fill); - const INT32 height = (offset ? 1 : 2); - const INT32 x = (offset ? 17 : 11), y = (offset ? 27 : 35); + // ...aren't you forgetting something? + if (itembar != -1) + K_DrawItemBar(fx, fy, fflags|V_HUDTRANS, itembar, (UINT8 []){0, 8, 12}); - V_DrawScaledPatch(fx+x, fy+y, V_HUDTRANS|fflags, kp_itemtimer[offset]); - // The left dark "AA" edge - V_DrawFill(fx+x+1, fy+y+1, (length == 2 ? 2 : 1), height, 12|fflags|V_HUDTRANS); - // The bar itself - if (length > 2) - { - V_DrawFill(fx+x+length, fy+y+1, 1, height, 12|fflags|V_HUDTRANS); // the right one - if (height == 2) - V_DrawFill(fx+x+2, fy+y+2, length-2, 1, 8|fflags|V_HUDTRANS); // the dulled underside - V_DrawFill(fx+x+2, fy+y+1, length-2, 1, 0|fflags|V_HUDTRANS); // the shine - } - } - - if (flamebar) - { - const INT32 fill = ((flamebar*barlength)/flamemaxl); - const INT32 length = min(barlength, fill); - const INT32 height = (offset ? 1 : 2); - const INT32 x = (offset ? 17 : 11), y = (offset ? 27 : 35); - - V_DrawScaledPatch(fx+x, fy+y-8, V_HUDTRANS|fflags, kp_itemtimer[offset]); - // The left dark "AA" edge - V_DrawFill(fx+x+1, fy+y+1-8, (length == 2 ? 2 : 1), height, 55|fflags|V_HUDTRANS); - // The bar itself - if (length > 2) - { - V_DrawFill(fx+x+length, fy+y+1-8, 1, height, 55|fflags|V_HUDTRANS); // the right one - if (height == 2) - V_DrawFill(fx+x+2, fy+y+2-8, length-2, 1, 36|fflags|V_HUDTRANS); // the dulled underside - V_DrawFill(fx+x+2, fy+y+1-8, length-2, 1, 51|fflags|V_HUDTRANS); // the shine - } - } + if (flamebar != -1) + K_DrawItemBar(fx, fy - 8, fflags|V_HUDTRANS, flamebar, (UINT8 []){51, 36, 55}); // Quick Eggman numbers if (stplyr->eggmanexplode > 1) diff --git a/src/k_kart.c b/src/k_kart.c index fe4afadc8..bed9d3960 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -5306,59 +5306,49 @@ static void K_BreakBubbleShield(player_t* player) if (K_GetShieldFromPlayer(player) != KSHIELD_BUBBLE) return; - // TODO: Some more dramatic SFX and VFX (DSS3K59, bubble shield shards fly all over) - mobj_t* shard; + const INT32 flip = P_MobjFlip(player->mo); + const fixed_t scalediff = player->shieldtracer->scale - mapobjectscale; + const fixed_t shieldrad = player->shieldtracer->radius; - fixed_t move_magnitude, shieldrad, shieldscale, scalediff; - vector2_t mul_vec; - vector3_t mom; - fixed_t randang, randzang; - INT32 flip = P_MobjFlip(player->mo); - - shieldscale = player->shieldtracer->scale; - - scalediff = shieldscale - mapobjectscale; - shieldrad = FixedMul(player->shieldtracer->radius, shieldscale); - - mul_vec.x = FRACUNIT; - mul_vec.y = 0; - - INT32 i; - for (i = 0; i < MAXSHARDCOUNT; i++) + for (INT32 i = 0; i < MAXSHARDCOUNT; i++) { - mul_vec.x = FRACUNIT; - mul_vec.y = 0; + vector2_t mul_vec = { FRACUNIT, 0 }; - randang = (SHARDROT * (i + 1)); - randzang = (P_RandomRange(23, 157) * FRACUNIT); - move_magnitude = + fixed_t randang = SHARDROT * (i + 1); + fixed_t randzang = P_RandomRange(10, 120) * FRACUNIT; + fixed_t move_magnitude = FixedMul((P_RandomRange(4, 16) * FRACUNIT), mapobjectscale + (scalediff / 4)); - + FV2_Rotate(&mul_vec, randzang); // Do shitty initial 3D rotations around the shield's radius. - shard = P_SpawnMobj( + mobj_t *shard = P_SpawnMobj( player->shieldtracer->x + - FixedMul(FixedMul(mul_vec.x, shieldrad), FCOS(FixedAngle(randang))), + FixedMul(FixedMul(mul_vec.x, shieldrad), FCOS(FixedAngle(randang))), player->shieldtracer->y + - FixedMul(FixedMul(mul_vec.x, shieldrad), FSIN(FixedAngle(randang))), - player->shieldtracer->z + FixedMul(mul_vec.y * flip, shieldrad), + FixedMul(FixedMul(mul_vec.x, shieldrad), FSIN(FixedAngle(randang))), + player->shieldtracer->z + ((player->shieldtracer->height + scalediff) / 4) + + FixedMul(mul_vec.y * flip, shieldrad), MT_BUBBLESHLD_DEBRIS); - if (shard) + if (!P_MobjWasRemoved(shard)) { //CONS_Printf(M_GetText("randzang: %d, randang: %d\n"), randzang / FRACUNIT, randang / FRACUNIT); mul_vec.x = FixedMul(move_magnitude, mul_vec.x); mul_vec.y = FixedMul(move_magnitude, mul_vec.y * flip); - mom.x = FixedMul(mul_vec.x, FCOS(FixedAngle(randang))); - mom.y = FixedMul(mul_vec.x, FSIN(FixedAngle(randang))); - mom.z = mul_vec.y; + vector3_t mom = { + .x = FixedMul(mul_vec.x, FCOS(FixedAngle(randang))), + .y = FixedMul(mul_vec.x, FSIN(FixedAngle(randang))), + .z = mul_vec.y, + }; shard->momx = mom.x + player->mo->momx; shard->momy = mom.y + player->mo->momy; shard->momz = mom.z + player->mo->momz; + + shard->extravalue1 = i % 5; // 20% chance to play the shard sound } } @@ -5386,9 +5376,7 @@ void K_PopPlayerShield(player_t *player) player->itemamount = 0; break; case KSHIELD_BUBBLE: - if (player->bubblehealth > 0) - K_BreakBubbleShield(player); // Nice whiff; see ya! - + K_BreakBubbleShield(player); player->bubbleblowup = 0; player->bubblecool = 0; player->bubblehealth = 0; @@ -5415,10 +5403,7 @@ void K_RemoveBubbleHealth(player_t *player, INT16 sub) player->bubblehealth = (UINT8)(max(0, (INT16)(player->bubblehealth) - sub)); if (player->bubblehealth <= 0) - { - K_BreakBubbleShield(player); K_PopPlayerShield(player); - } } mobj_t *K_CreatePaperItem(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8 flip, UINT8 type, UINT8 amount) @@ -10797,7 +10782,6 @@ void K_MoveKartPlayer(player_t *player, boolean onground) { // If you overcharge the Bubble Shield at // any point, it pops and gives you a boost. - K_BreakBubbleShield(player); K_PopPlayerShield(player); if (onground) @@ -10832,10 +10816,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) player->bubblecool--; if (player->bubblecool == 0 && player->bubblehealth <= 0) - { - K_BreakBubbleShield(player); K_PopPlayerShield(player); - } } if (buttons & BT_ATTACK || player->bubblecool > 0) diff --git a/src/p_inter.c b/src/p_inter.c index dd4df34bb..b6c202b70 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -2273,7 +2273,8 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da } // Bubble Shield protects you from spinout, but not knockback - if (source != NULL && K_GetShieldFromPlayer(player) == KSHIELD_BUBBLE) + if (K_GetShieldFromPlayer(player) == KSHIELD_BUBBLE + && explosioncombo == false && source != NULL && type != DMG_VOLTAGE) { K_PopPlayerShield(player); K_DoInstashield(player); diff --git a/src/p_mobj.c b/src/p_mobj.c index c0ea81917..082da153a 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -69,18 +69,6 @@ mobj_t *boss3cap = NULL; mobj_t *mobjcache = NULL; -// Bubble Shield overlay spritescales. -static fixed_t bubbleoverlayscales[4][2] = { - // All scales are raw 16.16 fixed-point numbers based on FRACUNIT. - {65536, 65536}, // 1.0, 1.0 - {65536, 59667}, // 1.0, 0.9104477 - {90593, 61623}, // 1.382352, 0.940298 - {100231, 53798} // 1.529411, 0.820895 -}; -static statenum_t bubbledamagestates[5] = { S_INVISIBLE, S_BUBLSHLD_DMG_1, S_BUBLSHLD_DMG_2, S_BUBLSHLD_DMG_3, S_BUBLSHLD_DMG_4 }; - - - void P_InitCachedActions(void) { actioncachehead.prev = actioncachehead.next = &actioncachehead; @@ -9176,8 +9164,6 @@ static boolean P_MobjRegularThink(mobj_t *mobj) } case MT_BUBBLESHIELD: { - fixed_t scale; - statenum_t curstate, overlaystate; const player_t *player = P_MobjWasRemoved(mobj->target) ? NULL : mobj->target->player; if (player == NULL || player->mo->health == 0 || K_GetShieldFromPlayer(player) != KSHIELD_BUBBLE) @@ -9186,22 +9172,14 @@ static boolean P_MobjRegularThink(mobj_t *mobj) return false; } - 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 scale_idx = player->bubblecool > bubbletime ? player->bubblecool % 4 : 0; - if (player->bubbleblowup > 0 && player->bubblecool < 8) - scale_idx = player->bubblecool/2; + fixed_t scale = 5*player->mo->scale/4; if (player->bubblecool) { INT32 blow = player->bubbleblowup + min(player->bubblecool, bubbletime)*10; scale += (blow * scale) / (bubbletime * 10); - if (curstate != S_BUBBLESHIELDBLOWUP) + if (mobj->state - states != S_BUBBLESHIELDBLOWUP) P_SetMobjState(mobj, S_BUBBLESHIELDBLOWUP); mobj->angle += ANGLE_22h; @@ -9210,13 +9188,24 @@ static boolean P_MobjRegularThink(mobj_t *mobj) else mobj->renderflags &= ~RF_GHOSTLYMASK; - mobj->frame = states[S_BUBBLESHIELDBLOWUP].frame + scale_idx; + fixed_t stretch = 0; + if (player->bubblecool > bubbletime) + stretch = (player->bubblecool % 4) * FRACUNIT/5; + else if (player->bubbleblowup > 0 && player->bubblecool < 4) + stretch = player->bubblecool*FRACUNIT/6; + else if (player->bubbleblowup >= 4 && player->bubblecool < 8) + stretch = (8 - player->bubblecool)*FRACUNIT/6; + else if (player->bubblehealth == 0 && player->bubbleblowup == 0) + stretch = (player->bubblecool - bubbletime)*FRACUNIT/bubbletime; + mobj->spritexscale = FRACUNIT + FTAN(FixedAngle(stretch*40) + ANGLE_90); + mobj->spriteyscale = FRACUNIT - stretch/3 + abs(FSIN(FixedAngle(stretch*80)))/8; + + mobj->colorized = true; if (player->bubbleblowup > 0 && leveltime & 1) - { mobj->color = SKINCOLOR_WHITE; - mobj->colorized = true; - } + else + mobj->color = SKINCOLOR_BLUE; if (P_IsObjectOnGround(player->mo) && player->bubbleblowup > 0) { @@ -9250,8 +9239,10 @@ static boolean P_MobjRegularThink(mobj_t *mobj) { mobj->angle = player->mo->angle; mobj->renderflags &= ~RF_GHOSTLYMASK; + mobj->colorized = false; + mobj->color = SKINCOLOR_BLUE; - if (curstate == S_BUBBLESHIELDBLOWUP) + if (mobj->state - states == S_BUBBLESHIELDBLOWUP) P_SetMobjState(mobj, S_BUBBLESHIELD1); } @@ -9262,24 +9253,22 @@ static boolean P_MobjRegularThink(mobj_t *mobj) if (mobj->tracer->type == MT_OVERLAY) { fixed_t health_scalar; - + P_SetScale(mobj->tracer, mobj->destscale); mobj->tracer->threshold |= OV_DONTXYSCALE; - mobj->tracer->spritexscale = FixedMul(mobj->spritexscale, bubbleoverlayscales[scale_idx][0]); - mobj->tracer->spriteyscale = FixedMul(mobj->spriteyscale, bubbleoverlayscales[scale_idx][1]); + mobj->tracer->spritexscale = mobj->spritexscale; + mobj->tracer->spriteyscale = mobj->spriteyscale; 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))); + statenum_t damagestate = health_scalar == 0 ? S_INVISIBLE : S_BUBLSHLD_DMG_1 - 1 + health_scalar; // Depending on the level of damage done to the shield, show some cracks. - if (overlaystate != bubbledamagestates[health_scalar]) - { - P_SetMobjState(mobj->tracer, bubbledamagestates[health_scalar]); - } + if (mobj->tracer->state - states != damagestate) + P_SetMobjState(mobj->tracer, damagestate); } else { @@ -9328,7 +9317,8 @@ static boolean P_MobjRegularThink(mobj_t *mobj) if (curstate != S_INVISIBLE) { // "Despawn" and play the glass landing sound. - S_StartSoundAtVolume(mobj, mobj->info->activesound, 40); + if (mobj->extravalue1 == 0) + S_StartSoundAtVolume(mobj, mobj->info->activesound, 128); P_SetMobjState(mobj, S_INVISIBLE); } }