diff --git a/src/k_objects.h b/src/k_objects.h index 619b8f81c..d50e17125 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -293,6 +293,9 @@ void Obj_BallSwitchThink(mobj_t *mobj); void Obj_BallSwitchTouched(mobj_t *mobj, mobj_t *toucher); void Obj_BallSwitchDamaged(mobj_t *mobj, mobj_t *inflictor, mobj_t *source); +/* Barrier Power-Up */ +void Obj_SpawnMegaBarrier(player_t *player); +boolean Obj_MegaBarrierThink(mobj_t *mobj); #ifdef __cplusplus } // extern "C" diff --git a/src/k_powerup.cpp b/src/k_powerup.cpp index 3d1fcb312..6e5d6a164 100644 --- a/src/k_powerup.cpp +++ b/src/k_powerup.cpp @@ -56,6 +56,7 @@ void K_GivePowerUp(player_t* player, kartitems_t powerup, tic_t time) case POWERUP_BARRIER: player->powerup.barrierTimer += time; + Obj_SpawnMegaBarrier(player); break; case POWERUP_BUMPER: diff --git a/src/objects/CMakeLists.txt b/src/objects/CMakeLists.txt index 8abbd34ee..2bd4a38b3 100644 --- a/src/objects/CMakeLists.txt +++ b/src/objects/CMakeLists.txt @@ -40,6 +40,7 @@ target_sources(SRB2SDL2 PRIVATE shadow.cpp ball-switch.cpp charge.c + mega-barrier.cpp ) add_subdirectory(versus) diff --git a/src/objects/block.c b/src/objects/block.c index d9f1dbd82..d5682d8a5 100644 --- a/src/objects/block.c +++ b/src/objects/block.c @@ -5,11 +5,6 @@ #include "../k_kart.h" #include "../k_powerup.h" -static INT16 guard_upscale (player_t *player) -{ - return K_PowerUpRemaining(player, POWERUP_BARRIER) ? 40 : player->spheres; -} - void Obj_BlockRingThink (mobj_t *ring) { if (P_MobjWasRemoved(ring->target) || !ring->target->player) @@ -28,7 +23,7 @@ void Obj_BlockRingThink (mobj_t *ring) ring->color = mo->color; fixed_t baseScale = mo->scale / 2; - baseScale += (mo->scale / 30) * guard_upscale(player); + baseScale += (mo->scale / 30) * player->spheres; P_SetScale(ring, baseScale); // Twirl @@ -41,7 +36,7 @@ void Obj_BlockRingThink (mobj_t *ring) else ring->renderflags |= RF_DONTDRAW; - if (!K_PlayerGuard(player)) + if (K_PowerUpRemaining(player, POWERUP_BARRIER) || !K_PlayerGuard(player)) ring->renderflags |= RF_DONTDRAW; } } @@ -61,7 +56,7 @@ void Obj_BlockBodyThink (mobj_t *body) body->flags &= ~(MF_NOCLIPTHING); fixed_t baseScale = mo->scale / 2; - baseScale += (mo->scale / 30) * guard_upscale(player); + baseScale += (mo->scale / 30) * player->spheres; P_SetScale(body, baseScale); P_MoveOrigin(body, mo->x, mo->y, mo->z + mo->height/2); @@ -76,7 +71,7 @@ void Obj_BlockBodyThink (mobj_t *body) else body->renderflags |= RF_DONTDRAW; - if (!K_PlayerGuard(player)) + if (K_PowerUpRemaining(player, POWERUP_BARRIER) || !K_PlayerGuard(player)) body->renderflags |= RF_DONTDRAW; } } diff --git a/src/objects/mega-barrier.cpp b/src/objects/mega-barrier.cpp new file mode 100644 index 000000000..f7ed04719 --- /dev/null +++ b/src/objects/mega-barrier.cpp @@ -0,0 +1,161 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2023 by James Robert Roman +// +// 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 "../doomdef.h" +#include "../d_player.h" +#include "../g_game.h" +#include "../info.h" +#include "../k_kart.h" +#include "../k_objects.h" +#include "../k_powerup.h" +#include "../m_fixed.h" +#include "../p_local.h" +#include "../p_mobj.h" +#include "../r_defs.h" + +#define barrier_player(o) ((o)->extravalue1) + +namespace +{ + +struct Barrier; + +// TODO: header +struct Mobj : mobj_t +{ + struct PosArg + { + fixed_t x, y, z; + + PosArg(fixed_t x_, fixed_t y_, fixed_t z_) : x(x_), y(y_), z(z_) {} + PosArg(const mobj_t* mobj) : x(mobj->x), y(mobj->y), z(mobj->z) {} + }; + + bool valid() const { return !P_MobjWasRemoved(this); } + + PosArg center() const { return {x, y, z + (height / 2)}; } + + template + T* spawn_offset(mobjtype_t type) { return static_cast(P_SpawnMobjFromMobj(this, 0, 0, 0, type)); } + + void state(statenum_t state) { P_SetMobjState(this, state); } + statenum_t statenum() const { return static_cast(mobj_t::state - states); } + + fixed_t scale() const { return mobj_t::scale; } + + void scale(fixed_t n) + { + mobj_t::scale = n; + mobj_t::destscale = n; + } + + void move_origin(const PosArg& p) { P_MoveOrigin(this, p.x, p.y, p.z); } + + void remove() { P_RemoveMobj(this); } +}; + +struct Player : player_t +{ + struct Powerups : powerupvars_t + { + Barrier* barrier() const { return reinterpret_cast(powerupvars_t::barrier); } + void barrier(Barrier* n) { P_SetTarget(&this->powerupvars_t::barrier, reinterpret_cast(n)); } + }; + + static Player* at(std::size_t i) { return static_cast(&players[i]); } + + bool valid() const { return this >= players && this < &players[MAXPLAYERS] && playeringame[num()]; } + + std::size_t num() const { return this - Player::at(0); } + Mobj* mobj() const { return static_cast(mo); } + + Powerups& powerups() { return static_cast(player_t::powerup); } + const Powerups& powerups() const { return static_cast(player_t::powerup); } +}; + +struct Barrier : Mobj +{ + static constexpr angle_t kSpinSpeed = ANGLE_22h; + static constexpr angle_t kSpinGap = 20*ANG1; + + static Barrier* spawn(Player* player, statenum_t state, int idx) + { + Barrier* child = player->mobj()->spawn_offset(MT_MEGABARRIER); + + child->angle = player->mobj()->angle + (idx * kSpinGap); + child->player(player); + child->renderflags |= RF_DONTDRAW; + child->state(state); + + return child; + } + + static void spawn_chain(Player* player) + { + player->powerups().barrier(spawn(player, S_MEGABARRIER1, 0)); + spawn(player, S_MEGABARRIER2, 1); + spawn(player, S_MEGABARRIER2, 2); + spawn(player, S_MEGABARRIER2, 3); + spawn(player, S_MEGABARRIER2, 4); + spawn(player, S_MEGABARRIER3, 5); + } + + int playernum() const { return barrier_player(this); } + Player* player() const { return Player::at(playernum()); } + void player(player_t* n) { barrier_player(this) = n - players; } + + bool valid() const { return Mobj::valid() && player()->valid() && player()->mobj()->valid(); } + + bool think() + { + if (!valid() || !K_PowerUpRemaining(player(), POWERUP_BARRIER)) + { + remove(); + return false; + } + + Mobj* source = player()->mobj(); + color = source->color; + scale(8 * source->scale() / 9); + move_origin(source->center()); + angle += kSpinSpeed; + eflags = (eflags & ~MFE_VERTICALFLIP) | (source->eflags & MFE_VERTICALFLIP); + + if (K_PlayerGuard(player())) + { + renderflags &= ~RF_DONTDRAW; + renderflags ^= RF_ADD | RF_TRANS90; + } + else + { + renderflags |= RF_DONTDRAW; + } + + return true; + } +}; + +}; // namespace + +void Obj_SpawnMegaBarrier(player_t* p) +{ + Player* player = static_cast(p); + + if (!static_cast(player->powerups().barrier())->valid()) + { + Barrier::spawn_chain(player); + } +} + +boolean Obj_MegaBarrierThink(mobj_t* mobj) +{ + return static_cast(mobj)->think(); +} diff --git a/src/p_mobj.c b/src/p_mobj.c index 35eb6a2e5..8adeda49f 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -6807,6 +6807,14 @@ static void P_MobjSceneryThink(mobj_t *mobj) mobj->angle += ANG2; break; } + case MT_MEGABARRIER: + { + if (!Obj_MegaBarrierThink(mobj)) + { + return; + } + break; + } case MT_VWREF: case MT_VWREB: {