From 5cba9d63b3f644113bcfd4852b117a3a7d88ce3f Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 7 Dec 2023 19:19:02 -0800 Subject: [PATCH] Hardcode Lost Colony Fuel Canisters --- src/k_objects.h | 9 ++ src/objects/CMakeLists.txt | 1 + src/objects/fuel.cpp | 236 +++++++++++++++++++++++++++++++++++++ src/p_inter.c | 12 ++ src/p_mobj.c | 31 +++++ 5 files changed, 289 insertions(+) create mode 100644 src/objects/fuel.cpp diff --git a/src/k_objects.h b/src/k_objects.h index 215b4b84f..e3960513a 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -332,6 +332,15 @@ void Obj_TryCrateDamage(mobj_t *target, mobj_t *inflictor); void Obj_SpearInit(mobj_t *mo); void Obj_SpearThink(mobj_t *mo); +/* Lost Colony Fuel Canister */ +void Obj_FuelCanisterEmitterInit(mobj_t *mo); +boolean Obj_FuelCanisterVisualThink(mobj_t *mo); +boolean Obj_FuelCanisterEmitterThink(mobj_t *mo); +boolean Obj_FuelCanisterThink(mobj_t *mo); +void Obj_FuelCanisterTouch(mobj_t *special, mobj_t *toucher); +void Obj_FuelCanisterExplosionTouch(mobj_t *special, mobj_t *toucher); +boolean Obj_FuelCanisterExplosionThink(mobj_t *mo); + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/objects/CMakeLists.txt b/src/objects/CMakeLists.txt index 73a671802..3048568ea 100644 --- a/src/objects/CMakeLists.txt +++ b/src/objects/CMakeLists.txt @@ -46,6 +46,7 @@ target_sources(SRB2SDL2 PRIVATE ivoball.cpp crate.cpp spear.cpp + fuel.cpp ) add_subdirectory(versus) diff --git a/src/objects/fuel.cpp b/src/objects/fuel.cpp new file mode 100644 index 000000000..53e4ccfcd --- /dev/null +++ b/src/objects/fuel.cpp @@ -0,0 +1,236 @@ +// 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 "broly.hpp" +#include "objects.hpp" + +#include "../doomdef.h" +#include "../doomstat.h" +#include "../info.h" +#include "../k_objects.h" +#include "../sounds.h" +#include "../tables.h" + +using namespace srb2::objects; + +namespace +{ + +struct FuelCanister : Mobj +{ + struct Emitter : Mobj + { + void thing_args() = delete; + tic_t frequency() const { return mobj_t::thing_args[0]; } + tic_t initial_timer() const { return mobj_t::thing_args[1]; } + + void extravalue1() = delete; + tic_t timer() const { return mobj_t::extravalue1; } + void timer(tic_t n) { mobj_t::extravalue1 = n; } + + void init() + { + timer(initial_timer()); + } + + bool think() + { + if (timer() > 0) + { + timer(timer() - 1); + return true; + } + + timer(frequency()); + + FuelCanister::spawn(this); + + return true; + } + }; + + struct Vis : Mobj + { + void extravalue1() = delete; + angle_t phys_angle_ofs() const { return mobj_t::extravalue1; } + void phys_angle_ofs(angle_t n) { mobj_t::extravalue1 = n; } + + void extravalue2() = delete; + angle_t vis_angle_ofs() const { return mobj_t::extravalue2; } + void vis_angle_ofs(angle_t n) { mobj_t::extravalue2 = n; } + + bool valid() const { return Mobj::valid() && Mobj::valid(target()); } + + bool think() + { + if (!valid()) + { + remove(); + return false; + } + + const angle_t angleOutward = target()->angle + phys_angle_ofs(); + + move_origin({target()->pos2d() + (vector(angleOutward) * Fixed {radius}), target()->z}); + angle = angleOutward + vis_angle_ofs(); + + return true; + } + }; + + struct Explosion : Broly + { + static constexpr mobjtype_t kMobjType = MT_BETA_PARTICLE_EXPLOSION; + + static Explosion* spawn(Mobj* source) + { + Explosion* x = Broly::spawn(source, 3*TICRATE, {1, 8 * mapobjectscale}); + x->voice(sfx_lcfuel); + return x; + } + + void touch(Mobj* toucher) + { + if (!P_DamageMobj(toucher, this, this, 1, DMG_NORMAL)) + { + auto& hitlag = toucher->mobj_t::hitlag; + + // Hitlag = remaining duration of explosion + if (hitlag >= 0 && hitlag + 0u < remaining()) + { + hitlag = remaining(); + } + } + } + + bool think() { return Broly::think(); } + }; + + bool valid() const { return Mobj::valid() && momz; } + + static FuelCanister* spawn(Mobj* source) + { + FuelCanister* caps = source->spawn_from({}, MT_BETA_PARTICLE_PHYSICAL); + caps->init(); + return caps; + } + + void init() + { + momz = 8 * scale(); + z -= momz; + + pieces(); + pieces(); + } + + bool think() + { + if (!valid()) + { + remove(); + return false; + } + + angle += 8 * ANG1; + + return true; + } + + void touch(Mobj* toucher) + { + Explosion::spawn(toucher); + } + +private: + struct Wheel + { + static constexpr int kSides = 6; + static constexpr statenum_t kState = S_BETA_PARTICLE_WHEEL; + static constexpr int kRadius = 8; + static constexpr Fixed kScale = FRACUNIT; + static constexpr angle_t kAngleOffset = 0; + static constexpr int kZOffset = 0; + }; + + struct Icon + { + static constexpr int kSides = 2; + static constexpr statenum_t kState = S_BETA_PARTICLE_ICON; + static constexpr int kRadius = 8; + static constexpr Fixed kScale = 3*FRACUNIT/4; + static constexpr angle_t kAngleOffset = ANGLE_90; + static constexpr int kZOffset = 64; + }; + + static Vec2 vector(angle_t angle) { return {FCOS(angle), FSIN(angle)}; } + + template + void pieces() + { + constexpr angle_t kAngleBetween = ANGLE_MAX / Config::kSides; + + const Fixed zOfs = Config::kZOffset * (Fixed {FRACUNIT} / Config::kScale); + const Fixed radius = Config::kRadius * scale(); + const Fixed scale = Config::kScale * this->scale(); + + for (int i = 1; i <= Config::kSides; ++i) + { + angle_t angleOutward = i * kAngleBetween; + + Vis* vis = spawn_from({vector(angle + angleOutward) * radius, 0}, MT_BETA_PARTICLE_VISUAL); + + vis->state(Config::kState); + vis->target(this); + vis->scale(scale); + vis->radius = radius; + vis->spriteyoffset(zOfs); + + vis->phys_angle_ofs(angleOutward); + vis->vis_angle_ofs(Config::kAngleOffset); + } + } +}; + +}; // namespace + +void Obj_FuelCanisterEmitterInit(mobj_t *mo) +{ + static_cast(mo)->init(); +} + +boolean Obj_FuelCanisterVisualThink(mobj_t *mo) +{ + return static_cast(mo)->think(); +} + +boolean Obj_FuelCanisterEmitterThink(mobj_t *mo) +{ + return static_cast(mo)->think(); +} + +boolean Obj_FuelCanisterThink(mobj_t *mo) +{ + return static_cast(mo)->think(); +} + +void Obj_FuelCanisterTouch(mobj_t *special, mobj_t *toucher) +{ + static_cast(special)->touch(static_cast(toucher)); +} + +void Obj_FuelCanisterExplosionTouch(mobj_t *special, mobj_t *toucher) +{ + static_cast(special)->touch(static_cast(toucher)); +} + +boolean Obj_FuelCanisterExplosionThink(mobj_t *mo) +{ + return static_cast(mo)->think(); +} diff --git a/src/p_inter.c b/src/p_inter.c index 05fb78b3d..4c600ef0b 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -995,6 +995,18 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) return; } + case MT_BETA_PARTICLE_PHYSICAL: + { + Obj_FuelCanisterTouch(special, toucher); + break; + } + + case MT_BETA_PARTICLE_EXPLOSION: + { + Obj_FuelCanisterExplosionTouch(special, toucher); + return; + } + default: // SOC or script pickup P_SetTarget(&special->target, toucher); break; diff --git a/src/p_mobj.c b/src/p_mobj.c index 555b2da35..a24f35956 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -6859,6 +6859,24 @@ static void P_MobjSceneryThink(mobj_t *mobj) { return; } + case MT_BETA_PARTICLE_VISUAL: + { + Obj_FuelCanisterVisualThink(mobj); + return; + } + case MT_BETA_EMITTER: + { + Obj_FuelCanisterEmitterThink(mobj); + return; + } + case MT_BETA_PARTICLE_EXPLOSION: + { + if (Obj_FuelCanisterExplosionThink(mobj) == false) + { + return; + } + break; + } case MT_VWREF: case MT_VWREB: { @@ -10256,6 +10274,14 @@ static boolean P_MobjRegularThink(mobj_t *mobj) Obj_SidewaysFreezeThrusterThink(mobj); break; } + case MT_BETA_PARTICLE_PHYSICAL: + { + if (!Obj_FuelCanisterThink(mobj)) + { + return false; + } + break; + } default: // check mobj against possible water content, before movement code @@ -14514,6 +14540,11 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj) Obj_SpearInit(mobj); break; } + case MT_BETA_EMITTER: + { + Obj_FuelCanisterEmitterInit(mobj); + break; + } default: break; }