Merge branch 'hardcode-lost-colony' into 'master'

Hardcode Lost Colony Fuel Canisters

See merge request KartKrew/Kart!1681
This commit is contained in:
Oni 2023-12-08 04:45:59 +00:00
commit 49b09a3cf5
14 changed files with 582 additions and 80 deletions

View file

@ -4860,6 +4860,10 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi
// MT_BSZSLAMP
"S_BSWL",
"S_BSWC",
"S_BETA_PARTICLE_WHEEL",
"S_BETA_PARTICLE_ICON",
"S_BETA_PARTICLE_EXPLOSION",
};
// RegEx to generate this from info.h: ^\tMT_([^,]+), --> \t"MT_\1",
@ -6099,6 +6103,11 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t
"MT_BSZLAMP_L",
"MT_BSZSLAMP",
"MT_BSZSLCHA",
"MT_BETA_EMITTER",
"MT_BETA_PARTICLE_PHYSICAL",
"MT_BETA_PARTICLE_VISUAL",
"MT_BETA_PARTICLE_EXPLOSION",
};
const char *const MOBJFLAG_LIST[] = {

View file

@ -999,6 +999,8 @@ char sprnames[NUMSPRITES + 1][5] =
"BSWL",
"BSWC",
"LCLA",
// First person view sprites; this is a sprite so that it can be replaced by a specialized MD2 draw later
"VIEW",
};
@ -5719,6 +5721,10 @@ state_t states[NUMSTATES] =
// 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
{SPR_LCLA, 0|FF_FULLBRIGHT|FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_BETA_PARTICLE_WHEEL
{SPR_LCLA, 1|FF_FULLBRIGHT|FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_BETA_PARTICLE_ICON
{SPR_LCLA, 2|FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_BETA_PARTICLE_EXPLOSION
};
mobjinfo_t mobjinfo[NUMMOBJTYPES] =
@ -30674,8 +30680,8 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL, // xdeathstate
sfx_None, // deathsound
0, // speed
0, // radius
0, // height
40*FRACUNIT, // radius
80*FRACUNIT, // height
0, // display offset
100, // mass
1, // damage
@ -32529,6 +32535,110 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
0, // flags
S_NULL // raisestate
},
{ // MT_BETA_EMITTER
2699, // doomednum
S_INVISIBLE, // 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
0, // radius
0, // height
0, // dispoffset
0, // mass
0, // damage
sfx_None, // activesound
MF_NOGRAVITY|MF_NOBLOCKMAP|MF_NOSECTOR|MF_SCENERY, // flags
S_NULL // raisestate
},
{ // MT_BETA_PARTICLE_PHYSICAL
-1, // doomednum
S_INVISIBLE, // 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
24*FRACUNIT, // radius
128*FRACUNIT, // height
0, // dispoffset
0, // mass
0, // damage
sfx_None, // activesound
MF_SPECIAL|MF_NOGRAVITY, // flags
S_NULL // raisestate
},
{ // MT_BETA_PARTICLE_VISUAL
-1, // doomednum
S_NULL, // 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
0, // radius
0, // height
0, // dispoffset
0, // mass
0, // damage
sfx_None, // activesound
MF_NOCLIPTHING|MF_NOCLIPHEIGHT|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY, // flags
S_NULL // raisestate
},
{ // MT_BETA_PARTICLE_EXPLOSION
-1, // doomednum
S_BETA_PARTICLE_EXPLOSION, // spawnstate
1000, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
8, // 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
40*FRACUNIT, // radius
80*FRACUNIT, // height
0, // display offset
100, // mass
1, // damage
sfx_None, // activesound
MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP|MF_NOHITLAGFORME|MF_SPECIAL|MF_DONTPUNT, // flags
S_NULL // raisestate
},
};

View file

@ -1553,6 +1553,8 @@ typedef enum sprite
SPR_BSWL,
SPR_BSWC,
SPR_LCLA,
// First person view sprites; this is a sprite so that it can be replaced by a specialized MD2 draw later
SPR_VIEW,
@ -6144,6 +6146,10 @@ typedef enum state
S_BSWL,
S_BSWC,
S_BETA_PARTICLE_WHEEL,
S_BETA_PARTICLE_ICON,
S_BETA_PARTICLE_EXPLOSION,
S_FIRSTFREESLOT,
S_LASTFREESLOT = S_FIRSTFREESLOT + NUMSTATEFREESLOTS - 1,
NUMSTATES
@ -7403,6 +7409,11 @@ typedef enum mobj_type
MT_BSZSLAMP,
MT_BSZSLCHA,
MT_BETA_EMITTER,
MT_BETA_PARTICLE_PHYSICAL,
MT_BETA_PARTICLE_VISUAL,
MT_BETA_PARTICLE_EXPLOSION,
MT_FIRSTFREESLOT,
MT_LASTFREESLOT = MT_FIRSTFREESLOT + NUMMOBJFREESLOTS - 1,
NUMMOBJTYPES

View file

@ -330,6 +330,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

View file

@ -8,7 +8,7 @@ target_sources(SRB2SDL2 PRIVATE
orbinaut.c
jawz.c
duel-bomb.c
broly.c
broly.cpp
ufo.c
monitor.c
item-spot.c
@ -46,6 +46,7 @@ target_sources(SRB2SDL2 PRIVATE
ivoball.cpp
crate.cpp
spear.cpp
fuel.cpp
)
add_subdirectory(versus)

View file

@ -1,77 +0,0 @@
#include "../doomdef.h"
#include "../info.h"
#include "../k_kart.h"
#include "../k_objects.h"
#include "../m_easing.h"
#include "../p_local.h"
#include "../s_sound.h"
/* An object may not be visible on the same tic:
1) that it spawned
2) that it cycles to the next state */
#define BUFFER_TICS (2)
#define broly_duration(o) ((o)->extravalue1)
#define broly_maxscale(o) ((o)->extravalue2)
static inline fixed_t
get_unit_linear (const mobj_t *x)
{
const tic_t t = (x->tics - BUFFER_TICS);
return t * FRACUNIT / broly_duration(x);
}
mobj_t *
Obj_SpawnBrolyKi
( mobj_t * source,
tic_t duration)
{
mobj_t *x;
if (duration <= 0)
{
return NULL;
}
x = P_SpawnMobjFromMobj(
source, 0, 0, 0, MT_BROLY);
P_SetTarget(&x->target, source);
// Shrink into center of source object.
x->z = (source->z + source->height / 2);
x->colorized = true;
x->color = source->color;
x->hitlag = 0; // do not copy source hitlag
broly_maxscale(x) = 64 * mapobjectscale;
broly_duration(x) = duration;
x->tics = (duration + BUFFER_TICS);
K_ReduceVFXForEveryone(x);
S_StartSound(x, sfx_cdfm74);
return x;
}
boolean
Obj_BrolyKiThink (mobj_t *x)
{
if (broly_duration(x) <= 0)
{
P_RemoveMobj(x);
return false;
}
const fixed_t
t = get_unit_linear(x),
n = Easing_OutSine(t, 0, broly_maxscale(x));
P_InstaScale(x, n);
return true;
}

38
src/objects/broly.cpp Normal file
View file

@ -0,0 +1,38 @@
// 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 "../doomstat.h"
#include "../k_kart.h"
#include "../sounds.h"
using namespace srb2::objects;
mobj_t *
Obj_SpawnBrolyKi
( mobj_t * source,
tic_t duration)
{
Broly* x = Broly::spawn<Broly>(static_cast<Mobj*>(source), duration, {64 * mapobjectscale, 0});
x->colorized = true;
x->color = source->color;
K_ReduceVFXForEveryone(x);
x->voice(sfx_cdfm74);
return x;
}
boolean
Obj_BrolyKiThink (mobj_t *x)
{
return static_cast<Broly*>(x)->think();
}

96
src/objects/broly.hpp Normal file
View file

@ -0,0 +1,96 @@
// 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.
//-----------------------------------------------------------------------------
#ifndef objects_broly_hpp
#define objects_broly_hpp
#include <type_traits>
#include "objects.hpp"
#include "../info.h"
#include "../m_easing.h"
namespace srb2::objects
{
struct Broly : Mobj
{
static constexpr mobjtype_t kMobjType = MT_BROLY;
/* An object may not be visible on the same tic:
1) that it spawned
2) that it cycles to the next state */
static constexpr int kBufferTics = 2;
void extravalue1() = delete;
tic_t duration() const { return mobj_t::extravalue1; }
void duration(tic_t n) { mobj_t::extravalue1 = n; }
void threshold() = delete;
void extravalue2() = delete;
Vec2<Fixed> size() const { return {mobj_t::threshold, mobj_t::extravalue2}; }
void size(const Vec2<Fixed>& n)
{
mobj_t::threshold = n.x;
mobj_t::extravalue2 = n.y;
}
bool valid() const { return duration(); }
tic_t remaining() const { return tics - kBufferTics; }
Fixed linear() const { return (remaining() * FRACUNIT) / duration(); }
template <typename T>
static T* spawn(Mobj* source, tic_t duration, const Vec2<Fixed>& size)
{
static_assert(std::is_base_of_v<Broly, T>);
if (duration == 0)
{
return nullptr;
}
T* x = Mobj::spawn<T>(source->center(), T::kMobjType);
x->target(source);
// Shrink into center of source object.
x->z -= x->height / 2;
x->size(size);
x->duration(duration);
x->tics = (duration + kBufferTics);
return x;
}
bool think()
{
if (!valid())
{
remove();
return false;
}
const Fixed center = z + (height / 2);
const Vec2<Fixed> v = size();
scale(Easing_OutSine(linear(), v.y, v.x));
z = center - (height / 2);
return true;
}
};
}; // namespace srb2::objects
#endif/*objects_broly_hpp*/

236
src/objects/fuel.cpp Normal file
View file

@ -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<Explosion>(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<FuelCanister>({}, MT_BETA_PARTICLE_PHYSICAL);
caps->init();
return caps;
}
void init()
{
momz = 8 * scale();
z -= momz;
pieces<Wheel>();
pieces<Icon>();
}
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<Fixed> vector(angle_t angle) { return {FCOS(angle), FSIN(angle)}; }
template <class Config>
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<Vis>({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<FuelCanister::Emitter*>(mo)->init();
}
boolean Obj_FuelCanisterVisualThink(mobj_t *mo)
{
return static_cast<FuelCanister::Vis*>(mo)->think();
}
boolean Obj_FuelCanisterEmitterThink(mobj_t *mo)
{
return static_cast<FuelCanister::Emitter*>(mo)->think();
}
boolean Obj_FuelCanisterThink(mobj_t *mo)
{
return static_cast<FuelCanister*>(mo)->think();
}
void Obj_FuelCanisterTouch(mobj_t *special, mobj_t *toucher)
{
static_cast<FuelCanister*>(special)->touch(static_cast<Mobj*>(toucher));
}
void Obj_FuelCanisterExplosionTouch(mobj_t *special, mobj_t *toucher)
{
static_cast<FuelCanister::Explosion*>(special)->touch(static_cast<Mobj*>(toucher));
}
boolean Obj_FuelCanisterExplosionThink(mobj_t *mo)
{
return static_cast<FuelCanister::Explosion*>(mo)->think();
}

19
src/objects/objects.hpp Normal file
View file

@ -0,0 +1,19 @@
#ifndef objects_objects_hpp
#define objects_objects_hpp
#include "../math/fixed.hpp"
#include "../math/vec.hpp"
#include "../mobj.hpp"
#include "../k_objects.h"
namespace srb2::objects
{
using srb2::Mobj;
using srb2::math::Fixed;
using srb2::math::Vec2;
}; // namespace srb2::objects
#endif/*objects_objects_hpp*/

View file

@ -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;

View file

@ -6862,6 +6862,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:
{
@ -10259,6 +10277,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
@ -11034,6 +11060,8 @@ fixed_t P_GetMobjDefaultScale(mobj_t *mobj)
return 2*FRACUNIT;
case MT_SPEAR:
return 2*FRACUNIT;
case MT_BETA_EMITTER:
return 4*FRACUNIT;
default:
break;
}
@ -14515,6 +14543,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;
}

View file

@ -1251,6 +1251,8 @@ sfxinfo_t S_sfx[NUMSFX] =
{"ivobal", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // Ivo Ball
{"lcfuel", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Fuel Capsule explodes"},
// Damage sounds
{"dmga1", false, 255, 8, -1, NULL, 0, -1, -1, LUMPERROR, "Damaged"},
{"dmga2", false, 255, 8, -1, NULL, 0, -1, -1, LUMPERROR, "Damaged"},

View file

@ -1323,6 +1323,9 @@ typedef enum
// Ivo Ball
sfx_ivobal,
// Fuel Capsule
sfx_lcfuel,
// Damage sounds
sfx_dmga1,
sfx_dmga2,