From 69541b942274509543f41f37912399386c94535f Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 7 Dec 2023 00:55:54 -0800 Subject: [PATCH] Hardcode bustable rocks -- Angel Island + Endless Mine --- src/deh_tables.c | 17 +++ src/info.c | 119 +++++++++++++++++++++ src/info.h | 21 ++++ src/k_objects.h | 7 ++ src/objects/CMakeLists.txt | 1 + src/objects/rocks.cpp | 210 +++++++++++++++++++++++++++++++++++++ src/p_inter.c | 7 ++ src/p_link.cpp | 3 +- src/p_mobj.c | 29 +++++ src/p_spec.c | 16 ++- 10 files changed, 427 insertions(+), 3 deletions(-) create mode 100644 src/objects/rocks.cpp diff --git a/src/deh_tables.c b/src/deh_tables.c index 9dfba1898..3a603086c 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -4879,6 +4879,17 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi "S_AIZDB6", "S_AIZDB7", "S_AIZDB8", + + // MT_AZROCKS + "S_AZROCKS", + "S_AZROCKS_RESPAWN", + "S_AZROCKS_PARTICLE1", + + // MT_EMROCKS + "S_EMROCKS", + "S_EMROCKS_RESPAWN", + "S_EMROCKS_PARTICLE1", + "S_EMROCKS_PARTICLE2", }; // RegEx to generate this from info.h: ^\tMT_([^,]+), --> \t"MT_\1", @@ -6130,6 +6141,12 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t "MT_AIZ_TREE", "MT_AIZ_FERN3", "MT_AIZ_DDB", + + "MT_AZROCKS", + "MT_AZROCKS_PARTICLE", + + "MT_EMROCKS", + "MT_EMROCKS_PARTICLE", }; const char *const MOBJFLAG_LIST[] = { diff --git a/src/info.c b/src/info.c index d503ec927..45c011312 100644 --- a/src/info.c +++ b/src/info.c @@ -1010,6 +1010,10 @@ char sprnames[NUMSPRITES + 1][5] = "AZR1", "AZR2", + "EMR1", + "EMR2", + "EMR3", + // First person view sprites; this is a sprite so that it can be replaced by a specialized MD2 draw later "VIEW", }; @@ -5749,6 +5753,17 @@ state_t states[NUMSTATES] = {SPR_AIZ6, 5, 8, {NULL}, 0, 0, S_AIZDB7}, // S_AIZDB6 {SPR_AIZ6, 6, 8, {NULL}, 0, 0, S_AIZDB8}, // S_AIZDB7 {SPR_AIZ6, 7, 10, {NULL}, 0, 0, S_AIZDB1}, // S_AIZDB8 + + // MT_AZROCKS + {SPR_AZR1, 0, -1, {NULL}, 0, 0, S_AZROCKS}, // S_AZROCKS + {SPR_NULL, 0, -1, {NULL}, 0, 0, S_AZROCKS}, // S_AZROCKS_RESPAWN + {SPR_AZR2, 0, 5*TICRATE, {NULL}, 0, 0, S_NULL}, // S_AZROCKS_PARTICLE1 + + // MT_EMROCKS + {SPR_EMR1, 0, -1, {NULL}, 0, 0, S_EMROCKS}, // S_EMROCKS + {SPR_NULL, 0, -1, {NULL}, 0, 0, S_EMROCKS}, // S_EMROCKS_RESPAWN + {SPR_EMR2, 0, 5*TICRATE, {NULL}, 0, 0, S_NULL}, // S_EMROCKS_PARTICLE1 + {SPR_EMR3, 0, 5*TICRATE, {NULL}, 0, 0, S_NULL}, // S_EMROCKS_PARTICLE2 }; mobjinfo_t mobjinfo[NUMMOBJTYPES] = @@ -32819,6 +32834,110 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = MF_SCENERY, // flags S_NULL // raisestate }, + { // MT_AZROCKS + 470, // doomednum + S_AZROCKS, // spawnstate + 1, // 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_s3k59, // deathsound + 0, // speed + 48*FRACUNIT, // radius + 96*FRACUNIT, // height + 0, // dispoffset + 0, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + { // MT_AZROCKS_PARTICLE + -1, // doomednum + S_AZROCKS_PARTICLE1, // spawnstate + 1, // 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 + 32*FRACUNIT, // height + 0, // dispoffset + 0, // mass + 0, // damage + sfx_None, // activesound + MF_NOCLIPHEIGHT|MF_SCENERY|MF_NOBLOCKMAP, // flags + S_NULL // raisestate + }, + { // MT_EMROCKS + 467, // doomednum + S_EMROCKS, // spawnstate + 1, // 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_s3k59, // deathsound + 0, // speed + 48*FRACUNIT, // radius + 96*FRACUNIT, // height + 0, // dispoffset + 0, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + { // MT_EMROCKS_PARTICLE + -1, // doomednum + S_EMROCKS_PARTICLE1, // spawnstate + 1, // 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 + 32*FRACUNIT, // height + 0, // dispoffset + 0, // mass + 0, // damage + sfx_None, // activesound + MF_NOCLIPHEIGHT|MF_SCENERY|MF_NOBLOCKMAP, // flags + S_NULL // raisestate + }, }; diff --git a/src/info.h b/src/info.h index 700eadeea..0fbee8940 100644 --- a/src/info.h +++ b/src/info.h @@ -1564,6 +1564,10 @@ typedef enum sprite SPR_AZR1, SPR_AZR2, + SPR_EMR1, + SPR_EMR2, + SPR_EMR3, + // First person view sprites; this is a sprite so that it can be replaced by a specialized MD2 draw later SPR_VIEW, @@ -6174,6 +6178,17 @@ typedef enum state S_AIZDB7, S_AIZDB8, + // MT_AZROCKS + S_AZROCKS, + S_AZROCKS_RESPAWN, + S_AZROCKS_PARTICLE1, + + // MT_EMROCKS + S_EMROCKS, + S_EMROCKS_RESPAWN, + S_EMROCKS_PARTICLE1, + S_EMROCKS_PARTICLE2, + S_FIRSTFREESLOT, S_LASTFREESLOT = S_FIRSTFREESLOT + NUMSTATEFREESLOTS - 1, NUMSTATES @@ -7445,6 +7460,12 @@ typedef enum mobj_type MT_AIZ_FERN3, MT_AIZ_DDB, + MT_AZROCKS, + MT_AZROCKS_PARTICLE, + + MT_EMROCKS, + MT_EMROCKS_PARTICLE, + MT_FIRSTFREESLOT, MT_LASTFREESLOT = MT_FIRSTFREESLOT + NUMMOBJFREESLOTS - 1, NUMMOBJTYPES diff --git a/src/k_objects.h b/src/k_objects.h index c564cc2a9..594d89702 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -339,6 +339,13 @@ void Obj_FuelCanisterTouch(mobj_t *special, mobj_t *toucher); void Obj_FuelCanisterExplosionTouch(mobj_t *special, mobj_t *toucher); boolean Obj_FuelCanisterExplosionThink(mobj_t *mo); +/* Bustable Rocks */ +void Obj_LinkRocks(mobj_t *mo); +void Obj_UnlinkRocks(mobj_t *mo); +void Obj_TouchRocks(mobj_t *special, mobj_t *toucher); +void Obj_UpdateRocks(void); +void Obj_AnimateEndlessMineRocks(mobj_t *mo); + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/objects/CMakeLists.txt b/src/objects/CMakeLists.txt index 3048568ea..c8ed02f59 100644 --- a/src/objects/CMakeLists.txt +++ b/src/objects/CMakeLists.txt @@ -47,6 +47,7 @@ target_sources(SRB2SDL2 PRIVATE crate.cpp spear.cpp fuel.cpp + rocks.cpp ) add_subdirectory(versus) diff --git a/src/objects/rocks.cpp b/src/objects/rocks.cpp new file mode 100644 index 000000000..4a27206e4 --- /dev/null +++ b/src/objects/rocks.cpp @@ -0,0 +1,210 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2023 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. +//----------------------------------------------------------------------------- + +// Original Lua script by Sal +// Hardcoded by jartha + +#include +#include + +#include "../math/fixed.hpp" +#include "../math/vec.hpp" +#include "../mobj.hpp" +#include "../mobj_list.hpp" + +#include "../d_player.h" +#include "../doomstat.h" +#include "../info.h" +#include "../k_objects.h" +#include "../m_fixed.h" +#include "../p_pspr.h" +#include "../sounds.h" +#include "../tables.h" + +using srb2::Mobj; +using srb2::MobjList; +using srb2::math::Fixed; +using srb2::math::Vec2; + +extern mobj_t* svg_rocks; + +namespace +{ + +struct AngelIsland +{ + static constexpr statenum_t kRespawnState = S_AZROCKS_RESPAWN; + static constexpr mobjtype_t kParticleType = MT_AZROCKS_PARTICLE; + static constexpr std::array kParticleStates = {S_AZROCKS_PARTICLE1, S_AZROCKS_PARTICLE1}; +}; + +struct EndlessMine +{ + static constexpr statenum_t kRespawnState = S_EMROCKS_RESPAWN; + static constexpr mobjtype_t kParticleType = MT_EMROCKS_PARTICLE; + static constexpr std::array kParticleStates = {S_EMROCKS_PARTICLE1, S_EMROCKS_PARTICLE2}; +}; + +struct AnyRocks : Mobj +{ + void hnext() = delete; + AnyRocks* next() const { return Mobj::hnext(); } + void next(AnyRocks* n) { Mobj::hnext(n); } + + template + bool visit(F&& visitor); +}; + +template +struct Rocks : AnyRocks +{ + bool busted() const { return state()->num() == Config::kRespawnState; } + + void respawn() + { + if (busted()) + { + tics = 2; // respawn soon + } + } + + void touch(Mobj* toucher) + { + if (busted()) + { + if (tics > 0) + { + tics = 2; // postpone respawn + } + } + else + { + slow(toucher); + bust(toucher); + } + } + +private: + static void slow(Mobj* toucher) + { + const player_t* p = toucher->player; + + if (p->sneakertimer || p->invincibilitytimer || p->growshrinktimer > 0 || p->hyudorotimer) + { + return; + } + + toucher->momx /= 2; + toucher->momy /= 2; + toucher->momz = toucher->flip(std::abs(2 * toucher->momz / 3)); + } + + void bust(Mobj* toucher) + { + vfx(toucher, 1); + vfx(toucher, 2); + vfx(toucher, 3); + + voice(info->deathsound); + state(Config::kRespawnState); + } + + void vfx(Mobj* toucher, int i) + { + fixed_t zvar = flip((i + 1) * 4 * mapobjectscale); + angle_t avar = ANGLE_45 * (i - 2); + + auto part = [&](angle_t angle, statenum_t state) + { + Mobj* h = spawn_from({Vec2 {}, zvar}, Config::kParticleType); + h->state(state); + h->angle = angle; + h->instathrust(angle, 4 * mapobjectscale); + h->momz = zvar; + return h; + }; + + static_assert(Config::kParticleStates.size() == 2); + + part(toucher->angle + ANGLE_90 - avar, Config::kParticleStates[0]); + part(toucher->angle - ANGLE_90 + avar, Config::kParticleStates[1]); + } +}; + +struct AngelIslandRocks : Rocks +{ +}; + +struct EndlessMineRocks : Rocks +{ +}; + +template +bool AnyRocks::visit(F&& visitor) +{ + switch (type) + { + case MT_AZROCKS: + visitor(static_cast(this)); + break; + + case MT_EMROCKS: + visitor(static_cast(this)); + break; + + default: + return false; + } + + return true; +} + +MobjList rocks_list; + +}; // namespace + +void Obj_LinkRocks(mobj_t* mo) +{ + rocks_list.push_front(static_cast(mo)); +} + +void Obj_UnlinkRocks(mobj_t* mo) +{ + rocks_list.erase(static_cast(mo)); +} + +void Obj_TouchRocks(mobj_t* special, mobj_t* toucher) +{ + static_cast(special)->visit([&](auto rocks) { rocks->touch(static_cast(toucher)); }); +} + +void Obj_UpdateRocks(void) +{ + for (AnyRocks* h : rocks_list) + { + h->visit([](auto rocks) { rocks->respawn(); }); + } +} + +void Obj_AnimateEndlessMineRocks(mobj_t *mo) +{ + // sync colors with sky animation + constexpr int kFrames = 8; + constexpr int kDiff = kFrames - 2; + constexpr int kTotal = kFrames + kDiff; + + UINT8 f = ((leveltime / 6) % kTotal); + + if (f >= kFrames) + { + f = kTotal - f; + } + + mo->frame = (mo->frame & ~FF_FRAMEMASK) | f; +} diff --git a/src/p_inter.c b/src/p_inter.c index 4c600ef0b..4a8b3e31b 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1007,6 +1007,13 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) return; } + case MT_AZROCKS: + case MT_EMROCKS: + { + Obj_TouchRocks(special, toucher); + return; + } + default: // SOC or script pickup P_SetTarget(&special->target, toucher); break; diff --git a/src/p_link.cpp b/src/p_link.cpp index 1bef20bc4..ca13df7fc 100644 --- a/src/p_link.cpp +++ b/src/p_link.cpp @@ -16,7 +16,8 @@ #define LINK_PACK \ svg_battleUfoSpawners,\ - svg_checkpoints + svg_checkpoints,\ + svg_rocks using link = mobj_t*; diff --git a/src/p_mobj.c b/src/p_mobj.c index 84f793513..5772c640e 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -3156,6 +3156,15 @@ boolean P_SceneryZMovement(mobj_t *mo) return false; } break; + case MT_EMROCKS_PARTICLE: + // Hits the ground + if (mo->momz <= 0 && mo->z + mo->momz <= mo->floorz - mo->height) + { + P_KillMobj(mo, NULL, NULL, DMG_NORMAL); + if (P_MobjWasRemoved(mo)) + return false; + } + break; default: break; } @@ -6880,6 +6889,11 @@ static void P_MobjSceneryThink(mobj_t *mobj) } break; } + case MT_EMROCKS_PARTICLE: + { + Obj_AnimateEndlessMineRocks(mobj); + break; + } case MT_VWREF: case MT_VWREB: { @@ -10285,6 +10299,11 @@ static boolean P_MobjRegularThink(mobj_t *mobj) } break; } + case MT_EMROCKS: + { + Obj_AnimateEndlessMineRocks(mobj); + break; + } case MT_BALLOON: { @@ -11793,6 +11812,10 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) case MT_GPZ_SEASAW_SPAWN: Obj_GPZSeasawSpawn(mobj); break; + case MT_AZROCKS: + case MT_EMROCKS: + Obj_LinkRocks(mobj); + break; default: break; } @@ -12067,6 +12090,12 @@ void P_RemoveMobj(mobj_t *mobj) Obj_UnlinkCheckpoint(mobj); break; } + case MT_AZROCKS: + case MT_EMROCKS: + { + Obj_UnlinkRocks(mobj); + break; + } default: { break; diff --git a/src/p_spec.c b/src/p_spec.c index 287a10e92..55d0bcf48 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -1933,12 +1933,14 @@ static void K_HandleLapIncrement(player_t *player) { size_t i = 0; UINT8 nump = 0; - UINT8 lowestLap; + UINT8 lowestLap = UINT8_MAX; for (i = 0; i < MAXPLAYERS; i++) { if (!playeringame[i] || players[i].spectator) continue; + if (players[i].laps < lowestLap) + lowestLap = players[i].laps; nump++; } @@ -2090,7 +2092,17 @@ static void K_HandleLapIncrement(player_t *player) } thwompsactive = true; // Lap 2 effects - lowestLap = P_FindLowestLap(); + + { + UINT8 prevLowest = lowestLap; + + lowestLap = P_FindLowestLap(); + + if (lowestLap > prevLowest) // last place finished the lap + { + Obj_UpdateRocks(); + } + } for (i = 0; i < numlines; i++) {