Battle UFOs, Checkpoints: use srb2::MobjList instead of standard containers

- Net synced; new mechanism in p_link.cpp to automatically
  manage mobj pointers at the global scope
This commit is contained in:
James R 2023-12-03 10:37:31 -08:00
parent 43d090f699
commit 02bacc9a6f
9 changed files with 149 additions and 73 deletions

View file

@ -50,6 +50,7 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32
p_floor.c
p_inter.c
p_lights.c
p_link.cpp
p_loop.c
p_map.c
p_mapthing.cpp

View file

@ -821,7 +821,7 @@ void K_BattleInit(boolean singleplayercontext)
}
g_battleufo.due = starttime;
g_battleufo.previousId = Obj_GetFirstBattleUFOSpawnerID();
g_battleufo.previousId = Obj_RandomBattleUFOSpawnerID() - 1;
}
UINT8 K_Bumpers(player_t *player)

View file

@ -184,8 +184,7 @@ void Obj_BattleUFODeath(mobj_t *ufo);
void Obj_LinkBattleUFOSpawner(mobj_t *spawner);
void Obj_UnlinkBattleUFOSpawner(mobj_t *spawner);
void Obj_SpawnBattleUFOFromSpawner(void);
INT32 Obj_GetFirstBattleUFOSpawnerID(void);
void Obj_ResetUFOSpawners(void);
INT32 Obj_RandomBattleUFOSpawnerID(void);
void Obj_BattleUFOBeamThink(mobj_t *beam);
/* Power-Up Aura */
@ -230,7 +229,6 @@ void Obj_FakeShadowThink(mobj_t *shadow);
boolean Obj_FakeShadowZ(const mobj_t *shadow, fixed_t *return_z, pslope_t **return_slope);
/* Checkpoints */
void Obj_ResetCheckpoints(void);
void Obj_LinkCheckpoint(mobj_t *end);
void Obj_UnlinkCheckpoint(mobj_t *end);
void Obj_CheckpointThink(mobj_t *end);

View file

@ -1,9 +1,10 @@
#include <algorithm>
#include <cstddef>
#include <iterator>
#include <set>
#include "../math/fixed.hpp"
#include "../mobj.hpp"
#include "../mobj_list.hpp"
#include "../doomdef.h"
#include "../m_random.h"
@ -14,6 +15,9 @@
using srb2::math::Fixed;
using srb2::Mobj;
using srb2::MobjList;
extern mobj_t* svg_battleUfoSpawners;
#define BATTLEUFO_LEG_ZOFFS (3*FRACUNIT) // Spawn height offset from the body
#define BATTLEUFO_LEGS (3) // Number of UFO legs to spawn
@ -27,6 +31,10 @@ struct Spawner : Mobj
{
void thing_args() = delete;
INT32 id() const { return this->mobj_t::thing_args[0]; }
void hnext() = delete;
Spawner* next() const { return Mobj::hnext<Spawner>(); }
void next(Spawner* n) { Mobj::hnext(n); }
};
struct UFO : Mobj
@ -53,69 +61,43 @@ struct UFO : Mobj
}
};
struct SpawnerCompare
{
bool operator()(const Spawner* a, const Spawner* b) const
{
return a->id() < b->id();
}
};
class SpawnerList
{
private:
std::set<Spawner*, SpawnerCompare> set_;
MobjList<Spawner, svg_battleUfoSpawners> list_;
public:
void insert(Spawner* spawner)
{
auto [it, inserted] = set_.insert(spawner);
if (inserted)
{
mobj_t* dummy = nullptr;
P_SetTarget(&dummy, spawner);
}
}
void erase(Spawner* spawner)
{
if (set_.erase(spawner))
{
mobj_t* dummy = spawner;
P_SetTarget(&dummy, nullptr);
}
}
void insert(Spawner* spawner) { list_.push_front(spawner); }
void erase(Spawner* spawner) { list_.erase(spawner); }
Spawner* next(INT32 order) const
{
auto it = std::upper_bound(
set_.begin(),
set_.end(),
order,
[](INT32 a, const Spawner* b) { return a < b->id(); }
);
using T = const Spawner*;
return it != set_.end() ? *it : *set_.begin();
auto it = std::find_if(list_.begin(), list_.end(), [order](T p) { return order < p->id(); });
auto min = [&](auto cmp) { return std::min_element(list_.begin(), list_.end(), cmp); };
return *(it != list_.end()
? min([order](T a, T b) { return order < a->id() && a->id() < b->id(); })
: min([](T a, T b) { return a->id() < b->id(); }));
}
INT32 random_id() const
{
if (set_.empty())
if (list_.empty())
{
return 0;
}
auto it = set_.begin();
auto it = list_.begin();
std::size_t count = std::distance(it, list_.end());
std::advance(it, P_RandomKey(PR_BATTLEUFO, set_.size()));
return (*std::prev(it == set_.begin() ? set_.end() : it))->id();
return std::next(it, P_RandomKey(PR_BATTLEUFO, count - 1u))->id();
}
void spawn_ufo() const
{
if (set_.empty())
if (list_.empty())
{
return;
}
@ -236,16 +218,11 @@ void Obj_SpawnBattleUFOFromSpawner(void)
g_spawners.spawn_ufo();
}
INT32 Obj_GetFirstBattleUFOSpawnerID(void)
INT32 Obj_RandomBattleUFOSpawnerID(void)
{
return g_spawners.random_id();
}
void Obj_ResetUFOSpawners(void)
{
g_spawners = {};
}
void Obj_BattleUFOBeamThink(mobj_t *beam)
{
P_SetObjectMomZ(beam, beam->info->speed, true);

View file

@ -8,10 +8,11 @@
//-----------------------------------------------------------------------------
#include <algorithm>
#include <vector>
#include <fmt/format.h>
#include "../mobj_list.hpp"
#include "../doomdef.h"
#include "../doomtype.h"
#include "../info.h"
@ -32,10 +33,13 @@
#include "../sounds.h"
#include "../tables.h"
extern mobj_t* svg_checkpoints;
#define checkpoint_id(o) ((o)->thing_args[0])
#define checkpoint_other(o) ((o)->target)
#define checkpoint_orb(o) ((o)->tracer)
#define checkpoint_arm(o) ((o)->hnext)
#define checkpoint_next(o) ((o)->hprev)
#define checkpoint_var(o) ((o)->movedir)
#define checkpoint_speed(o) ((o)->movecount)
#define checkpoint_speed_multiplier(o) ((o)->movefactor)
@ -120,6 +124,9 @@ struct Checkpoint : mobj_t
Arm* arm() const { return static_cast<Arm*>(checkpoint_arm(this)); }
void arm(Arm* n) { P_SetTarget(&checkpoint_arm(this), n); }
Checkpoint* next() const { return static_cast<Checkpoint*>(checkpoint_next(this)); }
void next(Checkpoint* n) { P_SetTarget(&checkpoint_next(this), n); }
fixed_t var() const { return checkpoint_var(this); }
void var(fixed_t n) { checkpoint_var(this) = n; }
@ -391,34 +398,23 @@ private:
struct CheckpointManager
{
auto begin() { return vec_.begin(); }
auto end() { return vec_.end(); }
auto begin() { return list_.begin(); }
auto end() { return list_.end(); }
auto find(INT32 id) { return std::find_if(begin(), end(), [id](Checkpoint* chk) { return chk->id() == id; }); }
void push_back(Checkpoint* chk) { vec_.push_back(chk); }
void push_front(Checkpoint* chk) { list_.push_front(chk); }
void erase(const Checkpoint* chk)
{
if (auto it = std::find(vec_.begin(), vec_.end(), chk); it != end())
{
vec_.erase(it);
}
}
void erase(Checkpoint* chk) { list_.erase(chk); }
private:
std::vector<Checkpoint*> vec_;
srb2::MobjList<Checkpoint, svg_checkpoints> list_;
};
CheckpointManager g_checkpoints;
}; // namespace
void Obj_ResetCheckpoints(void)
{
g_checkpoints = {};
}
void Obj_LinkCheckpoint(mobj_t* end)
{
auto chk = static_cast<Checkpoint*>(end);
@ -456,7 +452,7 @@ void Obj_LinkCheckpoint(mobj_t* end)
}
else
{
g_checkpoints.push_back(chk);
g_checkpoints.push_front(chk);
}
chk->gingerbread();
@ -464,7 +460,7 @@ void Obj_LinkCheckpoint(mobj_t* end)
void Obj_UnlinkCheckpoint(mobj_t* end)
{
auto chk = static_cast<const Checkpoint*>(end);
auto chk = static_cast<Checkpoint*>(end);
g_checkpoints.erase(chk);

49
src/p_link.cpp Normal file
View file

@ -0,0 +1,49 @@
// 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 <functional>
#include <initializer_list>
#include "p_link.h"
#include "p_mobj.h"
#define LINK_PACK \
svg_battleUfoSpawners,\
svg_checkpoints
using link = mobj_t*;
using each_ref = std::initializer_list<std::reference_wrapper<mobj_t*>>;
link LINK_PACK;
void P_InitMobjPointers(void)
{
for (mobj_t*& head : each_ref {LINK_PACK})
{
head = nullptr;
}
}
void P_SaveMobjPointers(void(*fn)(mobj_t*))
{
for (mobj_t* head : {LINK_PACK})
{
fn(head);
}
}
void P_LoadMobjPointers(void(*fn)(mobj_t**))
{
for (mobj_t*& head : each_ref {LINK_PACK})
{
fn(&head);
}
}

27
src/p_link.h Normal file
View file

@ -0,0 +1,27 @@
// 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 p_link_h
#define p_link_h
#include "typedef.h"
#ifdef __cplusplus
extern "C" {
#endif
void P_InitMobjPointers(void);
void P_SaveMobjPointers(void(*callback)(mobj_t*));
void P_LoadMobjPointers(void(*callback)(mobj_t**));
#ifdef __cplusplus
} // extern "C"
#endif
#endif/*p_link_h*/

View file

@ -36,6 +36,7 @@
#include "lua_script.h"
#include "p_slopes.h"
#include "m_cond.h" // netUnlocked
#include "p_link.h"
// SRB2Kart
#include "k_grandprix.h"
@ -50,6 +51,8 @@
savedata_t savedata;
savedata_cup_t cupsavedata;
static savebuffer_t *current_savebuffer;
// Block UINT32s to attempt to ensure that the correct data is
// being sent and received
#define ARCHIVEBLOCK_MISC 0x7FEEDEED
@ -3839,6 +3842,11 @@ static void SavePolyfadeThinker(savebuffer_t *save, const thinker_t *th, const U
WRITEINT32(save->p, ht->timer);
}
static void WriteMobjPointer(mobj_t *mobj)
{
WRITEUINT32(current_savebuffer->p, SaveMobjnum(mobj));
}
static void P_NetArchiveThinkers(savebuffer_t *save)
{
const thinker_t *th;
@ -3846,6 +3854,8 @@ static void P_NetArchiveThinkers(savebuffer_t *save)
WRITEUINT32(save->p, ARCHIVEBLOCK_THINKERS);
P_SaveMobjPointers(WriteMobjPointer);
for (i = 0; i < NUM_THINKERLISTS; i++)
{
UINT32 numsaved = 0;
@ -5238,6 +5248,11 @@ static thinker_t* LoadPolyfadeThinker(savebuffer_t *save, actionf_p1 thinker)
return &ht->thinker;
}
static void ReadMobjPointer(mobj_t **mobj_p)
{
*mobj_p = LoadMobj(READUINT32(current_savebuffer->p));
}
static void P_NetUnArchiveThinkers(savebuffer_t *save)
{
thinker_t *currentthinker;
@ -5273,6 +5288,8 @@ static void P_NetUnArchiveThinkers(savebuffer_t *save)
// we don't want the removed mobjs to come back
P_InitThinkers();
P_LoadMobjPointers(ReadMobjPointer);
// clear sector thinker pointers so they don't point to non-existant thinkers for all of eternity
for (i = 0; i < numsectors; i++)
{
@ -5583,12 +5600,19 @@ static mobj_t *RelinkMobj(mobj_t **ptr)
return P_SetTarget(ptr, P_FindNewPosition(temp));
}
static void RelinkMobjVoid(mobj_t **ptr)
{
RelinkMobj(ptr);
}
static void P_RelinkPointers(void)
{
thinker_t *currentthinker;
mobj_t *mobj;
UINT32 temp, i;
P_LoadMobjPointers(RelinkMobjVoid);
// use info field (value = oldposition) to relink mobjs
for (currentthinker = thlist[THINK_MOBJ].next; currentthinker != &thlist[THINK_MOBJ];
currentthinker = currentthinker->next)
@ -6648,6 +6672,8 @@ void P_SaveGame(savebuffer_t *save)
void P_SaveNetGame(savebuffer_t *save, boolean resending)
{
current_savebuffer = save;
thinker_t *th;
mobj_t *mobj;
UINT32 i = 1; // don't start from 0, it'd be confused with a blank pointer otherwise
@ -6724,6 +6750,8 @@ badloadgame:
boolean P_LoadNetGame(savebuffer_t *save, boolean reloading)
{
current_savebuffer = save;
CV_LoadNetVars(&save->p);
if (!P_NetUnArchiveMisc(save, reloading))

View file

@ -29,6 +29,7 @@
#include "r_main.h"
#include "r_fps.h"
#include "d_clisrv.h" // UpdateChallenges
#include "p_link.h"
// Object place
#include "m_cheat.h"
@ -299,8 +300,7 @@ void P_InitThinkers(void)
skyboxcenterpnts[i] = skyboxviewpnts[i] = NULL;
}
Obj_ResetUFOSpawners();
Obj_ResetCheckpoints();
P_InitMobjPointers();
}
//