diff --git a/src/deh_tables.c b/src/deh_tables.c index 45bbd6955..78d6a6422 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -4844,6 +4844,22 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi "S_ICECAPBLOCK_DEBRIS_D", "S_ICECAPBLOCK_DEBRIS_E", "S_ICECAPBLOCK_DEBRIS_F", + + // MT_SPEAR + "S_SPEAR_ROD", + "S_SPEAR_TIP", + "S_SPEAR_HILT_FRONT", + "S_SPEAR_HILT_BACK", + "S_SPEAR_WALL", + + // MT_BSZLAMP_S + "S_BLMS", + "S_BLMM", + "S_BLML", + + // MT_BSZSLAMP + "S_BSWL", + "S_BSWC", }; // RegEx to generate this from info.h: ^\tMT_([^,]+), --> \t"MT_\1", @@ -6075,6 +6091,14 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t "MT_BOX_DEBRIS", "MT_SA2_CRATE", "MT_ICECAPBLOCK", + + "MT_SPEAR", + "MT_SPEARVISUAL", + "MT_BSZLAMP_S", + "MT_BSZLAMP_M", + "MT_BSZLAMP_L", + "MT_BSZSLAMP", + "MT_BSZSLCHA", }; const char *const MOBJFLAG_LIST[] = { diff --git a/src/info.c b/src/info.c index 26561f4eb..a6ec0a044 100644 --- a/src/info.c +++ b/src/info.c @@ -989,6 +989,16 @@ char sprnames[NUMSPRITES + 1][5] = "SABX", "ICBL", + "BSSP", + "BSPB", + "BSPR", + "BSSR", + "BLMS", + "BLMM", + "BLML", + "BSWL", + "BSWC", + // First person view sprites; this is a sprite so that it can be replaced by a specialized MD2 draw later "VIEW", }; @@ -5693,6 +5703,22 @@ state_t states[NUMSTATES] = {SPR_ICBL, 3, 70, {NULL}, 0, 0, S_NULL}, // S_ICECAPBLOCK_DEBRIS_D {SPR_ICBL, 4, 70, {NULL}, 0, 0, S_NULL}, // S_ICECAPBLOCK_DEBRIS_E {SPR_ICBL, 5, 70, {NULL}, 0, 0, S_NULL}, // S_ICECAPBLOCK_DEBRIS_F + + // MT_SPEAR + {SPR_BSSP, 1|FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_SPEAR_ROD + {SPR_BSSP, 2|FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_SPEAR_TIP + {SPR_BSPR, 0|FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_SPEAR_HILT_FRONT + {SPR_BSPB, 0|FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_SPEAR_HILT_BACK + {SPR_BSSR, 0|FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_SPEAR_WALL + + // MT_BSZLAMP_S + {SPR_BLMS, 0|FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_BLMS + {SPR_BLMM, 0|FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_BLMM + {SPR_BLML, 0|FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_BLML + + // MT_BSZSLAMP + {SPR_BSWL, 0|FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_BSWL + {SPR_BSWC, 0|FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_BSWC }; mobjinfo_t mobjinfo[NUMMOBJTYPES] = @@ -32320,6 +32346,189 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = MF_SPECIAL|MF_SOLID|MF_SHOOTABLE|MF_SCENERY|MF_DONTPUNT, // flags S_NULL // raisestate }, + + { // MT_SPEAR + 3450, // doomednum + S_SPEAR_ROD, // 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 + 64*FRACUNIT, // radius + 80*FRACUNIT, // height + 0, // dispoffset + 0, // mass + 0, // damage + sfx_None, // activesound + MF_PAIN|MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_PAPERCOLLISION|MF_SCENERY|MF_NOHITLAGFORME, // flags + S_NULL // raisestate + }, + { // MT_SPEARVISUAL + -1, // doomednum + S_UNKNOWN, // 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 + 1*FRACUNIT, // radius + 1*FRACUNIT, // height + 0, // dispoffset + 0, // mass + 0, // damage + sfx_None, // activesound + MF_NOCLIPHEIGHT|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_NOBLOCKMAP, // flags + S_NULL // raisestate + }, + { // MT_BSZLAMP_S + 3452, // doomednum + S_BLMS, // 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 + 32*FRACUNIT, // radius + 32*FRACUNIT, // height + 0, // dispoffset + 0, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + { // MT_BSZLAMP_M + 3453, // doomednum + S_BLMM, // 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 + 32*FRACUNIT, // radius + 32*FRACUNIT, // height + 0, // dispoffset + 0, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + { // MT_BSZLAMP_L + 3454, // doomednum + S_BLML, // 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 + 32*FRACUNIT, // radius + 32*FRACUNIT, // height + 0, // dispoffset + 0, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + { // MT_BSZSLAMP + 3469, // doomednum + S_BSWL, // 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 + 96*FRACUNIT, // radius + 128*FRACUNIT, // height + 0, // dispoffset + 0, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + { // MT_BSZSLCHA + 3470, // doomednum + S_BSWC, // 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 + 128*FRACUNIT, // radius + 128*FRACUNIT, // height + 0, // dispoffset + 0, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, }; diff --git a/src/info.h b/src/info.h index c623dcbca..52c1b5942 100644 --- a/src/info.h +++ b/src/info.h @@ -1543,6 +1543,16 @@ typedef enum sprite SPR_SABX, SPR_ICBL, + SPR_BSSP, + SPR_BSPB, + SPR_BSPR, + SPR_BSSR, + SPR_BLMS, + SPR_BLMM, + SPR_BLML, + SPR_BSWL, + SPR_BSWC, + // First person view sprites; this is a sprite so that it can be replaced by a specialized MD2 draw later SPR_VIEW, @@ -6118,6 +6128,22 @@ typedef enum state S_ICECAPBLOCK_DEBRIS_E, S_ICECAPBLOCK_DEBRIS_F, + // MT_SPEAR + S_SPEAR_ROD, + S_SPEAR_TIP, + S_SPEAR_HILT_FRONT, + S_SPEAR_HILT_BACK, + S_SPEAR_WALL, + + // MT_BSZLAMP_S + S_BLMS, + S_BLMM, + S_BLML, + + // MT_BSZSLAMP + S_BSWL, + S_BSWC, + S_FIRSTFREESLOT, S_LASTFREESLOT = S_FIRSTFREESLOT + NUMSTATEFREESLOTS - 1, NUMSTATES @@ -7369,6 +7395,14 @@ typedef enum mobj_type MT_SA2_CRATE, MT_ICECAPBLOCK, + MT_SPEAR, + MT_SPEARVISUAL, + MT_BSZLAMP_S, + MT_BSZLAMP_M, + MT_BSZLAMP_L, + MT_BSZSLAMP, + MT_BSZSLCHA, + MT_FIRSTFREESLOT, MT_LASTFREESLOT = MT_FIRSTFREESLOT + NUMMOBJFREESLOTS - 1, NUMMOBJTYPES diff --git a/src/k_objects.h b/src/k_objects.h index 803a0caa9..215b4b84f 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -328,6 +328,10 @@ boolean Obj_TryCrateThink(mobj_t *mo); void Obj_TryCrateTouch(mobj_t *special, mobj_t *toucher); void Obj_TryCrateDamage(mobj_t *target, mobj_t *inflictor); +/* Lavender Shrine Spears */ +void Obj_SpearInit(mobj_t *mo); +void Obj_SpearThink(mobj_t *mo); + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/mobj.hpp b/src/mobj.hpp index da4973606..ce947b851 100644 --- a/src/mobj.hpp +++ b/src/mobj.hpp @@ -227,6 +227,21 @@ struct Mobj : mobj_t spryoff(v.y); } + void linkdraw(bool n) { flags2 = n ? flags2 | MF2_LINKDRAW : flags2 & ~MF2_LINKDRAW; } + + // WARNING: sets tracer! + void linkdraw(Mobj* parent) + { + tracer(parent); + linkdraw(true); + } + + void linkdraw(Mobj* parent, INT32 offset) + { + linkdraw(parent); + dispoffset = offset; + } + // TODO: Vec3 diff --git a/src/objects/CMakeLists.txt b/src/objects/CMakeLists.txt index 19cb75116..b7dda6caf 100644 --- a/src/objects/CMakeLists.txt +++ b/src/objects/CMakeLists.txt @@ -45,6 +45,7 @@ target_sources(SRB2SDL2 PRIVATE frost-thrower.cpp ivoball.cpp crate.cpp + spear.cpp ) add_subdirectory(versus) diff --git a/src/objects/spear.cpp b/src/objects/spear.cpp new file mode 100644 index 000000000..4998c1cdc --- /dev/null +++ b/src/objects/spear.cpp @@ -0,0 +1,179 @@ +// 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 Lat +// Hardcoded by jartha + +#include + +#include "../math/fixed.hpp" +#include "../math/vec.hpp" +#include "../mobj.hpp" +#include "../mobj_list_view.hpp" + +#include "../doomstat.h" +#include "../doomtype.h" +#include "../info.h" +#include "../k_objects.h" +#include "../tables.h" + +using srb2::math::Fixed; +using srb2::math::Vec2; +using srb2::Mobj; +using srb2::MobjListView; + +namespace +{ + +Vec2 angle_vector(angle_t x) +{ + return Vec2 {FCOS(x), FSIN(x)}; +} + +struct Spear : Mobj +{ + enum Mode + { + kWait, + kShake, + kPush, + kPull, + kNumModes, + }; + + static constexpr tic_t kWaitTimes[kNumModes] = { + TICRATE, + TICRATE/2, + TICRATE/2, + TICRATE, + }; + + void extravalue1() = delete; + int mode() const { return mobj_t::extravalue1; } + void mode(int n) + { + mobj_t::extravalue1 = n; + timer(kWaitTimes[n]); + } + + void extravalue2() = delete; + tic_t timer() const { return mobj_t::extravalue2; } + void timer(tic_t n) { mobj_t::extravalue2 = n; } + + void threshold() = delete; + Fixed dist() const { return mobj_t::threshold; } + void dist(Fixed n) { mobj_t::threshold = n; } + + void thing_args() = delete; + bool delayed_start() const { return mobj_t::thing_args[0]; } + + void init() + { + mode(kWait); + + if (delayed_start()) + { + timer(timer() + TICRATE*3/2); + } + + auto piece = [&](statenum_t state) + { + Mobj* vis = spawn_from({}, MT_SPEARVISUAL); + vis->state(state); + return vis; + }; + + Vec2 v = angle_vector(angle) * scale(); + auto divider = [&](statenum_t state, int offset) + { + Mobj* vis = piece(state); + vis->angle = angle - ANGLE_90; + vis->sproff2d(v * offset); + return vis; + }; + + Mobj* head = this; + auto link = [&](Mobj* vis) + { + vis->punt_ref(this); + head->hnext(vis); + head = vis; + return vis; + }; + + Mobj* wall = divider(S_SPEAR_WALL, 0); // never moves + + set_origin({pos2d() + (angle_vector(angle) * Fixed {radius}), z}); + + link(divider(S_SPEAR_HILT_BACK, 26)); + Mobj* front = link(divider(S_SPEAR_HILT_FRONT, 34)); + + Mobj* tip = piece(S_SPEAR_TIP); + tip->angle = angle; + link(tip); + + // Whether you use a positive or negative offset + // depends on how the sprite would originally be + // sorted... + this->linkdraw(wall, -5); // this sorts the rod behind the wall plate + tip->linkdraw(front, -5); // this sorts the tip IN FRONT of the rod + } + + void think() + { + Vec2 p = pos2d() - vector(); + dist(new_dist()); + Mobj::PosArg mpos{p + vector(), z}; + + move_origin(mpos); + for (Mobj* vis : MobjListView(hnext(), [](Mobj* vis) { return vis->hnext(); })) + { + vis->move_origin(mpos); + } + + timer(timer() - 1); + if (!timer()) + { + mode((mode() + 1) % kNumModes); + } + } + +private: + Fixed new_dist() const + { + static constexpr int kMinDist = -96; + static constexpr int kMaxDist = 0; + + switch (mode()) + { + default: + return kMinDist * scale(); + case kShake: + return (leveltime & 1 ? kMinDist : kMinDist + 4) * scale(); + case kPush: + return std::min(scale() * kMaxDist, dist() + (16 * scale())); + case kPull: + return std::max(scale() * kMinDist, dist() - (4 * scale())); + } + } + + Vec2 vector() const { return angle_vector(angle) * dist(); } +}; + +}; // namespace + +void Obj_SpearInit(mobj_t* mobj) +{ + static_cast(mobj)->init(); +} + +void Obj_SpearThink(mobj_t* mobj) +{ + static_cast(mobj)->think(); +} diff --git a/src/p_mobj.c b/src/p_mobj.c index 50bb15ac8..a21d676cb 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -6850,6 +6850,15 @@ static void P_MobjSceneryThink(mobj_t *mobj) Obj_BoxSideThink(mobj); return; } + case MT_SPEAR: + { + Obj_SpearThink(mobj); + return; + } + case MT_SPEARVISUAL: + { + return; + } case MT_VWREF: case MT_VWREB: { @@ -11020,6 +11029,8 @@ fixed_t P_GetMobjDefaultScale(mobj_t *mobj) case MT_HANAGUMIHALL_STEAM: case MT_HANAGUMIHALL_NPC: return 2*FRACUNIT; + case MT_SPEAR: + return 2*FRACUNIT; default: break; } @@ -14496,6 +14507,11 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj) Obj_TryCrateInit(mobj); break; } + case MT_SPEAR: + { + Obj_SpearInit(mobj); + break; + } default: break; } diff --git a/src/r_things.cpp b/src/r_things.cpp index a1187b803..9cb033f05 100644 --- a/src/r_things.cpp +++ b/src/r_things.cpp @@ -1656,8 +1656,6 @@ static void R_ProjectBoundingBox(mobj_t *thing, vissprite_t *vis) box->sortscale = vis->sortscale; // link sorting to sprite box->dispoffset = vis->dispoffset + 5; - - box->cut = static_cast(box->cut | SC_LINKDRAW); } else {