diff --git a/src/d_player.h b/src/d_player.h index 39a25a8fd..30cf9c0de 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -189,6 +189,8 @@ typedef enum POWERUP_BUMPER, POWERUP_BADGE, POWERUP_SUPERFLICKY, + ENDOFPOWERUPS, + NUMPOWERUPS = ENDOFPOWERUPS - FIRSTPOWERUP, } kartitems_t; typedef enum @@ -463,6 +465,9 @@ typedef struct { // player_t struct for power-ups struct powerupvars_t { + UINT16 superTimer; + UINT16 barrierTimer; + UINT16 rhythmBadgeTimer; mobj_t *flickyController; }; diff --git a/src/deh_tables.c b/src/deh_tables.c index 9c4b19c88..9453f1cba 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -4586,6 +4586,8 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi "S_GACHABOM_RETURNING", "S_SUPER_FLICKY", + + "S_POWERUP_AURA", }; // RegEx to generate this from info.h: ^\tMT_([^,]+), --> \t"MT_\1", @@ -5717,6 +5719,8 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t "MT_SUPER_FLICKY", "MT_SUPER_FLICKY_CONTROLLER", + + "MT_POWERUP_AURA", }; const char *const MOBJFLAG_LIST[] = { @@ -6880,6 +6884,8 @@ struct int_const_s const INT_CONST[] = { {"POWERUP_BUMPER",POWERUP_BUMPER}, {"POWERUP_BADGE",POWERUP_BADGE}, {"POWERUP_SUPERFLICKY",POWERUP_SUPERFLICKY}, + {"ENDOFPOWERUPS",ENDOFPOWERUPS}, + {"NUMPOWERUPS",NUMPOWERUPS}, // kartshields_t {"KSHIELD_NONE",KSHIELD_NONE}, diff --git a/src/hud/CMakeLists.txt b/src/hud/CMakeLists.txt index 7ef234ac1..b30d1156e 100644 --- a/src/hud/CMakeLists.txt +++ b/src/hud/CMakeLists.txt @@ -1,3 +1,4 @@ target_sources(SRB2SDL2 PRIVATE + powerup.cpp timer.cpp ) diff --git a/src/hud/powerup.cpp b/src/hud/powerup.cpp new file mode 100644 index 000000000..dfdabca15 --- /dev/null +++ b/src/hud/powerup.cpp @@ -0,0 +1,133 @@ +#include +#include + +#include + +#include "../core/static_vec.hpp" + +#include "../doomstat.h" +#include "../g_game.h" +#include "../k_hud.h" +#include "../k_powerup.h" +#include "../p_local.h" +#include "../r_fps.h" +#include "../v_draw.hpp" + +using srb2::Draw; + +namespace +{ + +struct Icon +{ + kartitems_t powerup; + tic_t time; + + Icon() {} + + explicit Icon(int k) : + powerup(static_cast(k)), + time(K_PowerUpRemaining(stplyr, powerup)) + { + } + + int letter() const { return 'A' + (powerup - FIRSTPOWERUP); } + + bool operator <(const Icon& b) const { return time < b.time; } + bool operator >(const Icon& b) const { return time > b.time; } +}; + +srb2::StaticVec get_powerup_list(bool ascending) +{ + srb2::StaticVec v; + + for (int k = FIRSTPOWERUP; k < ENDOFPOWERUPS; ++k) + { + Icon ico(k); + + if (ico.time) + { + v.push_back(ico); + } + } + + if (ascending) + { + std::sort(v.begin(), v.end(), std::less()); + } + else + { + std::sort(v.begin(), v.end(), std::greater()); + } + + return v; +} + +}; // namespace + +void K_drawKartPowerUps(void) +{ + struct Offsets + { + Draw row; + const char* sprite; + int spr_x; + int spr_y; + int shift_x; + int dir; + }; + + auto make_offsets = []() -> Offsets + { + auto make_drawer = [](int x, int y, Draw::Font font) -> Draw + { + return Draw(x, y).font(font).align(Draw::Align::kRight); + }; + + const int viewnum = R_GetViewNumber(); + + // 1/2P + switch (r_splitscreen) + { + case 0: + return { make_drawer(307, 55, Draw::Font::kZVote), "PWRU", -17, 7, -35, -1 }; + + case 1: + return { make_drawer(318, viewnum == 0 ? 55 : 155, Draw::Font::kPing), "PWRS", -9, 6, -19, -1 }; + } + + // 3/4P + int x = 21; + int y = 47; + + int dir = 1; + + switch (viewnum) + { + case 1: + case 3: + x = 318; + dir = -1; + } + + switch (viewnum) + { + case 2: + case 3: + y += 100; + } + + return { make_drawer(x, y, Draw::Font::kPing), "PWRS", -9, 5, 19 * dir, dir }; + }; + + Offsets i = make_offsets(); + + for (const Icon& ico : get_powerup_list(i.dir == -1)) + { + i.row.xy(i.spr_x, i.spr_y) + .colormap(static_cast(stplyr->skincolor)) + .patch(fmt::format("{0}{1:c}L{1:c}R", i.sprite, ico.letter()).c_str()); + i.row.text("{}", (ico.time + (TICRATE / 2)) / TICRATE); + i.row = i.row.x(i.shift_x); + } +} diff --git a/src/info.c b/src/info.c index 37e2a9034..e4c0cbb2d 100644 --- a/src/info.c +++ b/src/info.c @@ -652,6 +652,7 @@ char sprnames[NUMSPRITES + 1][5] = "ITMI", "ITMN", "PWRB", + "RBOW", // power-up aura "WANT", "PBOM", // player bomb @@ -5256,6 +5257,8 @@ state_t states[NUMSTATES] = {SPR_GBOM, FF_INVERT, -1, {A_SetScale}, FRACUNIT/2, 1, S_NULL}, // S_GACHABOM_RETURNING {SPR_3DFR, 1|FF_ANIMATE, -1, {NULL}, 2, 5, S_NULL}, // S_SUPER_FLICKY + + {SPR_RBOW, FF_PAPERSPRITE|FF_ADD|FF_FULLBRIGHT|FF_ANIMATE, -1, {NULL}, 14, 2, S_NULL}, // S_POWERUP_AURA }; mobjinfo_t mobjinfo[NUMMOBJTYPES] = @@ -29896,6 +29899,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = MF_NOSECTOR|MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_SCENERY, // flags S_NULL // raisestate }, + + { // MT_POWERUP_AURA + -1, // doomednum + S_POWERUP_AURA, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 0, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 106*FRACUNIT, // height + 0, // display offset + 0, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_SCENERY, // flags + S_NULL // raisestate + }, }; skincolor_t skincolors[MAXSKINCOLORS] = { diff --git a/src/info.h b/src/info.h index 289bbe438..99c7e5bb8 100644 --- a/src/info.h +++ b/src/info.h @@ -1203,6 +1203,7 @@ typedef enum sprite SPR_ITMI, SPR_ITMN, SPR_PWRB, + SPR_RBOW, // power-up aura SPR_WANT, SPR_PBOM, // player bomb @@ -5687,6 +5688,8 @@ typedef enum state S_SUPER_FLICKY, + S_POWERUP_AURA, + S_FIRSTFREESLOT, S_LASTFREESLOT = S_FIRSTFREESLOT + NUMSTATEFREESLOTS - 1, NUMSTATES @@ -6837,6 +6840,8 @@ typedef enum mobj_type MT_SUPER_FLICKY, MT_SUPER_FLICKY_CONTROLLER, + MT_POWERUP_AURA, + MT_FIRSTFREESLOT, MT_LASTFREESLOT = MT_FIRSTFREESLOT + NUMMOBJFREESLOTS - 1, NUMMOBJTYPES diff --git a/src/k_hud.c b/src/k_hud.c index acbaa24c2..c6d71dec9 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -5521,6 +5521,8 @@ void K_drawKartHUD(void) K_drawMiniPing(); } + K_drawKartPowerUps(); + if (G_IsPartyLocal(displayplayers[viewnum]) == false && !demo.playback) { K_drawDirectorHUD(); diff --git a/src/k_hud.h b/src/k_hud.h index 9f6f7c3c9..9c6d4bebd 100644 --- a/src/k_hud.h +++ b/src/k_hud.h @@ -40,6 +40,7 @@ const char *K_GetItemPatch(UINT8 item, boolean tiny); void K_LoadKartHUDGraphics(void); void K_drawKartHUD(void); void K_drawKartFreePlay(void); +void K_drawKartPowerUps(void); void K_drawKartTimestamp(tic_t drawtime, INT32 TX, INT32 TY, INT32 splitflags, UINT8 mode); void K_drawKart2PTimestamp(void); void K_drawKart4PTimestamp(void); diff --git a/src/k_kart.c b/src/k_kart.c index ed58a5b19..f2ce208e2 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -45,6 +45,7 @@ #include "k_specialstage.h" #include "k_roulette.h" #include "k_podium.h" +#include "k_powerup.h" // SOME IMPORTANT VARIABLES DEFINED IN DOOMDEF.H: // gamespeed is cc (0 for easy, 1 for normal, 2 for hard) @@ -4482,19 +4483,12 @@ void K_DebtStingPlayer(player_t *player, mobj_t *source) P_SetPlayerMobjState(player->mo, S_KART_SPINOUT); } -void K_TakeBumpersFromPlayer(player_t *player, player_t *victim, UINT8 amount) +void K_GiveBumpersToPlayer(player_t *player, player_t *victim, UINT8 amount) { const UINT8 oldPlayerBumpers = K_Bumpers(player); UINT8 tookBumpers = 0; - amount = min(amount, K_Bumpers(victim)); - - if (amount == 0) - { - return; - } - while (tookBumpers < amount) { const UINT8 newbumper = (oldPlayerBumpers + tookBumpers); @@ -4520,11 +4514,15 @@ void K_TakeBumpersFromPlayer(player_t *player, player_t *victim, UINT8 amount) newmo = P_SpawnMobj(newx, newy, player->mo->z, MT_BATTLEBUMPER); newmo->threshold = newbumper; - P_SetTarget(&newmo->tracer, victim->mo); + if (victim) + { + P_SetTarget(&newmo->tracer, victim->mo); + } + P_SetTarget(&newmo->target, player->mo); newmo->angle = (diff * (newbumper-1)); - newmo->color = victim->skincolor; + newmo->color = (victim ? victim : player)->skincolor; if (newbumper+1 < 2) { @@ -4544,6 +4542,18 @@ void K_TakeBumpersFromPlayer(player_t *player, player_t *victim, UINT8 amount) // :jartcookiedance: player->mo->health += tookBumpers; +} + +void K_TakeBumpersFromPlayer(player_t *player, player_t *victim, UINT8 amount) +{ + amount = min(amount, K_Bumpers(victim)); + + if (amount == 0) + { + return; + } + + K_GiveBumpersToPlayer(player, victim, amount); // Play steal sound S_StartSound(player->mo, sfx_3db06); @@ -7997,7 +8007,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) player->spheredigestion = spheredigestion; } - if (K_PlayerGuard(player) && (player->ebrakefor%6 == 0)) + if (K_PlayerGuard(player) && !K_PowerUpRemaining(player, POWERUP_BARRIER) && (player->ebrakefor%6 == 0)) player->spheres--; } else @@ -8090,6 +8100,22 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) } } + if (player->powerup.rhythmBadgeTimer > 0) + { + player->instaShieldCooldown = min(player->instaShieldCooldown, 1); + player->powerup.rhythmBadgeTimer--; + } + + if (player->powerup.barrierTimer > 0) + { + player->powerup.barrierTimer--; + } + + if (player->powerup.superTimer > 0) + { + player->powerup.superTimer--; + } + if (player->guardCooldown) player->guardCooldown--; @@ -8334,7 +8360,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) player->pflags &= ~PF_DRIFTINPUT; } - if (K_PlayerGuard(player)) + if (K_PlayerGuard(player) && !K_PowerUpRemaining(player, POWERUP_BARRIER)) player->instaShieldCooldown = max(player->instaShieldCooldown, INSTAWHIP_DROPGUARD); // Roulette Code @@ -10057,7 +10083,17 @@ boolean K_PlayerEBrake(player_t *player) boolean K_PlayerGuard(player_t *player) { - return (K_PlayerEBrake(player) && player->spheres > 0 && player->guardCooldown == 0); + if (player->guardCooldown != 0) + { + return false; + } + + if (K_PowerUpRemaining(player, POWERUP_BARRIER)) + { + return true; + } + + return (K_PlayerEBrake(player) && player->spheres > 0); } SINT8 K_Sliptiding(player_t *player) @@ -10101,26 +10137,6 @@ void K_KartEbrakeVisuals(player_t *p) if (!S_SoundPlaying(p->mo, sfx_s3kd9s)) S_ReducedVFXSound(p->mo, sfx_s3kd9s, p); - // Block visuals - // (These objects track whether a player is block-eligible on their own, no worries) - if (!p->ebrakefor) - { - mobj_t *ring = P_SpawnMobj(p->mo->x, p->mo->y, p->mo->z, MT_BLOCKRING); - P_SetTarget(&ring->target, p->mo); - P_SetScale(ring, p->mo->scale); - K_MatchGenericExtraFlags(ring, p->mo); - ring->renderflags &= ~RF_DONTDRAW; - - mobj_t *body = P_SpawnMobj(p->mo->x, p->mo->y, p->mo->z, MT_BLOCKBODY); - P_SetTarget(&body->target, p->mo); - P_SetScale(body, p->mo->scale); - K_MatchGenericExtraFlags(body, p->mo); - body->renderflags |= RF_DONTDRAW; - - if (K_PlayerGuard(p)) - S_StartSound(body, sfx_s1af); - } - // HOLD! bubble. if (!p->ebrakefor) { @@ -10864,7 +10880,12 @@ void K_MoveKartPlayer(player_t *player, boolean onground) else { player->instaShieldCooldown = INSTAWHIP_COOLDOWN; - player->guardCooldown = INSTAWHIP_COOLDOWN; + + if (!K_PowerUpRemaining(player, POWERUP_BARRIER)) + { + player->guardCooldown = INSTAWHIP_COOLDOWN; + } + S_StartSound(player->mo, sfx_iwhp); mobj_t *whip = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_INSTAWHIP); P_SetTarget(&player->whip, whip); @@ -10876,10 +10897,13 @@ void K_MoveKartPlayer(player_t *player, boolean onground) player->flashing = max(player->flashing, 12); player->mo->momz += 4*mapobjectscale; - // Spawn in triangle formation - Obj_SpawnInstaWhipRecharge(player, 0); - Obj_SpawnInstaWhipRecharge(player, ANGLE_120); - Obj_SpawnInstaWhipRecharge(player, ANGLE_240); + if (!K_PowerUpRemaining(player, POWERUP_BADGE)) + { + // Spawn in triangle formation + Obj_SpawnInstaWhipRecharge(player, 0); + Obj_SpawnInstaWhipRecharge(player, ANGLE_120); + Obj_SpawnInstaWhipRecharge(player, ANGLE_240); + } } } diff --git a/src/k_kart.h b/src/k_kart.h index 9c9d876d8..b51e324e1 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -113,6 +113,7 @@ void K_UpdateStumbleIndicator(player_t *player); void K_UpdateSliptideZipIndicator(player_t *player); INT32 K_ExplodePlayer(player_t *player, mobj_t *inflictor, mobj_t *source); void K_DebtStingPlayer(player_t *player, mobj_t *source); +void K_GiveBumpersToPlayer(player_t *player, player_t *victim, UINT8 amount); void K_TakeBumpersFromPlayer(player_t *player, player_t *victim, UINT8 amount); void K_MineFlashScreen(mobj_t *source); void K_SpawnMineExplosion(mobj_t *source, UINT8 color, tic_t delay); diff --git a/src/k_objects.h b/src/k_objects.h index cd645a1e7..b5cbb6e70 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -161,6 +161,10 @@ void Obj_SuperFlickyPlayerCollide(mobj_t *flicky, mobj_t *player); void Obj_SuperFlickyLanding(mobj_t *flicky); boolean Obj_IsSuperFlickyWhippable(const mobj_t *flicky); +/* Power-Up Aura */ +void Obj_SpawnPowerUpAura(player_t* player); +void Obj_PowerUpAuraThink(mobj_t* mobj); + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/k_powerup.cpp b/src/k_powerup.cpp index f646ec4f7..84a1e27f3 100644 --- a/src/k_powerup.cpp +++ b/src/k_powerup.cpp @@ -8,6 +8,15 @@ tic_t K_PowerUpRemaining(const player_t* player, kartitems_t powerup) { switch (powerup) { + case POWERUP_SMONITOR: + return player->powerup.superTimer; + + case POWERUP_BARRIER: + return player->powerup.barrierTimer; + + case POWERUP_BADGE: + return player->powerup.rhythmBadgeTimer; + case POWERUP_SUPERFLICKY: return Obj_SuperFlickySwarmTime(player->powerup.flickyController); @@ -16,10 +25,45 @@ tic_t K_PowerUpRemaining(const player_t* player, kartitems_t powerup) } } +boolean K_AnyPowerUpRemaining(const player_t* player) +{ + for (int k = FIRSTPOWERUP; k < ENDOFPOWERUPS; ++k) + { + if (K_PowerUpRemaining(player, static_cast(k))) + { + return true; + } + } + + return false; +} + void K_GivePowerUp(player_t* player, kartitems_t powerup, tic_t time) { + if (!K_AnyPowerUpRemaining(player)) + { + Obj_SpawnPowerUpAura(player); + } + switch (powerup) { + case POWERUP_SMONITOR: + K_DoInvincibility(player, time); + player->powerup.superTimer += time; + break; + + case POWERUP_BARRIER: + player->powerup.barrierTimer += time; + break; + + case POWERUP_BUMPER: + K_GiveBumpersToPlayer(player, nullptr, 5); + break; + + case POWERUP_BADGE: + player->powerup.rhythmBadgeTimer += time; + break; + case POWERUP_SUPERFLICKY: if (K_PowerUpRemaining(player, POWERUP_SUPERFLICKY)) { @@ -38,6 +82,21 @@ void K_GivePowerUp(player_t* player, kartitems_t powerup, tic_t time) void K_DropPowerUps(player_t* player) { + auto simple_drop = [player](kartitems_t powerup, auto& timer) + { + tic_t remaining = K_PowerUpRemaining(player, powerup); + + if (remaining) + { + K_DropPaperItem(player, powerup, remaining); + timer = 0; + } + }; + + simple_drop(POWERUP_SMONITOR, player->powerup.superTimer); + simple_drop(POWERUP_BARRIER, player->powerup.barrierTimer); + simple_drop(POWERUP_BADGE, player->powerup.rhythmBadgeTimer); + if (K_PowerUpRemaining(player, POWERUP_SUPERFLICKY)) { mobj_t* swarm = player->powerup.flickyController; diff --git a/src/k_powerup.h b/src/k_powerup.h index 736ce68fe..0598e45ce 100644 --- a/src/k_powerup.h +++ b/src/k_powerup.h @@ -9,6 +9,7 @@ extern "C" { #endif tic_t K_PowerUpRemaining(const player_t *player, kartitems_t powerup); +boolean K_AnyPowerUpRemaining(const player_t *player); void K_GivePowerUp(player_t *player, kartitems_t powerup, tic_t timer); void K_DropPowerUps(player_t *player); diff --git a/src/objects/CMakeLists.txt b/src/objects/CMakeLists.txt index 1bbf55729..2c1e68030 100644 --- a/src/objects/CMakeLists.txt +++ b/src/objects/CMakeLists.txt @@ -22,4 +22,5 @@ target_sources(SRB2SDL2 PRIVATE gachabom-rebound.cpp servant-hand.c super-flicky.cpp + powerup-aura.cpp ) diff --git a/src/objects/block.c b/src/objects/block.c index b2d70ed49..d9f1dbd82 100644 --- a/src/objects/block.c +++ b/src/objects/block.c @@ -3,10 +3,16 @@ #include "../k_objects.h" #include "../p_local.h" #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 || !ring->target->player->ebrakefor) + if (P_MobjWasRemoved(ring->target) || !ring->target->player) { P_RemoveMobj(ring); } @@ -22,7 +28,7 @@ void Obj_BlockRingThink (mobj_t *ring) ring->color = mo->color; fixed_t baseScale = mo->scale / 2; - baseScale += (mo->scale / 30) * player->spheres; + baseScale += (mo->scale / 30) * guard_upscale(player); P_SetScale(ring, baseScale); // Twirl @@ -42,7 +48,7 @@ void Obj_BlockRingThink (mobj_t *ring) void Obj_BlockBodyThink (mobj_t *body) { - if (P_MobjWasRemoved(body->target) || !body->target->player || !body->target->player->ebrakefor) + if (P_MobjWasRemoved(body->target) || !body->target->player) { P_RemoveMobj(body); } @@ -55,7 +61,7 @@ void Obj_BlockBodyThink (mobj_t *body) body->flags &= ~(MF_NOCLIPTHING); fixed_t baseScale = mo->scale / 2; - baseScale += (mo->scale / 30) * player->spheres; + baseScale += (mo->scale / 30) * guard_upscale(player); P_SetScale(body, baseScale); P_MoveOrigin(body, mo->x, mo->y, mo->z + mo->height/2); diff --git a/src/objects/powerup-aura.cpp b/src/objects/powerup-aura.cpp new file mode 100644 index 000000000..80620010d --- /dev/null +++ b/src/objects/powerup-aura.cpp @@ -0,0 +1,121 @@ +#include "../info.h" +#include "../g_game.h" +#include "../m_fixed.h" +#include "../k_objects.h" +#include "../k_powerup.h" +#include "../p_local.h" +#include "../p_mobj.h" +#include "../tables.h" + +// copied from objects/monitor.c +#define FINE90 (FINEANGLES/4) +#define FINE180 (FINEANGLES/2) +#define TRUETAN(n) FINETANGENT(FINE90 + (n)) // bruh + +#define part_theta(o) ((o)->movedir) +#define part_seek(o) ((o)->extravalue1) + +namespace +{ + +constexpr int kSpriteWidth = 32; +constexpr int kNumSides = 6; + +struct Aura : mobj_t +{ + angle_t theta() const { return part_theta(this); } + void theta(angle_t n) { part_theta(this) = n; } + + unsigned seek() const { return part_seek(this); } + void seek(unsigned n) { part_seek(this) = n; } + + mobj_t* origin() const { return players[seek()].mo; } + + static void spawn(int player) + { + const fixed_t angle_factor = ANGLE_MAX / kNumSides; + + angle_t ang = 0u; + + for (int i = 0; i < kNumSides; ++i) + { + Aura* x = static_cast(P_SpawnMobj(0, 0, 0, MT_POWERUP_AURA)); + + x->theta(ang); + x->seek(player); + + ang += angle_factor; + } + } + + // copied from objects/monitor.c + static fixed_t get_inradius(fixed_t length) + { + return FixedDiv(length, 2 * TRUETAN(FINE180 / kNumSides)); + } + + bool valid() const + { + if (seek() >= MAXPLAYERS) + { + return false; + } + + if (!playeringame[seek()]) + { + return false; + } + + if (!K_AnyPowerUpRemaining(&players[seek()])) + { + return false; + } + + return true; + } + + void move() + { + if (P_MobjWasRemoved(origin())) + { + return; + } + + P_MoveOrigin(this, origin()->x, origin()->y, origin()->z); + P_InstaScale(this, 11 * origin()->scale / 10); + + translate(); + } + + void translate() + { + const fixed_t width = scale * kSpriteWidth; + const fixed_t rad = get_inradius(width); + const angle_t ang = theta() + origin()->angle; + + angle = (ang - ANGLE_90); + + sprxoff = FixedMul(FCOS(ang), rad); + spryoff = FixedMul(FSIN(ang), rad); + } +}; + +}; // namespace + +void Obj_SpawnPowerUpAura(player_t* player) +{ + Aura::spawn(player - players); +} + +void Obj_PowerUpAuraThink(mobj_t* mobj) +{ + Aura* x = static_cast(mobj); + + if (!x->valid()) + { + P_RemoveMobj(x); + return; + } + + x->move(); +} diff --git a/src/p_inter.c b/src/p_inter.c index e2c5ed6fc..86de35c03 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -2418,7 +2418,8 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da if (source && source != player->mo && source->player) { // Extend the invincibility if the hit was a direct hit. - if (inflictor == source && source->player->invincibilitytimer) + if (inflictor == source && source->player->invincibilitytimer && + !K_PowerUpRemaining(player, POWERUP_SMONITOR)) { tic_t kinvextend; diff --git a/src/p_mobj.c b/src/p_mobj.c index 6592fab7b..5e89fefbf 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -6698,6 +6698,14 @@ static void P_MobjSceneryThink(mobj_t *mobj) case MT_SUPER_FLICKY_CONTROLLER: Obj_SuperFlickyControllerThink(mobj); + if (P_MobjWasRemoved(mobj)) + { + return; + } + break; + case MT_POWERUP_AURA: + Obj_PowerUpAuraThink(mobj); + if (P_MobjWasRemoved(mobj)) { return; @@ -11866,6 +11874,26 @@ void P_SpawnPlayer(INT32 playernum) } } + // Block visuals + // (These objects track whether a player is block-eligible on their own, no worries) + if (!p->spectator) + { + mobj_t *ring = P_SpawnMobj(p->mo->x, p->mo->y, p->mo->z, MT_BLOCKRING); + P_SetTarget(&ring->target, p->mo); + P_SetScale(ring, p->mo->scale); + K_MatchGenericExtraFlags(ring, p->mo); + ring->renderflags &= ~RF_DONTDRAW; + + mobj_t *body = P_SpawnMobj(p->mo->x, p->mo->y, p->mo->z, MT_BLOCKBODY); + P_SetTarget(&body->target, p->mo); + P_SetScale(body, p->mo->scale); + K_MatchGenericExtraFlags(body, p->mo); + body->renderflags |= RF_DONTDRAW; + + if (K_PlayerGuard(p)) + S_StartSound(body, sfx_s1af); + } + // I'm not refactoring the loop at the top of this file. pcount = 0; diff --git a/src/p_saveg.c b/src/p_saveg.c index 6fea134e7..e6f7a61d6 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -634,6 +634,11 @@ static void P_NetArchivePlayers(savebuffer_t *save) // ACS has read access to this, so it has to be net-communicated. // It is the ONLY roundcondition that is sent over the wire and I'd like it to stay that way. WRITEUINT32(save->p, players[i].roundconditions.unlocktriggers); + + // powerupvars_t + WRITEUINT16(save->p, players[i].powerup.superTimer); + WRITEUINT16(save->p, players[i].powerup.barrierTimer); + WRITEUINT16(save->p, players[i].powerup.rhythmBadgeTimer); } } @@ -1059,6 +1064,11 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) // It is the ONLY roundcondition that is sent over the wire and I'd like it to stay that way. players[i].roundconditions.unlocktriggers = READUINT32(save->p); + // powerupvars_t + players[i].powerup.superTimer = READUINT16(save->p); + players[i].powerup.barrierTimer = READUINT16(save->p); + players[i].powerup.rhythmBadgeTimer = READUINT16(save->p); + //players[i].viewheight = P_GetPlayerViewHeight(players[i]); // scale cannot be factored in at this point } }