From f5d68783fc12c8293333d75ef28d08409ae86482 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 27 Jun 2023 18:04:26 -0700 Subject: [PATCH] Battle UFO spawning behavior - MT_BATTLEUFO_SPAWNER args[0] is the ID - Spawn a random UFO from the list spawner at the start of Battle - UFO spawns 200 units above the spawner - After destroyig a UFO, wait 25 seconds before spawning the next UFO (next ID in the list) --- src/k_battle.c | 8 +++ src/k_battle.h | 1 + src/k_objects.h | 5 ++ src/m_random.h | 2 + src/objects/CMakeLists.txt | 2 +- src/objects/battle-ufo.cpp | 137 ++++++++++++++++++++++++++++++++++++- src/p_mobj.c | 10 +++ src/p_tick.c | 3 + 8 files changed, 166 insertions(+), 2 deletions(-) diff --git a/src/k_battle.c b/src/k_battle.c index 86f4c6be7..c5292f9f9 100644 --- a/src/k_battle.c +++ b/src/k_battle.c @@ -350,6 +350,11 @@ void K_RunPaperItemSpawners(void) return; } + if (leveltime == g_battleufo.due) + { + Obj_SpawnBattleUFOFromSpawner(); + } + if (!IsOnInterval(interval)) { return; @@ -797,6 +802,9 @@ void K_BattleInit(boolean singleplayercontext) K_SpawnPlayerBattleBumpers(players+i); } } + + g_battleufo.due = starttime; + g_battleufo.previousId = Obj_GetFirstBattleUFOSpawnerID(); } UINT8 K_Bumpers(player_t *player) diff --git a/src/k_battle.h b/src/k_battle.h index caa68c58a..1acac3432 100644 --- a/src/k_battle.h +++ b/src/k_battle.h @@ -11,6 +11,7 @@ extern "C" { #define BATTLE_SPAWN_INTERVAL (4*TICRATE) #define BATTLE_DESPAWN_TIME (15*TICRATE) #define BATTLE_POWERUP_TIME (20*TICRATE) +#define BATTLE_UFO_TIME (25*TICRATE) extern struct battleovertime { diff --git a/src/k_objects.h b/src/k_objects.h index ec7093e44..f81ddfa98 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -166,6 +166,11 @@ void Obj_BattleUFOLegThink(mobj_t *leg); void Obj_BattleUFOThink(mobj_t *ufo); void Obj_SpawnBattleUFOLegs(mobj_t *ufo); 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); /* Power-Up Aura */ void Obj_SpawnPowerUpAura(player_t* player); diff --git a/src/m_random.h b/src/m_random.h index 039dcb6ad..54d3698a8 100644 --- a/src/m_random.h +++ b/src/m_random.h @@ -74,6 +74,8 @@ typedef enum PR_MOVINGTARGET, // Randomised moving targets + PR_BATTLEUFO, // Battle UFO spawning + PR_BOTS, // Bot spawning PRNUMCLASS diff --git a/src/objects/CMakeLists.txt b/src/objects/CMakeLists.txt index f85b01ee4..0aa872933 100644 --- a/src/objects/CMakeLists.txt +++ b/src/objects/CMakeLists.txt @@ -22,6 +22,6 @@ target_sources(SRB2SDL2 PRIVATE gachabom-rebound.cpp servant-hand.c super-flicky.cpp - battle-ufo.c + battle-ufo.cpp powerup-aura.cpp ) diff --git a/src/objects/battle-ufo.cpp b/src/objects/battle-ufo.cpp index b3d4ad7be..09a6bc954 100644 --- a/src/objects/battle-ufo.cpp +++ b/src/objects/battle-ufo.cpp @@ -1,5 +1,11 @@ +#include +#include +#include + #include "../doomdef.h" +#include "../m_random.h" #include "../p_local.h" +#include "../k_battle.h" #include "../k_objects.h" #define BATTLEUFO_LEG_ZOFFS (3*FRACUNIT) // Spawn height offset from the body @@ -7,6 +13,102 @@ #define BATTLEUFO_BOB_AMP (4) // UFO bob strength #define BATTLEUFO_BOB_SPEED (TICRATE*2) // UFO bob speed +#define spawner_id(o) ((o)->args[0]) + +#define ufo_spawner(o) ((o)->target) + +namespace +{ + +struct Spawner : mobj_t +{ + INT32 id() const { return spawner_id(this); } +}; + +struct UFO : mobj_t +{ + Spawner* spawner() const { return static_cast(ufo_spawner(this)); } + void spawner(Spawner* n) { P_SetTarget(&ufo_spawner(this), n); } +}; + +struct SpawnerCompare +{ + bool operator()(const Spawner* a, const Spawner* b) const + { + return a->id() < b->id(); + } +}; + +class SpawnerList +{ +private: + std::set set_; + +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); + } + } + + Spawner* next(INT32 order) const + { + auto it = std::upper_bound( + set_.begin(), + set_.end(), + order, + [](INT32 a, const Spawner* b) { return a < b->id(); } + ); + + return it != set_.end() ? *it : *set_.begin(); + } + + INT32 random_id() const + { + if (set_.empty()) + { + return 0; + } + + auto it = set_.begin(); + + std::advance(it, P_RandomKey(PR_BATTLEUFO, set_.size())); + + return (*std::prev(it == set_.begin() ? set_.end() : it))->id(); + } + + void spawn_ufo() const + { + if (set_.empty()) + { + return; + } + + Spawner* spawner = next(g_battleufo.previousId); + UFO* ufo = static_cast(P_SpawnMobjFromMobj(spawner, 0, 0, 200*FRACUNIT, MT_BATTLEUFO)); + + ufo->spawner(spawner); + } +}; + +SpawnerList g_spawners; + +}; // namespace + void Obj_BattleUFOThink(mobj_t *ufo) { // Copied and slightly modified from k_kart.c @@ -15,10 +117,18 @@ void Obj_BattleUFOThink(mobj_t *ufo) ufo->momz = targz; } -void Obj_BattleUFODeath(mobj_t *ufo) +void Obj_BattleUFODeath(mobj_t *mobj) { + UFO* ufo = static_cast(mobj); + ufo->momz = -(8*mapobjectscale)/2; ufo->fuse = TICRATE; + + if (ufo->spawner()) + { + g_battleufo.previousId = ufo->spawner()->id(); + g_battleufo.due = leveltime + BATTLE_UFO_TIME; + } } void Obj_SpawnBattleUFOLegs(mobj_t *ufo) @@ -66,3 +176,28 @@ void Obj_BattleUFOLegThink(mobj_t *leg) leg->eflags |= (leg->target->eflags & MFE_DAMAGEHITLAG); } } + +void Obj_LinkBattleUFOSpawner(mobj_t *spawner) +{ + g_spawners.insert(static_cast(spawner)); +} + +void Obj_UnlinkBattleUFOSpawner(mobj_t *spawner) +{ + g_spawners.erase(static_cast(spawner)); +} + +void Obj_SpawnBattleUFOFromSpawner(void) +{ + g_spawners.spawn_ufo(); +} + +INT32 Obj_GetFirstBattleUFOSpawnerID(void) +{ + return g_spawners.random_id(); +} + +void Obj_ResetUFOSpawners(void) +{ + g_spawners = {}; +} diff --git a/src/p_mobj.c b/src/p_mobj.c index 8518b8b7b..62cd0b10e 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -11175,6 +11175,11 @@ void P_RemoveMobj(mobj_t *mobj) Obj_RingShooterDelete(mobj); break; } + case MT_BATTLEUFO_SPAWNER: + { + Obj_UnlinkBattleUFOSpawner(mobj); + break; + } default: { break; @@ -13442,6 +13447,11 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj) Obj_InitLoopCenter(mobj); break; } + case MT_BATTLEUFO_SPAWNER: + { + Obj_LinkBattleUFOSpawner(mobj); + break; + } default: break; } diff --git a/src/p_tick.c b/src/p_tick.c index 871420055..1c756a8e5 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -41,6 +41,7 @@ #include "k_director.h" #include "k_specialstage.h" #include "acs/interface.h" +#include "k_objects.h" #ifdef PARANOIA #include "deh_tables.h" // MOBJTYPE_LIST @@ -230,6 +231,8 @@ void P_InitThinkers(void) { skyboxcenterpnts[i] = skyboxviewpnts[i] = NULL; } + + Obj_ResetUFOSpawners(); } // Adds a new thinker at the end of the list.