diff --git a/src/k_battle.c b/src/k_battle.c index e05b003a4..765df4966 100644 --- a/src/k_battle.c +++ b/src/k_battle.c @@ -18,6 +18,7 @@ #include "r_sky.h" // skyflatnum #include "k_grandprix.h" // K_CanChangeRules #include "p_spec.h" +#include "k_objects.h" // Battle overtime info struct battleovertime battleovertime; @@ -472,9 +473,7 @@ void K_RunPaperItemSpawners(void) #define MAXITEM 64 mobj_t *spotList[MAXITEM]; UINT8 spotMap[MAXITEM]; - UINT8 spotCount = 0, spotBackup = 0; - - INT16 starti = 0; + UINT8 spotCount = 0, spotBackup = 0, spotAvailable = 0; for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) { @@ -488,14 +487,26 @@ void K_RunPaperItemSpawners(void) emeraldsSpawned |= mo->extravalue1; } + if (mo->type == MT_MONITOR) + { + emeraldsSpawned |= Obj_MonitorGetEmerald(mo); + } + if (mo->type != MT_PAPERITEMSPOT) continue; if (spotCount >= MAXITEM) continue; + if (Obj_ItemSpotIsAvailable(mo)) + { + // spotMap first only includes spots + // where a monitor doesn't exist + spotMap[spotAvailable] = spotCount; + spotAvailable++; + } + spotList[spotCount] = mo; - spotMap[spotCount] = spotCount; spotCount++; } @@ -513,7 +524,6 @@ void K_RunPaperItemSpawners(void) if (!(emeraldsSpawned & emeraldFlag)) { firstUnspawnedEmerald = emeraldFlag; - starti = -1; break; } } @@ -521,77 +531,72 @@ void K_RunPaperItemSpawners(void) //CONS_Printf("leveltime = %d ", leveltime); - spotBackup = spotCount; - for (i = starti; i < pcount; i++) + if (spotAvailable > 0) { - UINT8 r = 0, key = 0; - mobj_t *drop = NULL; - SINT8 flip = 1; + const UINT8 r = spotMap[P_RandomKey(PR_ITEM_ROULETTE, spotAvailable)]; - if (spotCount == 0) + Obj_ItemSpotAssignMonitor(spotList[r], Obj_SpawnMonitor( + spotList[r], 1 + pcount, firstUnspawnedEmerald)); + } + + for (i = 0; i < spotCount; ++i) + { + // now spotMap includes every spot + spotMap[i] = i; + } + + if ((gametyperules & GTR_SPHERES) && IsOnInterval(2 * interval)) + { + spotBackup = spotCount; + for (i = 0; i < pcount; i++) { - // all are accessible again - spotCount = spotBackup; - } + UINT8 r = 0, key = 0; + mobj_t *drop = NULL; + SINT8 flip = 1; - if (spotCount == 1) - { - key = 0; - } - else - { - key = P_RandomKey(PR_ITEM_ROULETTE, spotCount); - } - - r = spotMap[key]; - - //CONS_Printf("[%d %d %d] ", i, key, r); - - flip = P_MobjFlip(spotList[r]); - - // When -1, we're spawning a Chaos Emerald. - if (i == -1) - { - drop = K_SpawnChaosEmerald( - spotList[r]->x, spotList[r]->y, spotList[r]->z + (128 * mapobjectscale * flip), - FixedAngle(P_RandomRange(PR_ITEM_ROULETTE, 0, 359) * FRACUNIT), flip, - firstUnspawnedEmerald - ); - } - else - { - if ((gametyperules & GTR_SPHERES) && IsOnInterval(2 * interval)) + if (spotCount == 0) { - drop = K_SpawnSphereBox( - spotList[r]->x, spotList[r]->y, spotList[r]->z + (128 * mapobjectscale * flip), - FixedAngle(P_RandomRange(PR_ITEM_ROULETTE, 0, 359) * FRACUNIT), flip, - 10 - ); - K_FlipFromObject(drop, spotList[r]); + // all are accessible again + spotCount = spotBackup; } - drop = K_CreatePaperItem( + if (spotCount == 1) + { + key = 0; + } + else + { + key = P_RandomKey(PR_ITEM_ROULETTE, spotCount); + } + + r = spotMap[key]; + + //CONS_Printf("[%d %d %d] ", i, key, r); + + flip = P_MobjFlip(spotList[r]); + + drop = K_SpawnSphereBox( spotList[r]->x, spotList[r]->y, spotList[r]->z + (128 * mapobjectscale * flip), - FixedAngle(P_RandomRange(PR_ITEM_ROULETTE, 0, 359) * FRACUNIT), flip, - 0, 0 + FixedAngle(P_RandomRange(PR_ITEM_ROULETTE, 0, 359) * FRACUNIT), flip, + 10 ); - } - K_FlipFromObject(drop, spotList[r]); + K_FlipFromObject(drop, spotList[r]); - spotCount--; - if (key != spotCount) - { - // So the core theory of what's going on is that we keep every - // available option at the front of the array, so we don't have - // to skip over any gaps or do recursion to avoid doubles. - // But because spotCount can be reset in the case of a low - // quanitity of item spawnpoints in a map, we still need every - // entry in the array, even outside of the "visible" range. - // A series of swaps allows us to adhere to both constraints. - // -toast 22/03/22 (semipalindromic!) - spotMap[key] = spotMap[spotCount]; - spotMap[spotCount] = r; // was set to spotMap[key] previously + spotCount--; + if (key != spotCount) + { + // So the core theory of what's going on is that we keep every + // available option at the front of the array, so we don't have + // to skip over any gaps or do recursion to avoid doubles. + // But because spotCount can be reset in the case of a low + // quanitity of item spawnpoints in a map, we still need every + // entry in the array, even outside of the "visible" range. + // A series of swaps allows us to adhere to both constraints. + // -toast 22/03/22 (semipalindromic!) + spotMap[key] = spotMap[spotCount]; + spotMap[spotCount] = r; // was set to spotMap[key] previously + } } } //CONS_Printf("\n"); diff --git a/src/k_objects.h b/src/k_objects.h index 5ded33e2f..b1fe2014b 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -82,6 +82,12 @@ void Obj_MonitorOnDamage(mobj_t *monitor, mobj_t *inflictor, INT32 damage); void Obj_MonitorOnDeath(mobj_t *monitor); void Obj_MonitorShardThink(mobj_t *shard); UINT32 Obj_MonitorGetEmerald(const mobj_t *monitor); +void Obj_MonitorSetItemSpot(mobj_t *monitor, mobj_t *spot); + +/* Item Spot */ +boolean Obj_ItemSpotIsAvailable(const mobj_t *spot); +void Obj_ItemSpotAssignMonitor(mobj_t *spot, mobj_t *monitor); +void Obj_ItemSpotUpdate(mobj_t *spot); #ifdef __cplusplus } // extern "C" diff --git a/src/objects/CMakeLists.txt b/src/objects/CMakeLists.txt index aa1b91079..440f700de 100644 --- a/src/objects/CMakeLists.txt +++ b/src/objects/CMakeLists.txt @@ -11,4 +11,5 @@ target_sources(SRB2SDL2 PRIVATE broly.c ufo.c monitor.c + item-spot.c ) diff --git a/src/objects/item-spot.c b/src/objects/item-spot.c new file mode 100644 index 000000000..0fdff3056 --- /dev/null +++ b/src/objects/item-spot.c @@ -0,0 +1,35 @@ +#include "../doomdef.h" +#include "../m_fixed.h" +#include "../k_objects.h" +#include "../k_battle.h" +#include "../p_tick.h" +#include "../p_local.h" + +#define spot_monitor(o) ((o)->target) +#define spot_cool(o) ((o)->threshold) + +boolean +Obj_ItemSpotIsAvailable (const mobj_t *spot) +{ + return P_MobjWasRemoved(spot_monitor(spot)) && + (leveltime - spot_cool(spot)) > BATTLE_SPAWN_INTERVAL; +} + +void +Obj_ItemSpotAssignMonitor +( mobj_t * spot, + mobj_t * monitor) +{ + P_SetTarget(&spot_monitor(spot), monitor); + Obj_MonitorSetItemSpot(monitor, spot); +} + +void +Obj_ItemSpotUpdate (mobj_t *spot) +{ + if (P_MobjWasRemoved(spot_monitor(spot)) || + spot_monitor(spot)->health <= 0) + { + spot_cool(spot) = leveltime; + } +} diff --git a/src/objects/monitor.c b/src/objects/monitor.c index 16967c771..50d13ee3c 100644 --- a/src/objects/monitor.c +++ b/src/objects/monitor.c @@ -32,6 +32,7 @@ static const struct monitor_part_config { MONITOR_PART_DEFINE (-5, 5, S_MONITOR_STAND), }; +#define monitor_spot(o) ((o)->target) #define monitor_rngseed(o) ((o)->movedir) #define monitor_itemcount(o) ((o)->movecount) #define monitor_spawntic(o) ((o)->reactiontime) @@ -671,6 +672,11 @@ Obj_MonitorOnDeath (mobj_t *monitor) // There is hitlag from being damaged, so remove // tangibility RIGHT NOW. monitor->flags &= ~(MF_SOLID); + + if (!P_MobjWasRemoved(monitor_spot(monitor))) + { + Obj_ItemSpotUpdate(monitor_spot(monitor)); + } } void @@ -689,3 +695,11 @@ Obj_MonitorGetEmerald (const mobj_t *monitor) { return monitor_emerald(monitor); } + +void +Obj_MonitorSetItemSpot +( mobj_t * monitor, + mobj_t * spot) +{ + P_SetTarget(&monitor_spot(monitor), spot); +}