Add new Bubble Shield visuals (held version)

This commit is contained in:
James R 2025-05-13 18:25:16 -07:00
parent 48f5a8a1e4
commit 7190106ab7
5 changed files with 157 additions and 0 deletions

View file

@ -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)

View file

@ -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

View file

@ -60,6 +60,7 @@ target_sources(SRB2SDL2 PRIVATE
pulley.cpp
amps.c
ballhog.cpp
bubble-shield.cpp
)
add_subdirectory(versus)

View file

@ -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 <algorithm>
#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<Bubble>(); }
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<Visual>(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*>(bubble), S_BUBA1, 1, 2);
Visual::spawn(static_cast<Bubble*>(bubble), S_BUBB1, 0, 1);
Visual::spawn(static_cast<Bubble*>(bubble), S_BUBC1, 1, -1);
Visual::spawn(static_cast<Bubble*>(bubble), S_BUBD1, 0, -2);
Visual::spawn(static_cast<Bubble*>(bubble), S_BUBE1, 1, -3);
}
boolean Obj_TickBubbleShieldVisual(mobj_t *mobj)
{
return static_cast<Visual*>(mobj)->tick();
}

View file

@ -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)));