mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2026-02-04 04:36:21 +00:00
Merge branch 'hardcode-crates' into 'master'
Hardcode SA2 Crates and Ice Cap Blocks (BIG performance: Mega Ice Cap, Frigid Cove) See merge request KartKrew/Kart!1663
This commit is contained in:
commit
ca2d64d769
13 changed files with 782 additions and 24 deletions
|
|
@ -4831,6 +4831,19 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi
|
|||
|
||||
// MT_IVOBALL
|
||||
"S_IVOBALL",
|
||||
|
||||
"S_SA2_CRATE_DEBRIS",
|
||||
"S_SA2_CRATE_DEBRIS_E",
|
||||
"S_SA2_CRATE_DEBRIS_F",
|
||||
"S_SA2_CRATE_DEBRIS_G",
|
||||
"S_SA2_CRATE_DEBRIS_H",
|
||||
"S_SA2_CRATE_DEBRIS_METAL",
|
||||
|
||||
"S_ICECAPBLOCK_DEBRIS",
|
||||
"S_ICECAPBLOCK_DEBRIS_C",
|
||||
"S_ICECAPBLOCK_DEBRIS_D",
|
||||
"S_ICECAPBLOCK_DEBRIS_E",
|
||||
"S_ICECAPBLOCK_DEBRIS_F",
|
||||
};
|
||||
|
||||
// RegEx to generate this from info.h: ^\tMT_([^,]+), --> \t"MT_\1",
|
||||
|
|
@ -6057,6 +6070,11 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t
|
|||
"MT_IVOBALL",
|
||||
"MT_PATROLIVOBALL",
|
||||
"MT_AIRIVOBALL",
|
||||
|
||||
"MT_BOX_SIDE",
|
||||
"MT_BOX_DEBRIS",
|
||||
"MT_SA2_CRATE",
|
||||
"MT_ICECAPBLOCK",
|
||||
};
|
||||
|
||||
const char *const MOBJFLAG_LIST[] = {
|
||||
|
|
|
|||
120
src/info.c
120
src/info.c
|
|
@ -986,6 +986,9 @@ char sprnames[NUMSPRITES + 1][5] =
|
|||
"FBTN",
|
||||
"SFTR",
|
||||
|
||||
"SABX",
|
||||
"ICBL",
|
||||
|
||||
// First person view sprites; this is a sprite so that it can be replaced by a specialized MD2 draw later
|
||||
"VIEW",
|
||||
};
|
||||
|
|
@ -5677,6 +5680,19 @@ state_t states[NUMSTATES] =
|
|||
|
||||
// MT_IVOBALL
|
||||
{SPR_BSPH, 2|FF_SEMIBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_IVOBALL
|
||||
|
||||
{SPR_UNKN, FF_FULLBRIGHT, -1, {A_RandomStateRange}, S_SA2_CRATE_DEBRIS_E, S_SA2_CRATE_DEBRIS_H, S_NULL}, // S_SA2_CRATE_DEBRIS
|
||||
{SPR_SABX, 4, 70, {NULL}, 0, 0, S_NULL}, // S_SA2_CRATE_DEBRIS_E
|
||||
{SPR_SABX, 5, 70, {NULL}, 0, 0, S_NULL}, // S_SA2_CRATE_DEBRIS_F
|
||||
{SPR_SABX, 6, 70, {NULL}, 0, 0, S_NULL}, // S_SA2_CRATE_DEBRIS_G
|
||||
{SPR_SABX, 7, 70, {NULL}, 0, 0, S_NULL}, // S_SA2_CRATE_DEBRIS_H
|
||||
{SPR_SABX, 12, 70, {NULL}, 0, 0, S_NULL}, // S_SA2_CRATE_DEBRIS_METAL
|
||||
|
||||
{SPR_UNKN, FF_FULLBRIGHT, -1, {A_RandomStateRange}, S_ICECAPBLOCK_DEBRIS_C, S_ICECAPBLOCK_DEBRIS_F, S_NULL}, // S_ICECAPBLOCK_DEBRIS
|
||||
{SPR_ICBL, 2, 70, {NULL}, 0, 0, S_NULL}, // S_ICECAPBLOCK_DEBRIS_C
|
||||
{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
|
||||
};
|
||||
|
||||
mobjinfo_t mobjinfo[NUMMOBJTYPES] =
|
||||
|
|
@ -32200,6 +32216,110 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
|
|||
MF_SCENERY|MF_SPECIAL|MF_NOGRAVITY, // flags
|
||||
S_NULL // raisestate
|
||||
},
|
||||
{ // MT_BOX_SIDE
|
||||
-1, // doomednum
|
||||
S_INVISIBLE, // 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
|
||||
40*FRACUNIT, // radius
|
||||
80*FRACUNIT, // height
|
||||
0, // dispoffset
|
||||
0, // mass
|
||||
0, // damage
|
||||
sfx_None, // activesound
|
||||
MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_SCENERY|MF_NOCLIPTHING, // flags
|
||||
S_NULL // raisestate
|
||||
},
|
||||
{ // MT_BOX_DEBRIS
|
||||
-1, // doomednum
|
||||
S_INVISIBLE, // 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
|
||||
0*FRACUNIT, // radius
|
||||
0*FRACUNIT, // height
|
||||
0, // dispoffset
|
||||
0, // mass
|
||||
0, // damage
|
||||
sfx_None, // activesound
|
||||
MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_SCENERY|MF_NOCLIPTHING, // flags
|
||||
S_NULL // raisestate
|
||||
},
|
||||
{ // MT_SA2_CRATE
|
||||
2529, // 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
|
||||
40*FRACUNIT, // radius
|
||||
80*FRACUNIT, // height
|
||||
0, // dispoffset
|
||||
0, // mass
|
||||
0, // damage
|
||||
sfx_None, // activesound
|
||||
MF_SPECIAL|MF_SOLID|MF_SHOOTABLE|MF_SCENERY|MF_DONTPUNT, // flags
|
||||
S_NULL // raisestate
|
||||
},
|
||||
{ // MT_ICECAPBLOCK
|
||||
3750, // 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
|
||||
40*FRACUNIT, // radius
|
||||
80*FRACUNIT, // height
|
||||
0, // dispoffset
|
||||
0, // mass
|
||||
0, // damage
|
||||
sfx_None, // activesound
|
||||
MF_SPECIAL|MF_SOLID|MF_SHOOTABLE|MF_SCENERY|MF_DONTPUNT, // flags
|
||||
S_NULL // raisestate
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
21
src/info.h
21
src/info.h
|
|
@ -1540,6 +1540,9 @@ typedef enum sprite
|
|||
SPR_FBTN,
|
||||
SPR_SFTR,
|
||||
|
||||
SPR_SABX,
|
||||
SPR_ICBL,
|
||||
|
||||
// First person view sprites; this is a sprite so that it can be replaced by a specialized MD2 draw later
|
||||
SPR_VIEW,
|
||||
|
||||
|
|
@ -6102,6 +6105,19 @@ typedef enum state
|
|||
// MT_IVOBALL
|
||||
S_IVOBALL,
|
||||
|
||||
S_SA2_CRATE_DEBRIS,
|
||||
S_SA2_CRATE_DEBRIS_E,
|
||||
S_SA2_CRATE_DEBRIS_F,
|
||||
S_SA2_CRATE_DEBRIS_G,
|
||||
S_SA2_CRATE_DEBRIS_H,
|
||||
S_SA2_CRATE_DEBRIS_METAL,
|
||||
|
||||
S_ICECAPBLOCK_DEBRIS,
|
||||
S_ICECAPBLOCK_DEBRIS_C,
|
||||
S_ICECAPBLOCK_DEBRIS_D,
|
||||
S_ICECAPBLOCK_DEBRIS_E,
|
||||
S_ICECAPBLOCK_DEBRIS_F,
|
||||
|
||||
S_FIRSTFREESLOT,
|
||||
S_LASTFREESLOT = S_FIRSTFREESLOT + NUMSTATEFREESLOTS - 1,
|
||||
NUMSTATES
|
||||
|
|
@ -7348,6 +7364,11 @@ typedef enum mobj_type
|
|||
MT_PATROLIVOBALL,
|
||||
MT_AIRIVOBALL,
|
||||
|
||||
MT_BOX_SIDE,
|
||||
MT_BOX_DEBRIS,
|
||||
MT_SA2_CRATE,
|
||||
MT_ICECAPBLOCK,
|
||||
|
||||
MT_FIRSTFREESLOT,
|
||||
MT_LASTFREESLOT = MT_FIRSTFREESLOT + NUMMOBJFREESLOTS - 1,
|
||||
NUMMOBJTYPES
|
||||
|
|
|
|||
|
|
@ -321,6 +321,13 @@ void Obj_PatrolIvoBallInit(mobj_t *mo);
|
|||
void Obj_PatrolIvoBallThink(mobj_t *mo);
|
||||
void Obj_PatrolIvoBallTouch(mobj_t *special, mobj_t *toucher);
|
||||
|
||||
/* SA2 Crates / Ice Cap Blocks */
|
||||
void Obj_BoxSideThink(mobj_t *mo);
|
||||
void Obj_TryCrateInit(mobj_t *mo);
|
||||
boolean Obj_TryCrateThink(mobj_t *mo);
|
||||
void Obj_TryCrateTouch(mobj_t *special, mobj_t *toucher);
|
||||
void Obj_TryCrateDamage(mobj_t *target, mobj_t *inflictor);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -22,9 +22,9 @@ struct Vec2
|
|||
{
|
||||
T x, y;
|
||||
|
||||
Vec2() : x{}, y{} {}
|
||||
Vec2(T x_, T y_) : x(x_), y(y_) {}
|
||||
Vec2(T z) : x(z), y(z) {}
|
||||
constexpr Vec2() : x{}, y{} {}
|
||||
constexpr Vec2(T x_, T y_) : x(x_), y(y_) {}
|
||||
constexpr Vec2(T z) : x(z), y(z) {}
|
||||
|
||||
template <typename U>
|
||||
Vec2(const Vec2<U>& b) : Vec2(b.x, b.y) {}
|
||||
|
|
|
|||
95
src/mobj.hpp
95
src/mobj.hpp
|
|
@ -13,8 +13,12 @@
|
|||
#include <optional>
|
||||
|
||||
#include "math/fixed.hpp"
|
||||
#include "math/line_segment.hpp"
|
||||
#include "math/vec.hpp"
|
||||
|
||||
#include "doomtype.h"
|
||||
#include "k_hitlag.h"
|
||||
#include "k_kart.h"
|
||||
#include "info.h"
|
||||
#include "p_local.h"
|
||||
#include "p_mobj.h"
|
||||
|
|
@ -27,16 +31,20 @@ namespace srb2
|
|||
|
||||
struct Mobj : mobj_t
|
||||
{
|
||||
using fixed = math::Fixed;
|
||||
using line_segment = math::LineSegment<fixed>;
|
||||
using vec2 = math::Vec2<fixed>;
|
||||
|
||||
// TODO: Vec3 would be nice
|
||||
struct PosArg
|
||||
{
|
||||
math::Fixed x, y, z;
|
||||
fixed x, y, z;
|
||||
|
||||
PosArg() : PosArg(0, 0, 0) {}
|
||||
PosArg(fixed_t x_, fixed_t y_, fixed_t z_) : x(x_), y(y_), z(z_) {}
|
||||
PosArg(fixed x_, fixed y_, fixed z_) : x(x_), y(y_), z(z_) {}
|
||||
|
||||
template <typename T>
|
||||
PosArg(math::Vec2<T> p, fixed_t z) : PosArg(p.x, p.y, z) {}
|
||||
PosArg(math::Vec2<T> p, fixed z) : PosArg(p.x, p.y, z) {}
|
||||
|
||||
PosArg(const mobj_t* mobj) : PosArg(mobj->x, mobj->y, mobj->z) {}
|
||||
};
|
||||
|
|
@ -97,19 +105,26 @@ struct Mobj : mobj_t
|
|||
|
||||
PosArg center() const { return {x, y, z + (height / 2)}; }
|
||||
PosArg pos() const { return {x, y, z}; }
|
||||
math::Vec2<math::Fixed> pos2d() const { return {x, y}; }
|
||||
math::Fixed top() const { return z + height; }
|
||||
vec2 pos2d() const { return {x, y}; }
|
||||
fixed top() const { return z + height; }
|
||||
|
||||
bool is_flipped() const { return eflags & MFE_VERTICALFLIP; }
|
||||
math::Fixed flip(fixed_t x) const { return x * P_MobjFlip(this); }
|
||||
fixed flip(fixed x) const { return x * P_MobjFlip(this); }
|
||||
|
||||
// Collision helper
|
||||
bool z_overlaps(const Mobj* b) const { return z < b->top() && b->z < top(); }
|
||||
|
||||
void move_origin(const PosArg& p) { P_MoveOrigin(this, p.x, p.y, p.z); }
|
||||
void set_origin(const PosArg& p) { P_SetOrigin(this, p.x, p.y, p.z); }
|
||||
void instathrust(angle_t angle, fixed_t speed) { P_InstaThrust(this, angle, speed); }
|
||||
void thrust(angle_t angle, fixed_t speed) { P_Thrust(this, angle, speed); }
|
||||
void instathrust(angle_t angle, fixed speed) { P_InstaThrust(this, angle, speed); }
|
||||
void thrust(angle_t angle, fixed speed) { P_Thrust(this, angle, speed); }
|
||||
|
||||
static void bounce(Mobj* t1, Mobj* t2) { K_KartBouncing(t1, t2); }
|
||||
void solid_bounce(Mobj* solid) { K_KartSolidBounce(this, solid); }
|
||||
|
||||
// A = bottom left corner
|
||||
// this->aabb; the standard bounding box. This is inapproporiate for paper collision!
|
||||
line_segment aabb() const { return {{x - radius, y - radius}, {x + radius, y + radius}}; }
|
||||
|
||||
|
||||
//
|
||||
|
|
@ -150,15 +165,15 @@ struct Mobj : mobj_t
|
|||
// Scale
|
||||
//
|
||||
|
||||
math::Fixed scale() const { return mobj_t::scale; }
|
||||
fixed scale() const { return mobj_t::scale; }
|
||||
|
||||
void scale(fixed_t n)
|
||||
void scale(fixed n)
|
||||
{
|
||||
mobj_t::scale = n;
|
||||
P_SetScale(this, n);
|
||||
mobj_t::destscale = n;
|
||||
}
|
||||
|
||||
void scale_to(fixed_t stop, std::optional<fixed_t> speed = {})
|
||||
void scale_to(fixed stop, std::optional<fixed> speed = {})
|
||||
{
|
||||
mobj_t::destscale = stop;
|
||||
|
||||
|
|
@ -168,13 +183,53 @@ struct Mobj : mobj_t
|
|||
}
|
||||
}
|
||||
|
||||
void scale_between(fixed_t start, fixed_t stop, std::optional<fixed_t> speed = {})
|
||||
void scale_between(fixed start, fixed stop, std::optional<fixed> speed = {})
|
||||
{
|
||||
mobj_t::scale = start;
|
||||
P_SetScale(this, start);
|
||||
scale_to(stop, speed);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Sprite offsets
|
||||
//
|
||||
|
||||
#define FIXED_METHOD(member) \
|
||||
fixed member() const { return mobj_t::member; } \
|
||||
void member(fixed n) { mobj_t::member = n; }
|
||||
|
||||
FIXED_METHOD(spritexscale)
|
||||
FIXED_METHOD(spriteyscale)
|
||||
FIXED_METHOD(spritexoffset)
|
||||
FIXED_METHOD(spriteyoffset)
|
||||
FIXED_METHOD(sprxoff)
|
||||
FIXED_METHOD(spryoff)
|
||||
FIXED_METHOD(sprzoff)
|
||||
|
||||
vec2 spritescale() const { return {spritexscale(), spriteyscale()}; }
|
||||
void spritescale(const vec2& v)
|
||||
{
|
||||
spritexscale(v.x);
|
||||
spriteyscale(v.y);
|
||||
}
|
||||
|
||||
vec2 spriteoffset() const { return {spritexoffset(), spriteyoffset()}; }
|
||||
void spriteoffset(const vec2& v)
|
||||
{
|
||||
spritexoffset(v.x);
|
||||
spriteyoffset(v.y);
|
||||
}
|
||||
|
||||
vec2 sproff2d() const { return {sprxoff(), spryoff()}; }
|
||||
void sproff2d(const vec2& v)
|
||||
{
|
||||
sprxoff(v.x);
|
||||
spryoff(v.y);
|
||||
}
|
||||
|
||||
// TODO: Vec3
|
||||
|
||||
|
||||
//
|
||||
// Sound
|
||||
//
|
||||
|
|
@ -194,6 +249,18 @@ struct Mobj : mobj_t
|
|||
voice(sfx, volume);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Hitlag
|
||||
//
|
||||
|
||||
INT32 hitlag() const { return mobj_t::hitlag; }
|
||||
void hitlag(INT32 tics, bool damage = false) { K_AddHitLag(this, tics, damage); }
|
||||
void hitlag(Mobj* inflictor, Mobj* source, INT32 tics, bool damage)
|
||||
{
|
||||
K_SetHitLagForObjects(this, inflictor, source, tics, damage);
|
||||
}
|
||||
};
|
||||
|
||||
}; // namespace srb2
|
||||
|
|
|
|||
74
src/mobj_list_view.hpp
Normal file
74
src/mobj_list_view.hpp
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
// 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 mobj_list_view_hpp
|
||||
#define mobj_list_view_hpp
|
||||
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
|
||||
#include "mobj.hpp"
|
||||
|
||||
namespace srb2
|
||||
{
|
||||
|
||||
// for (T* ptr : MobjList(hnext(), [](T* ptr) { return ptr->hnext(); }))
|
||||
template <typename T, typename F, std::enable_if_t<std::is_convertible_v<T, Mobj>, bool> = true>
|
||||
struct MobjListView
|
||||
{
|
||||
struct Iterator
|
||||
{
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using value_type = T*;
|
||||
using pointer = value_type;
|
||||
using reference = value_type;
|
||||
|
||||
Iterator(pointer ptr, F adv) : ptr_(deref(ptr)), adv_(adv) {}
|
||||
|
||||
bool operator==(const Iterator& b) const { return ptr_ == b.ptr_; };
|
||||
bool operator!=(const Iterator& b) const { return ptr_ != b.ptr_; };
|
||||
|
||||
reference operator*() const { return ptr_; }
|
||||
pointer operator->() { return &ptr_; }
|
||||
|
||||
Iterator& operator++()
|
||||
{
|
||||
ptr_ = deref(adv_(ptr_));
|
||||
return *this;
|
||||
}
|
||||
|
||||
Iterator operator++(int)
|
||||
{
|
||||
Iterator prev = *this;
|
||||
++(*this);
|
||||
return prev;
|
||||
}
|
||||
|
||||
private:
|
||||
pointer ptr_;
|
||||
F adv_;
|
||||
|
||||
static T* deref(T* ptr) { return ptr && ptr->valid() ? ptr : nullptr; }
|
||||
};
|
||||
|
||||
MobjListView(T* ptr, F adv) : ptr_(ptr), adv_(adv) {}
|
||||
|
||||
Iterator begin() const { return {ptr_, adv_}; }
|
||||
Iterator end() const { return {nullptr, adv_}; }
|
||||
|
||||
private:
|
||||
T* ptr_;
|
||||
F adv_;
|
||||
};
|
||||
|
||||
}; // namespace srb2
|
||||
|
||||
#endif/*mobj_list_view_hpp*/
|
||||
|
|
@ -44,6 +44,7 @@ target_sources(SRB2SDL2 PRIVATE
|
|||
mega-barrier.cpp
|
||||
frost-thrower.cpp
|
||||
ivoball.cpp
|
||||
crate.cpp
|
||||
)
|
||||
|
||||
add_subdirectory(versus)
|
||||
|
|
|
|||
391
src/objects/crate.cpp
Normal file
391
src/objects/crate.cpp
Normal file
|
|
@ -0,0 +1,391 @@
|
|||
// 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 <algorithm>
|
||||
#include <array>
|
||||
#include <tuple>
|
||||
|
||||
#include "../math/fixed.hpp"
|
||||
#include "../math/line_segment.hpp"
|
||||
#include "../math/vec.hpp"
|
||||
#include "../mobj.hpp"
|
||||
#include "../mobj_list_view.hpp"
|
||||
|
||||
#include "../d_player.h"
|
||||
#include "../doomtype.h"
|
||||
#include "../info.h"
|
||||
#include "../k_kart.h"
|
||||
#include "../k_objects.h"
|
||||
#include "../m_random.h"
|
||||
#include "../p_local.h"
|
||||
#include "../p_maputl.h"
|
||||
#include "../p_pspr.h"
|
||||
#include "../r_defs.h"
|
||||
|
||||
using srb2::math::Fixed;
|
||||
using srb2::math::LineSegment;
|
||||
using srb2::math::Vec2;
|
||||
using srb2::Mobj;
|
||||
using srb2::MobjListView;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
using frame_layout = std::array<int, 6>; // -z, +z, -x, -y, +x, +y
|
||||
|
||||
struct SA2CrateConfig
|
||||
{
|
||||
static constexpr spritenum_t kSprite = SPR_SABX;
|
||||
static constexpr frame_layout kFrames = {3, 2, 0, 0, 0, 0};
|
||||
static constexpr statenum_t kDefaultDebris = S_SA2_CRATE_DEBRIS;
|
||||
};
|
||||
|
||||
struct IceCapBlockConfig
|
||||
{
|
||||
static constexpr spritenum_t kSprite = SPR_ICBL;
|
||||
static constexpr frame_layout kFrames = {6, 6, 0, 0, 0, 0};
|
||||
static constexpr statenum_t kDefaultDebris = S_ICECAPBLOCK_DEBRIS;
|
||||
};
|
||||
|
||||
struct Graphic : Mobj
|
||||
{
|
||||
void hnext() = delete;
|
||||
Graphic* next() const { return Mobj::hnext<Graphic>(); }
|
||||
void next(Graphic* n) { Mobj::hnext(n); }
|
||||
|
||||
Graphic* dress(spritenum_t sprite, UINT32 frame)
|
||||
{
|
||||
this->sprite = sprite;
|
||||
this->frame = frame;
|
||||
this->renderflags |= RF_NOSPLATBILLBOARD;
|
||||
return this;
|
||||
}
|
||||
|
||||
Graphic* xy(fixed_t x, fixed_t y)
|
||||
{
|
||||
this->sproff2d({x, y});
|
||||
return this;
|
||||
}
|
||||
|
||||
Graphic* z(fixed_t z)
|
||||
{
|
||||
this->sprzoff(z);
|
||||
return this;
|
||||
}
|
||||
|
||||
Graphic* turn(angle_t angle)
|
||||
{
|
||||
this->angle = angle;
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
struct Side : Graphic
|
||||
{
|
||||
bool valid() const { return Mobj::valid() && Mobj::valid(owner()); }
|
||||
|
||||
void think()
|
||||
{
|
||||
if (!valid())
|
||||
{
|
||||
remove();
|
||||
return;
|
||||
}
|
||||
|
||||
move_origin(owner());
|
||||
renderflags = owner()->renderflags;
|
||||
}
|
||||
};
|
||||
|
||||
struct Toucher : Mobj
|
||||
{
|
||||
bool boosting() const { return player && (player->sneakertimer || K_PlayerCanPunt(player)); }
|
||||
};
|
||||
|
||||
struct AnyBox : Graphic
|
||||
{
|
||||
template <typename F>
|
||||
bool visit(F&& visitor);
|
||||
|
||||
void update()
|
||||
{
|
||||
visit([](auto box) { box->mobj_t::eflags &= ~MFE_ONGROUND; });
|
||||
}
|
||||
};
|
||||
|
||||
template <class Config>
|
||||
struct Box : AnyBox
|
||||
{
|
||||
static constexpr Fixed kIntendedSize = 128*FRACUNIT;
|
||||
static constexpr Vec2<Fixed> kScrunch = {4*FRACUNIT/5, 6*FRACUNIT/5};
|
||||
|
||||
void extravalue1() = delete;
|
||||
statenum_t debris_state() const { return static_cast<statenum_t>(mobj_t::extravalue1); }
|
||||
void debris_state(statenum_t n) { mobj_t::extravalue1 = n; }
|
||||
|
||||
auto gfx() { return MobjListView(static_cast<Graphic*>(this), [](Graphic* g) { return g->next(); }); }
|
||||
|
||||
void init()
|
||||
{
|
||||
scale(scale() * (kIntendedSize / Fixed {info->height}));
|
||||
|
||||
Graphic* node = this;
|
||||
int i = 0;
|
||||
auto dress = [&](Graphic* g, UINT32 ff) { return g->dress(Config::kSprite, Config::kFrames[i++] | ff); };
|
||||
auto side = [&](UINT32 ff)
|
||||
{
|
||||
Side* side = spawn_from<Side>({}, MT_BOX_SIDE);
|
||||
side->owner(this);
|
||||
node->next(side); // link
|
||||
node = side;
|
||||
return dress(side, ff);
|
||||
};
|
||||
|
||||
dress(this, FF_FLOORSPRITE); // bottom (me)
|
||||
side(FF_FLOORSPRITE)->z(height); // top
|
||||
|
||||
// sides
|
||||
side(FF_PAPERSPRITE)->xy(-radius, 0)->turn(ANGLE_270);
|
||||
side(FF_PAPERSPRITE)->xy(0, -radius);
|
||||
side(FF_PAPERSPRITE)->xy(+radius, 0)->turn(ANGLE_90);
|
||||
side(FF_PAPERSPRITE)->xy(0, +radius)->turn(ANGLE_180);
|
||||
|
||||
debris_state(Config::kDefaultDebris);
|
||||
}
|
||||
|
||||
bool think()
|
||||
{
|
||||
if (fuse)
|
||||
{
|
||||
fuse--;
|
||||
renderflags ^= RF_DONTDRAW;
|
||||
|
||||
if (!fuse)
|
||||
{
|
||||
update_nearby();
|
||||
remove();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void touch(Toucher* toucher)
|
||||
{
|
||||
if (fuse)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
P_DamageMobj(this, toucher, nullptr, 1, DMG_NORMAL);
|
||||
|
||||
if (!toucher->boosting())
|
||||
{
|
||||
toucher->solid_bounce(this);
|
||||
}
|
||||
}
|
||||
|
||||
bool damage_valid(const Mobj* inflictor) const { return !fuse && Mobj::valid(inflictor); }
|
||||
|
||||
void damage(Mobj* inflictor)
|
||||
{
|
||||
if (!damage_valid(inflictor))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
inflictor->hitlag(3);
|
||||
fuse = 10;
|
||||
|
||||
// scrunch crate sides
|
||||
for (Graphic* g : gfx())
|
||||
{
|
||||
if (g->frame & FF_PAPERSPRITE)
|
||||
{
|
||||
g->frame++;
|
||||
g->spritescale(kScrunch);
|
||||
}
|
||||
else
|
||||
{
|
||||
g->spritescale(kScrunch.x);
|
||||
}
|
||||
|
||||
// reset interp
|
||||
g->mobj_t::old_spritexscale = g->spritexscale();
|
||||
g->mobj_t::old_spriteyscale = g->spriteyscale();
|
||||
|
||||
g->sproff2d(g->sproff2d() * kScrunch.x);
|
||||
g->sprzoff(g->sprzoff() * kScrunch.y);
|
||||
}
|
||||
|
||||
debris(inflictor);
|
||||
update_nearby();
|
||||
}
|
||||
|
||||
private:
|
||||
void debris(Mobj* inflictor)
|
||||
{
|
||||
if (debris_state() >= NUMSTATES)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto rng = [&](int x, int y) { return P_RandomRange(PR_DECORATION, x, y) * scale(); };
|
||||
auto rng_xyz = [&](int x) { return std::tuple(rng(-x, x), rng(-x, x), rng(0, x)); };
|
||||
|
||||
auto spawn = [&]
|
||||
{
|
||||
auto [x, y, z] = rng_xyz(info->height / FRACUNIT);
|
||||
Mobj* p = spawn_from<Mobj>({x, y, z}, MT_BOX_DEBRIS);
|
||||
|
||||
p->scale_between(scale() / 2, scale());
|
||||
p->state(debris_state());
|
||||
|
||||
std::tie(x, y, z) = rng_xyz(4);
|
||||
|
||||
p->momx = (inflictor->momx / 8) + x;
|
||||
p->momy = (inflictor->momy / 8) + y;
|
||||
p->momz = (Fixed::hypot(inflictor->momx, inflictor->momy) / 4) + z;
|
||||
};
|
||||
|
||||
spawn();
|
||||
spawn();
|
||||
spawn();
|
||||
spawn();
|
||||
spawn();
|
||||
spawn();
|
||||
}
|
||||
|
||||
void update_nearby() const
|
||||
{
|
||||
LineSegment<Fixed> search = aabb();
|
||||
Vec2<Fixed> org{bmaporgx, bmaporgy};
|
||||
|
||||
search.a -= org + MAXRADIUS;
|
||||
search.b -= org - MAXRADIUS;
|
||||
|
||||
search.a.x = static_cast<UINT32>(search.a.x) >> MAPBLOCKSHIFT;
|
||||
search.b.x = static_cast<UINT32>(search.b.x) >> MAPBLOCKSHIFT;
|
||||
search.a.y = static_cast<UINT32>(search.a.y) >> MAPBLOCKSHIFT;
|
||||
search.b.y = static_cast<UINT32>(search.b.y) >> MAPBLOCKSHIFT;
|
||||
|
||||
BMBOUNDFIX(search.a.x, search.b.x, search.b.x, search.b.y);
|
||||
|
||||
for (INT32 bx = search.a.x; bx <= search.b.x; ++bx)
|
||||
{
|
||||
for (INT32 by = search.a.y; by <= search.b.y; ++by)
|
||||
{
|
||||
P_BlockThingsIterator(
|
||||
bx,
|
||||
by,
|
||||
[](mobj_t* thing)
|
||||
{
|
||||
static_cast<AnyBox*>(thing)->update();
|
||||
return BMIT_CONTINUE;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct Crate : Box<SA2CrateConfig>
|
||||
{
|
||||
static constexpr int kMetalFrameStart = 8;
|
||||
|
||||
void thing_args() = delete;
|
||||
bool metal() const { return mobj_t::thing_args[0]; }
|
||||
|
||||
void init()
|
||||
{
|
||||
Box::init();
|
||||
|
||||
if (metal())
|
||||
{
|
||||
for (Graphic* g : gfx())
|
||||
{
|
||||
g->frame += kMetalFrameStart;
|
||||
}
|
||||
|
||||
debris_state(S_SA2_CRATE_DEBRIS_METAL);
|
||||
}
|
||||
}
|
||||
|
||||
void damage(Toucher* inflictor)
|
||||
{
|
||||
if (!Box::damage_valid(inflictor))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (metal() && !inflictor->boosting())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Box::damage(inflictor);
|
||||
}
|
||||
};
|
||||
|
||||
struct Ice : Box<IceCapBlockConfig>
|
||||
{
|
||||
};
|
||||
|
||||
template <typename F>
|
||||
bool AnyBox::visit(F&& visitor)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case MT_SA2_CRATE:
|
||||
visitor(static_cast<Crate*>(this));
|
||||
break;
|
||||
|
||||
case MT_ICECAPBLOCK:
|
||||
visitor(static_cast<Ice*>(this));
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}; // namespace
|
||||
|
||||
void Obj_BoxSideThink(mobj_t* mobj)
|
||||
{
|
||||
static_cast<Side*>(mobj)->think();
|
||||
}
|
||||
|
||||
void Obj_TryCrateInit(mobj_t* mobj)
|
||||
{
|
||||
static_cast<AnyBox*>(mobj)->visit([&](auto box) { box->init(); });
|
||||
}
|
||||
|
||||
boolean Obj_TryCrateThink(mobj_t* mobj)
|
||||
{
|
||||
bool c = false;
|
||||
static_cast<AnyBox*>(mobj)->visit([&](auto box) { c = box->think(); });
|
||||
return c;
|
||||
}
|
||||
|
||||
void Obj_TryCrateTouch(mobj_t* special, mobj_t* toucher)
|
||||
{
|
||||
static_cast<AnyBox*>(special)->visit([&](auto box) { box->touch(static_cast<Toucher*>(toucher)); });
|
||||
}
|
||||
|
||||
void Obj_TryCrateDamage(mobj_t* target, mobj_t* inflictor)
|
||||
{
|
||||
static_cast<AnyBox*>(target)->visit([&](auto box) { box->damage(static_cast<Toucher*>(inflictor)); });
|
||||
}
|
||||
|
|
@ -64,7 +64,7 @@ struct IvoBall : Mobj
|
|||
Fixed wave{(x / mapobjectscale) + (y / mapobjectscale)};
|
||||
offset(wave / kRippleFactor);
|
||||
color = SKINCOLOR_TANGERINE;
|
||||
sprzoff = kFloat * mapobjectscale;
|
||||
sprzoff(kFloat * mapobjectscale);
|
||||
}
|
||||
|
||||
void think()
|
||||
|
|
@ -81,7 +81,7 @@ struct IvoBall : Mobj
|
|||
|
||||
fixed_t ballTimer = leveltime + offset();
|
||||
Fixed bob = kBobHeight * Fixed {FSIN((M_TAU_FIXED * kBobTime) * ballTimer)};
|
||||
spriteyoffset = bob;
|
||||
spriteyoffset(bob);
|
||||
|
||||
colorized = !((ballTimer / kFlashTime) & 1);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -988,6 +988,13 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
return;
|
||||
}
|
||||
|
||||
case MT_SA2_CRATE:
|
||||
case MT_ICECAPBLOCK:
|
||||
{
|
||||
Obj_TryCrateTouch(special, toucher);
|
||||
return;
|
||||
}
|
||||
|
||||
default: // SOC or script pickup
|
||||
P_SetTarget(&special->target, toucher);
|
||||
break;
|
||||
|
|
@ -2842,10 +2849,19 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
|
|||
return false;
|
||||
}
|
||||
|
||||
if (target->type == MT_BALLSWITCH_BALL)
|
||||
switch (target->type)
|
||||
{
|
||||
Obj_BallSwitchDamaged(target, inflictor, source);
|
||||
return false;
|
||||
case MT_BALLSWITCH_BALL:
|
||||
Obj_BallSwitchDamaged(target, inflictor, source);
|
||||
return false;
|
||||
|
||||
case MT_SA2_CRATE:
|
||||
case MT_ICECAPBLOCK:
|
||||
Obj_TryCrateDamage(target, inflictor);
|
||||
return true;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!force)
|
||||
|
|
|
|||
27
src/p_map.c
27
src/p_map.c
|
|
@ -1200,8 +1200,6 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
|
|||
return BMIT_CONTINUE;
|
||||
}
|
||||
|
||||
//}
|
||||
|
||||
if ((thing->type == MT_SPRINGSHELL || thing->type == MT_YELLOWSHELL) && thing->health > 0
|
||||
&& (tm.thing->player || (tm.thing->flags & MF_PUSHABLE)) && tm.thing->health > 0)
|
||||
{
|
||||
|
|
@ -1634,6 +1632,31 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
|
|||
}
|
||||
}
|
||||
|
||||
switch (tm.thing->type)
|
||||
{
|
||||
case MT_SA2_CRATE:
|
||||
case MT_ICECAPBLOCK:
|
||||
// Let crates stack on top of solid objects (this
|
||||
// includes other crates).
|
||||
if (thing->flags & MF_SOLID)
|
||||
{
|
||||
fixed_t thingtop = thing->z + thing->height;
|
||||
if (tm.thing->z > thing->z && thingtop > tm.floorz)
|
||||
{
|
||||
tm.floorz = thingtop;
|
||||
tm.floorrover = NULL;
|
||||
tm.floorslope = NULL;
|
||||
tm.floorpic = -1;
|
||||
tm.floorstep = 0;
|
||||
return BMIT_CONTINUE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// This code is causing conflicts for Ring Racers,
|
||||
// as solid objects cause bumping. If you need to
|
||||
// bring back this code for a moving platform-style
|
||||
|
|
|
|||
20
src/p_mobj.c
20
src/p_mobj.c
|
|
@ -6836,6 +6836,20 @@ static void P_MobjSceneryThink(mobj_t *mobj)
|
|||
Obj_PatrolIvoBallThink(mobj);
|
||||
return;
|
||||
}
|
||||
case MT_SA2_CRATE:
|
||||
case MT_ICECAPBLOCK:
|
||||
{
|
||||
if (!Obj_TryCrateThink(mobj))
|
||||
{
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MT_BOX_SIDE:
|
||||
{
|
||||
Obj_BoxSideThink(mobj);
|
||||
return;
|
||||
}
|
||||
case MT_VWREF:
|
||||
case MT_VWREB:
|
||||
{
|
||||
|
|
@ -14476,6 +14490,12 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj)
|
|||
Obj_PatrolIvoBallInit(mobj);
|
||||
break;
|
||||
}
|
||||
case MT_SA2_CRATE:
|
||||
case MT_ICECAPBLOCK:
|
||||
{
|
||||
Obj_TryCrateInit(mobj);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue