Spawn Battle monitors

- Item count has always scaled up with player count. Items
spawn every N tics. Previously, these items were evenly
distributed across all item spots. Now, only one monitor
is spawned at a time and the number of items inside scales
up.

- Emeralds spawn inside of monitors instead of loosely.
- Sphere boxes should spawn in the same way as before.

- Once a monitor has been spawned at an item spot, no more
monitors can be spawned there until that one is destroyed.
There's an additional delay of one spawn interval before
a monitor can be spawned in that location again. This is
so a monitor cannot ever instantly spawn back if one is
destroyed at just the right time.
This commit is contained in:
James R 2023-01-01 02:10:10 -08:00
parent 3491bd0b1d
commit 7ce12f37dc
5 changed files with 126 additions and 65 deletions

View file

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

View file

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

View file

@ -11,4 +11,5 @@ target_sources(SRB2SDL2 PRIVATE
broly.c
ufo.c
monitor.c
item-spot.c
)

35
src/objects/item-spot.c Normal file
View file

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

View file

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