mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-10-30 08:01:28 +00:00
195 lines
5.7 KiB
C
195 lines
5.7 KiB
C
// DR. ROBOTNIK'S RING RACERS
|
|
//-----------------------------------------------------------------------------
|
|
// Copyright (C) 2022 by Sally "TehRealSalt" Cochenour
|
|
// Copyright (C) 2022 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.
|
|
//-----------------------------------------------------------------------------
|
|
/// \file random-item.c
|
|
/// \brief Random item boxes
|
|
|
|
#include "../doomdef.h"
|
|
#include "../doomstat.h"
|
|
#include "../k_objects.h"
|
|
#include "../g_game.h"
|
|
#include "../p_local.h"
|
|
#include "../r_defs.h"
|
|
#include "../k_battle.h"
|
|
#include "../m_random.h"
|
|
#include "../k_specialstage.h" // specialstageinfo
|
|
|
|
#define FLOAT_HEIGHT ( 12 * FRACUNIT )
|
|
#define FLOAT_TIME ( 2 * TICRATE )
|
|
#define FLOAT_ANGLE ( ANGLE_MAX / FLOAT_TIME )
|
|
|
|
#define SCALE_LO ( FRACUNIT * 2 / 3 )
|
|
#define SCALE_HI ( FRACUNIT )
|
|
#define SCALE_TIME ( 5 * TICRATE / 2 )
|
|
#define SCALE_ANGLE ( ANGLE_MAX / SCALE_TIME )
|
|
|
|
#define item_vfxtimer(o) ((o)->cvmem)
|
|
|
|
static player_t *GetItemBoxPlayer(mobj_t *mobj)
|
|
{
|
|
fixed_t closest = INT32_MAX;
|
|
player_t *player = NULL;
|
|
UINT8 i;
|
|
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
|
{
|
|
if (!(playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo) && !players[i].spectator))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Always use normal item box rules -- could pass in "2" for fakes but they blend in better like this
|
|
if (P_CanPickupItem(&players[i], 1))
|
|
{
|
|
// Check for players who can take this pickup, but won't be allowed to (antifarming)
|
|
UINT8 mytype = (mobj->flags2 & MF2_AMBUSH) ? 2 : 1;
|
|
if (P_IsPickupCheesy(&players[i], mytype))
|
|
continue;
|
|
|
|
fixed_t dist = P_AproxDistance(P_AproxDistance(
|
|
players[i].mo->x - mobj->x,
|
|
players[i].mo->y - mobj->y),
|
|
players[i].mo->z - mobj->z
|
|
);
|
|
|
|
if (dist > 8192*mobj->scale)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (dist < closest)
|
|
{
|
|
player = &players[i];
|
|
closest = dist;
|
|
}
|
|
}
|
|
}
|
|
|
|
return player;
|
|
}
|
|
|
|
static void ItemBoxColor(mobj_t *mobj)
|
|
{
|
|
player_t *player = GetItemBoxPlayer(mobj);
|
|
skincolornum_t color = SKINCOLOR_BLACK;
|
|
|
|
if (player != NULL)
|
|
{
|
|
color = player->skincolor;
|
|
}
|
|
|
|
mobj->color = color;
|
|
mobj->colorized = false;
|
|
}
|
|
|
|
static void ItemBoxBob(mobj_t *mobj)
|
|
{
|
|
const fixed_t sine = FINESINE((FLOAT_ANGLE * item_vfxtimer(mobj)) >> ANGLETOFINESHIFT);
|
|
const fixed_t bob = FixedMul(FLOAT_HEIGHT, sine);
|
|
mobj->spriteyoffset = FLOAT_HEIGHT + bob;
|
|
}
|
|
|
|
static void ItemBoxScaling(mobj_t *mobj)
|
|
{
|
|
const fixed_t sine = FINESINE((SCALE_ANGLE * item_vfxtimer(mobj)) >> ANGLETOFINESHIFT);
|
|
const fixed_t newScale = SCALE_LO + FixedMul(SCALE_HI - SCALE_LO, (sine + FRACUNIT) >> 1);
|
|
mobj->spritexscale = mobj->spriteyscale = newScale;
|
|
}
|
|
|
|
void Obj_RandomItemVisuals(mobj_t *mobj)
|
|
{
|
|
ItemBoxColor(mobj);
|
|
ItemBoxBob(mobj);
|
|
ItemBoxScaling(mobj);
|
|
item_vfxtimer(mobj)++;
|
|
|
|
if (mobj->type != MT_RANDOMITEM)
|
|
return;
|
|
|
|
// Respawn flow, documented by a dumb asshole:
|
|
// P_TouchSpecialThing -> P_ItemPop sets fuse, NOCLIPTHING and DONTDRAW.
|
|
// P_FuseThink does visual flicker, and when fuse is 0, unsets NOCLIPTHING/DONTDRAW/etc...
|
|
// ...unless it's a map-start box from Battle, in which case it does nothing and waits for
|
|
// P_RespawnBattleBoxes to trigger the effect instead, since Battle boxes don't respawn until
|
|
// the player's cleared out a good portion of the map.
|
|
//
|
|
// Then extraval1 starts ticking up and triggers the transformation from Ringbox to Random Item.
|
|
if (mobj->fuse == 0 && !(mobj->flags & MF_NOCLIPTHING) && !(mobj->flags2 & MF2_AMBUSH) && !cv_thunderdome.value
|
|
&& (modeattacking == 0 || specialstageinfo.valid)) // Time Attacking in Special is a fucked-looking exception
|
|
{
|
|
mobj->extravalue1++;
|
|
|
|
if (specialstageinfo.valid) // Setting the timer in this case probably looks kinda goofy, but P_ItemPop checks xval1, not states.
|
|
mobj->extravalue1 = max(mobj->extravalue1, RINGBOX_TIME); // I will change this if this logic ever becomes even slightly more complicated.
|
|
|
|
if (mobj->extravalue1 == RINGBOX_TIME)
|
|
{
|
|
// Sync the position in RINGBOX and RANDOMITEM animations.
|
|
statenum_t animDelta = mobj->state - states - S_RINGBOX1;
|
|
P_SetMobjState(mobj, S_RANDOMITEM1 + (animDelta%12));
|
|
}
|
|
}
|
|
}
|
|
|
|
boolean Obj_RandomItemSpawnIn(mobj_t *mobj)
|
|
{
|
|
if ((leveltime == starttime) && !(gametyperules & GTR_CIRCUIT) && (mobj->flags2 & MF2_BOSSNOTRAP)) // here on map start?
|
|
{
|
|
if (gametyperules & GTR_PAPERITEMS)
|
|
{
|
|
if (battleprisons == true)
|
|
{
|
|
;
|
|
}
|
|
else
|
|
{
|
|
// Spawn a battle monitor in your place and Fucking Die
|
|
mobj_t *paperspawner = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_PAPERITEMSPOT);
|
|
paperspawner->spawnpoint = mobj->spawnpoint;
|
|
mobj->spawnpoint->mobj = paperspawner;
|
|
P_RemoveMobj(mobj);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// poof into existance
|
|
P_UnsetThingPosition(mobj);
|
|
mobj->flags &= ~(MF_NOCLIPTHING|MF_NOBLOCKMAP);
|
|
mobj->renderflags &= ~RF_DONTDRAW;
|
|
P_SetThingPosition(mobj);
|
|
P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_EXPLODE);
|
|
nummapboxes++;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
fixed_t Obj_RandomItemScale(fixed_t oldScale)
|
|
{
|
|
const fixed_t intendedScale = oldScale * 3;
|
|
const fixed_t maxScale = FixedDiv(MAPBLOCKSIZE, mobjinfo[MT_RANDOMITEM].radius); // don't make them larger than the blockmap can handle
|
|
|
|
return min(intendedScale, maxScale);
|
|
}
|
|
|
|
void Obj_RandomItemSpawn(mobj_t *mobj)
|
|
{
|
|
item_vfxtimer(mobj) = P_RandomRange(PR_DECORATION, 0, SCALE_TIME - 1);
|
|
|
|
// Respawns are handled by P_RespawnBattleBoxes,
|
|
// but we do need to start MT_RANDOMITEMs in the right state...
|
|
if (mobj->type == MT_RANDOMITEM && (gametyperules & GTR_BUMPERS))
|
|
{
|
|
mobj->extravalue1 = RINGBOX_TIME;
|
|
P_SetMobjState(mobj, S_RANDOMITEM1);
|
|
}
|
|
|
|
mobj->destscale = Obj_RandomItemScale(mobj->destscale);
|
|
P_SetScale(mobj, mobj->destscale);
|
|
}
|