From 7190106ab794e0e305f2a65982aa3410fa1b53c2 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 13 May 2025 18:25:16 -0700 Subject: [PATCH] Add new Bubble Shield visuals (held version) --- src/k_kart.c | 2 + src/k_objects.h | 4 + src/objects/CMakeLists.txt | 1 + src/objects/bubble-shield.cpp | 136 ++++++++++++++++++++++++++++++++++ src/p_mobj.c | 14 ++++ 5 files changed, 157 insertions(+) create mode 100644 src/objects/bubble-shield.cpp diff --git a/src/k_kart.c b/src/k_kart.c index 5173fa3eb..b991e3dc4 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -14021,6 +14021,8 @@ void K_MoveKartPlayer(player_t *player, boolean onground) P_SetTarget(&shield->target, player->mo); S_StartSound(player->mo, sfx_s3k3f); player->curshield = KSHIELD_BUBBLE; + + Obj_SpawnBubbleShieldVisuals(shield); } if (!HOLDING_ITEM && NO_HYUDORO) diff --git a/src/k_objects.h b/src/k_objects.h index c661e47d2..d92ba8d40 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -444,6 +444,10 @@ UINT8 K_HogChargeToHogCount(INT32 charge, UINT8 cap); void K_UpdateBallhogReticules(player_t *player, UINT8 num_hogs, boolean on_release); void K_DoBallhogAttack(player_t *player, UINT8 num_hogs); +/* Bubble Shield */ +void Obj_SpawnBubbleShieldVisuals(mobj_t *source); +boolean Obj_TickBubbleShieldVisual(mobj_t *mobj); + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/objects/CMakeLists.txt b/src/objects/CMakeLists.txt index 593d5b8f6..63b0d5665 100644 --- a/src/objects/CMakeLists.txt +++ b/src/objects/CMakeLists.txt @@ -60,6 +60,7 @@ target_sources(SRB2SDL2 PRIVATE pulley.cpp amps.c ballhog.cpp + bubble-shield.cpp ) add_subdirectory(versus) diff --git a/src/objects/bubble-shield.cpp b/src/objects/bubble-shield.cpp new file mode 100644 index 000000000..89d60e129 --- /dev/null +++ b/src/objects/bubble-shield.cpp @@ -0,0 +1,136 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2025 by James Robert Roman +// Copyright (C) 2025 by Kart Krew +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- + +#include + +#include "objects.hpp" + +#include "../m_easing.h" +#include "../m_fixed.h" +#include "../tables.h" + +using namespace srb2::objects; + +namespace +{ + +struct Bubble : Mobj +{ + static constexpr fixed_t kBaseScale = 5*FRACUNIT/4; + static constexpr fixed_t kMaxScale = 5*FRACUNIT; + static constexpr fixed_t kScaleRange = kMaxScale - kBaseScale; + + void target() = delete; + Mobj* follow() const { return Mobj::target(); } + void follow(Mobj* n) { Mobj::target(n); } + + player_t* player() const { return follow()->player; } + + bool valid() const { return Mobj::valid(follow()) && player(); } +}; + +struct Visual : Mobj +{ + void target() = delete; + Bubble* bubble() const { return Mobj::target(); } + void bubble(Bubble* n) { Mobj::target(n); } + + void extravalue1() = delete; + Fixed prev_scale() const { return Mobj::extravalue1; } + void prev_scale(Fixed n) { Mobj::extravalue1 = n; } + + bool valid() const { return Mobj::valid(bubble()) && bubble()->valid(); } + + static void spawn + ( Bubble* bubble, + statenum_t state, + int flicker, + int offset) + { + if (!bubble->valid()) + return; + + Visual* x = Mobj::spawn(bubble->pos(), MT_BUBBLESHIELD_VISUAL); + //x->scale(5 * x->scale() / 4); + x->state(state); + x->spriteyoffset(22*FRACUNIT); + x->bubble(bubble); + x->linkdraw(bubble->follow(), offset); + + if (flicker) + x->renderflags |= RF_DONTDRAW; + } + + bool tick() + { + if (!valid()) + { + remove(); + return false; + } + + move_origin(bubble()->pos()); + + renderflags = ((renderflags ^ RF_DONTDRAW) & RF_DONTDRAW); + + // ATTENTION: this object relies on the original MT_BUBBLESHIELD object for scale + fixed_t f = Fixed {bubble()->scale() / bubble()->follow()->scale() - Bubble::kBaseScale} / Fixed {Bubble::kScaleRange}; + + scale(Easing_Linear(f, + bubble()->follow()->scale() * Fixed {Bubble::kBaseScale}, + bubble()->follow()->scale() * 4)); + + if (sprite != SPR_BUBB && + sprite != SPR_BUBC && + bubble()->player()->bubblecool && + f == 0) // base size + renderflags |= RF_DONTDRAW; + + if (scale() > prev_scale()) + spritescale({ 3*FRACUNIT/2, 3*FRACUNIT/4 }); + else if (scale() < prev_scale()) + spritescale({ 3*FRACUNIT/4, 3*FRACUNIT/2 }); + else + { + if (f == FRACUNIT) // max size + { + if (leveltime & 1) + spritescale({ 3*FRACUNIT/4, 3*FRACUNIT/2 }); + else + { + spritescale({ FRACUNIT, FRACUNIT }); + renderflags |= RF_ADD; + } + } + else + spritescale({ FRACUNIT, FRACUNIT }); + } + + prev_scale(scale()); + + return true; + } +}; + +}; // namespace + +void Obj_SpawnBubbleShieldVisuals(mobj_t *bubble) +{ + Visual::spawn(static_cast(bubble), S_BUBA1, 1, 2); + Visual::spawn(static_cast(bubble), S_BUBB1, 0, 1); + Visual::spawn(static_cast(bubble), S_BUBC1, 1, -1); + Visual::spawn(static_cast(bubble), S_BUBD1, 0, -2); + Visual::spawn(static_cast(bubble), S_BUBE1, 1, -3); +} + +boolean Obj_TickBubbleShieldVisual(mobj_t *mobj) +{ + return static_cast(mobj)->tick(); +} diff --git a/src/p_mobj.c b/src/p_mobj.c index ee7a8f5e0..5ccb3b046 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -6645,6 +6645,14 @@ static void P_MobjSceneryThink(mobj_t *mobj) Obj_PulleyThink(mobj); return; } + case MT_BUBBLESHIELD_VISUAL: + { + if (!Obj_TickBubbleShieldVisual(mobj)) + { + return; + } + break; + } default: if (mobj->fuse) { // Scenery object fuse! Very basic! @@ -8521,6 +8529,12 @@ static boolean P_MobjRegularThink(mobj_t *mobj) return false; } + // FIXME: Old Bubble Shield code is still running. + // Some of it is visual, some gameplay. + // I left it alone and just tell it to go invisible~ + // See objects/bubble-shield.cpp + mobj->renderflags |= RF_DONTDRAW; + scale = (5*mobj->target->scale)>>2; curstate = ((mobj->tics == 1) ? (mobj->state->nextstate) : ((statenum_t)(mobj->state-states)));