From a2d83f4b5fc883ff61d29fe8f16ce3b93a9fa3fc Mon Sep 17 00:00:00 2001 From: Anonimus Date: Mon, 29 Sep 2025 21:59:23 -0400 Subject: [PATCH] Dapple up Add all the bubble shield VFX --- src/doomdef.h | 2 +- src/info/mobjs.h | 1 + src/info/sprites.h | 2 + src/info/states.h | 13 +++- src/k_collide.c | 12 ++++ src/k_kart.c | 71 +++++++++++++++++++++- src/p_mobj.c | 146 +++++++++++++++++++++++++++++++++++++++++++-- src/p_mobj.h | 2 + src/p_saveg.c | 16 +++++ 9 files changed, 255 insertions(+), 10 deletions(-) diff --git a/src/doomdef.h b/src/doomdef.h index 7d85d7b85..02a2bfd1e 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -103,7 +103,7 @@ extern "C" { // Special Hashing. //#define NOFILEHASH -//#define NOVERIFYIWADS +#define NOVERIFYIWADS // Uncheck this to compile debugging code //#define RANGECHECK diff --git a/src/info/mobjs.h b/src/info/mobjs.h index 368453768..f20c04676 100644 --- a/src/info/mobjs.h +++ b/src/info/mobjs.h @@ -612,6 +612,7 @@ _(THUNDERSHIELD) // Shields _(BUBBLESHIELD) _(FLAMESHIELD) _(BUBBLESHIELDTRAP) +_(BUBBLESHLD_DEBRIS) _(SINK) // Kitchen Sink Stuff _(SINK_SHIELD) diff --git a/src/info/sprites.h b/src/info/sprites.h index b1e373547..5f2407e5c 100644 --- a/src/info/sprites.h +++ b/src/info/sprites.h @@ -445,6 +445,8 @@ _(BHBM) // Ballhog BOOM _(SPBM) // Self-Propelled Bomb _(THNS) // Thunder Shield _(BUBS) // Bubble Shield (not Bubs) +_(BUBC) // Bubble Shield damage +_(BSDB) // Bubble Shield shatter debris _(BWVE) // Bubble Shield waves _(FLMS) // Flame Shield _(FLMD) // Flame Shield dash diff --git a/src/info/states.h b/src/info/states.h index a2a394e28..e6ee33b80 100644 --- a/src/info/states.h +++ b/src/info/states.h @@ -2888,7 +2888,7 @@ _(THUNDERSHIELD22) _(THUNDERSHIELD23) _(THUNDERSHIELD24) -// Bubble Shield +// Bubble Shield: Standard Sprites _(BUBBLESHIELD1) _(BUBBLESHIELD2) _(BUBBLESHIELD3) @@ -2917,6 +2917,17 @@ _(BUBBLESHIELDTRAP6) _(BUBBLESHIELDTRAP7) _(BUBBLESHIELDTRAP8) +// Bubble Shield: Damage Visuals +_(BUBLSHLD_DMG_1) +_(BUBLSHLD_DMG_2) +_(BUBLSHLD_DMG_3) +_(BUBLSHLD_DMG_4) +_(BUBBLEDEBRIS_1) +_(BUBBLEDEBRIS_2) +_(BUBBLEDEBRIS_3) +_(BUBBLEDEBRIS_4) +_(BUBBLEDEBRIS_5) + // Flame Shield _(FLAMESHIELD1) _(FLAMESHIELD2) diff --git a/src/k_collide.c b/src/k_collide.c index eefd54f9b..87027b20b 100644 --- a/src/k_collide.c +++ b/src/k_collide.c @@ -545,6 +545,12 @@ static void K_BubbleShieldCollideDrain(player_t *player, mobj_t *bubble) } } +static boolean K_BubbleReflectingTrapItem(mobjtype_t t) +{ + return ((t == MT_BANANA) || (t == MT_JAWZ_DUD) || (t == MT_SSMINE) || (t == MT_LANDMINE) || + (t == MT_EGGMANITEM) || (t == MT_SSMINE_SHIELD)); +} + boolean K_BubbleShieldReflect(mobj_t *t1, mobj_t *t2, boolean isplayer) { mobj_t *owner = t1->player ? t1 : t1->target; @@ -571,6 +577,12 @@ boolean K_BubbleShieldReflect(mobj_t *t1, mobj_t *t2, boolean isplayer) { t2->angle += ANGLE_180; } + + if (K_BubbleReflectingTrapItem(t2->type)) + { + // Stupid hack: Toss trap/dud items into the air + t2->momz += (24*t2->scale) * P_MobjFlip(t2); + } } if (!isplayer) diff --git a/src/k_kart.c b/src/k_kart.c index b27ccda52..71d2678bf 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -5227,12 +5227,76 @@ void K_DropHnextList(player_t *player) } } -// Bubble shield break SFX and VFX (maybe, hopefully) -static void K_BreakBubbleShield(player_t *player) +#define MAXSHARDCOUNT 40 +#define SHARDROT (360 * FRACUNIT / 40) +static void K_BreakBubbleShield(player_t* player) { + if (!player->mo->shieldtracer) + { + // No shield tracer, assume there's no bubble shield. + return; + } + // TODO: Some more dramatic SFX and VFX (DSS3K59, bubble shield shards fly all over) - S_StartSound(player->mo, sfx_s3k4b); + mobj_t* shard; + + 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->mo->shieldtracer->scale; + + scalediff = shieldscale - mapobjectscale; + shieldrad = FixedMul(player->mo->shieldtracer->radius, shieldscale); + + mul_vec.x = FRACUNIT; + mul_vec.y = 0; + + INT32 i; + for (i = 0; i < MAXSHARDCOUNT; i++) + { + mul_vec.x = FRACUNIT; + mul_vec.y = 0; + + randang = (SHARDROT * (i + 1)); + randzang = (P_RandomRange(23, 157) * FRACUNIT); + 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( + player->mo->shieldtracer->x + + FixedMul(FixedMul(mul_vec.x, shieldrad), FCOS(FixedAngle(randang))), + player->mo->shieldtracer->y + + FixedMul(FixedMul(mul_vec.x, shieldrad), FSIN(FixedAngle(randang))), + player->mo->shieldtracer->z + FixedMul(mul_vec.y * flip, shieldrad), + MT_BUBBLESHLD_DEBRIS); + + if (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; + + shard->momx = mom.x + player->mo->momx; + shard->momy = mom.y + player->mo->momy; + shard->momz = mom.z + player->mo->momz; + } + } + + S_StartSound(player->mo, sfx_kc41); } +#undef MAXSHARDCOUNT +#undef SHARDROT // For getting hit! void K_PopPlayerShield(player_t *player) @@ -10583,6 +10647,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) mobj_t *shield = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_BUBBLESHIELD); P_SetScale(shield, (shield->destscale = (5*shield->destscale)>>2)); P_SetTarget(&shield->target, player->mo); + P_SetTarget(&player->mo->shieldtracer, shield); S_StartSound(player->mo, sfx_s3k3f); player->curshield = KSHIELD_BUBBLE; diff --git a/src/p_mobj.c b/src/p_mobj.c index 7aff95942..b51e206f2 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -68,6 +68,18 @@ 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; @@ -9159,11 +9171,27 @@ static boolean P_MobjRegularThink(mobj_t *mobj) case MT_BUBBLESHIELD: { fixed_t scale; - statenum_t curstate; + statenum_t curstate, overlaystate; if (!mobj->target || !mobj->target->health || !mobj->target->player || mobj->target->player->curshield != KSHIELD_BUBBLE) { + if ((mobj->target->shieldtracer) && (mobj->target->shieldtracer->mobjnum == mobj->mobjnum)) + { + // We're being traced by this object; unset its shieldtracer. + P_SetTarget(&mobj->target->shieldtracer, NULL); + } + if (mobj->tracer && (!mobj->tracer->player)) + { + // Remove the tracer (overlay) + if (!P_MobjWasRemoved(mobj->tracer)) + { + P_RemoveMobj(mobj->tracer); + } + + P_SetTarget(&mobj->tracer, NULL); + } + P_RemoveMobj(mobj); return false; } @@ -9171,6 +9199,11 @@ static boolean P_MobjRegularThink(mobj_t *mobj) scale = (5*mobj->target->scale)>>2; curstate = ((mobj->tics == 1) ? (mobj->state->nextstate) : ((statenum_t)(mobj->state-states))); + // Lookup for rescaling the "bubble damage" overlay + UINT8 frame, scale_idx; + + frame = scale_idx = 0; + if (mobj->target->player->bubbleblowup) { INT32 blow = mobj->target->player->bubbleblowup; @@ -9185,6 +9218,10 @@ static boolean P_MobjRegularThink(mobj_t *mobj) scale += (blow * ((3*scale)>>1)) / bubbletime; mobj->frame = CLAMP(states[S_BUBBLESHIELDBLOWUP].frame + mobj->extravalue1, states[S_BUBBLESHIELDBLOWUP].frame, states[S_BUBBLESHIELDBLOWUP].frame + 3); + + frame = mobj->frame & FF_FRAMEMASK; + scale_idx = CLAMP(frame - 9, 0, 3); + if ((mobj->target->player->bubbleblowup > bubbletime) && (leveltime & 1)) { mobj->color = SKINCOLOR_WHITE; @@ -9247,6 +9284,9 @@ static boolean P_MobjRegularThink(mobj_t *mobj) { 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) @@ -9270,15 +9310,92 @@ static boolean P_MobjRegularThink(mobj_t *mobj) mobj->extravalue2 = mobj->target->player->bubbleblowup; P_SetScale(mobj, (mobj->destscale = scale)); - mobj->pitch = mobj->target->pitch; - mobj->roll = mobj->target->roll; + if (mobj->tracer && (!P_MobjWasRemoved(mobj->tracer))) + { + if (mobj->tracer->type == MT_OVERLAY) + { + fixed_t health_scalar; + + P_SetScale(mobj->tracer, mobj->destscale); - mobj->slopepitch = mobj->target->slopepitch; - mobj->sloperoll = mobj->target->sloperoll; + 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]); + + health_scalar = mobj->target->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))); + + // 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]); + } + } + else + { + // Very silently detach ourselves from our tracer and try a + // damage-control fix. + P_SetTarget(&mobj->tracer, NULL); + + mobj_t *spawn = P_SpawnMobj(mobj->x, mobj->z, mobj->z, MT_OVERLAY); + spawn->destscale = mobj->scale; + P_SetScale(spawn, mobj->scale); + P_SetTarget(&mobj->tracer, spawn); + P_SetTarget(&spawn->target, mobj); + } + } + + // Don't sloperoll Bubble Shields since it messes with the shatter VFX + mobj->pitch = 0; + mobj->roll = 0; + + mobj->slopepitch = 0; + mobj->sloperoll = 0; P_MoveOrigin(mobj, mobj->target->x, mobj->target->y, mobj->target->z); break; } + case MT_BUBBLESHLD_DEBRIS: + { + statenum_t curstate; + curstate = ((mobj->tics == 1) ? (mobj->state->nextstate) : ((statenum_t)(mobj->state-states))); + + if (curstate != S_INVISIBLE) + { + mobj->rollangle += ANG1 * 4; + } + else + { + mobj->rollangle = 0; + } + + if ((P_IsObjectOnGround(mobj)) && (!mobj->threshold)) + { + mobj->threshold = (TICRATE / 2); + + if (curstate != S_INVISIBLE) + { + // "Despawn" and play the glass landing sound. + S_StartSound(mobj, mobj->info->activesound); + P_SetMobjState(mobj, S_INVISIBLE); + } + } + + if (mobj->threshold) + { + mobj->threshold--; + + if (!mobj->threshold) + { + P_RemoveMobj(mobj); + } + } + + break; + } case MT_FLAMESHIELD: { statenum_t curstate; @@ -10389,6 +10506,8 @@ void P_MobjThinker(mobj_t *mobj) P_SetTarget(&mobj->hprev, NULL); if (mobj->itnext && P_MobjWasRemoved(mobj->itnext)) P_SetTarget(&mobj->itnext, NULL); + if (mobj->shieldtracer && P_MobjWasRemoved(mobj->shieldtracer)) + P_SetTarget(&mobj->shieldtracer, NULL); if (mobj->flags & MF_NOTHINK) return; @@ -11282,6 +11401,23 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) nummaprings++; break; // SRB2Kart + case MT_BUBBLESHIELD: + { + // Spawn in the damage overlay + mobj_t *spawn = P_SpawnMobj(mobj->x, mobj->z, mobj->z, MT_OVERLAY); + spawn->destscale = mobj->scale; + P_SetScale(spawn, mobj->scale); + P_SetTarget(&mobj->tracer, spawn); + P_SetTarget(&spawn->target, mobj); + break; + } + case MT_BUBBLESHLD_DEBRIS: + { + statenum_t shardset[] = {S_BUBBLEDEBRIS_1, S_BUBBLEDEBRIS_2, S_BUBBLEDEBRIS_3, S_BUBBLEDEBRIS_4, S_BUBBLEDEBRIS_5}; + P_SetMobjState(mobj, shardset[P_RandomRange(0, 4)]); + + break; + } case MT_ITEMCAPSULE: { // set default item & count diff --git a/src/p_mobj.h b/src/p_mobj.h index 9f2df3307..cf2225a1e 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -387,6 +387,8 @@ struct mobj_t mobj_t *tracer; // Thing being chased/attacked for tracers. + mobj_t *shieldtracer; // Blankart: Shield mobj, currently only for certain VFX + fixed_t friction; fixed_t movefactor; fixed_t gravity; diff --git a/src/p_saveg.c b/src/p_saveg.c index a6b0414b1..faf60c1a1 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -2118,6 +2118,7 @@ typedef enum MD3_MISCCAP = 1<<1, MD3_BAKEDOFFSET = 1<<2, MD3_EXTVAL3 = 1<<3, + MD3_SHIELDTRACER = 1<<4, } mobj_diff3_t; typedef enum @@ -2435,6 +2436,8 @@ static void SaveMobjThinker(savebuffer_t *save, const thinker_t *th, const UINT8 diff3 |= MD3_BAKEDOFFSET; if (mobj->extravalue3) diff3 |= MD3_EXTVAL3; + if (mobj->shieldtracer) + diff3 |= MD3_SHIELDTRACER; if (diff3 != 0) diff2 |= MD2_MORE; @@ -2725,6 +2728,10 @@ static void SaveMobjThinker(savebuffer_t *save, const thinker_t *th, const UINT8 { WRITEINT32(save->p, mobj->extravalue3); } + if (diff3 & MD3_SHIELDTRACER) + { + WRITEUINT32(save->p, mobj->shieldtracer->mobjnum); + } WRITEUINT32(save->p, mobj->mobjnum); } @@ -4028,6 +4035,10 @@ static thinker_t* LoadMobjThinker(savebuffer_t *save, actionf_p1 thinker, UINT8 { mobj->extravalue3 = READINT32(save->p); } + if (diff3 & MD3_SHIELDTRACER) + { + mobj->shieldtracer = (mobj_t *)(size_t)READUINT32(save->p); + } // Reset some non-synch values mobj->sloperoll = 0; @@ -5097,6 +5108,11 @@ static void P_RelinkPointers(void) if (!RelinkMobj(&mobj->terrainOverlay)) CONS_Debug(DBG_GAMELOGIC, "terrainOverlay not found on %d\n", mobj->type); } + if (mobj->shieldtracer) + { + if (!RelinkMobj(&mobj->shieldtracer)) + CONS_Debug(DBG_GAMELOGIC, "shieldtracer not found on %d\n", mobj->type); + } for (i = 0; i < MAXPLAYERS; i++) { if (!playeringame[i])