mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-10-30 08:01:28 +00:00
- Transparent for single-screen players who can't pick up - Land quicker and closer after monitor destruction - Lock for less time to compensate
759 lines
15 KiB
C
759 lines
15 KiB
C
#include "../doomdef.h"
|
|
#include "../doomstat.h"
|
|
#include "../info.h"
|
|
#include "../k_objects.h"
|
|
#include "../p_local.h"
|
|
#include "../r_state.h"
|
|
#include "../k_kart.h"
|
|
#include "../k_battle.h"
|
|
#include "../m_random.h"
|
|
#include "../r_main.h"
|
|
#include "../s_sound.h"
|
|
|
|
#define FINE90 (FINEANGLES/4)
|
|
#define FINE180 (FINEANGLES/2)
|
|
#define TRUETAN(n) FINETANGENT(FINE90 + (n)) // bruh
|
|
|
|
#define HEALTHFACTOR (FRACUNIT/4) // Always takes at most, 4 hits.
|
|
|
|
#define MONITOR_PART_DEFINE(dispoffset, nsides, ...) \
|
|
{dispoffset, nsides, (statenum_t[]){__VA_ARGS__, 0}}
|
|
|
|
static const struct monitor_part_config {
|
|
INT32 dispoffset;
|
|
UINT8 nsides;
|
|
statenum_t * states;
|
|
} monitor_parts[] = {
|
|
MONITOR_PART_DEFINE (0, 3,
|
|
S_MONITOR_SCREEN1A,
|
|
S_ITEMICON,
|
|
S_MONITOR_CRACKB,
|
|
S_MONITOR_CRACKA),
|
|
|
|
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)
|
|
#define monitor_emerald(o) ((o)->extravalue1)
|
|
#define monitor_damage(o) ((o)->extravalue2)
|
|
#define monitor_rammingspeed(o) ((o)->movefactor)
|
|
#define monitor_combohit(o) ((o)->cusval)
|
|
|
|
static inline UINT8
|
|
get_monitor_itemcount (const mobj_t *monitor)
|
|
{
|
|
// protects against divide by zero
|
|
return max(monitor_itemcount(monitor), 1);
|
|
}
|
|
|
|
#define part_monitor(o) ((o)->target)
|
|
#define part_type(o) ((o)->extravalue1)
|
|
#define part_index(o) ((o)->extravalue2)
|
|
#define part_theta(o) ((o)->movedir)
|
|
|
|
#define shard_can_roll(o) ((o)->extravalue1)
|
|
|
|
static const sprcache_t * get_state_sprcache (statenum_t);
|
|
|
|
static const sprcache_t *
|
|
get_sprcache
|
|
( spritenum_t sprite,
|
|
UINT8 frame)
|
|
{
|
|
const spritedef_t *sprdef = &sprites[sprite];
|
|
|
|
if (frame < sprdef->numframes)
|
|
{
|
|
size_t lump = sprdef->spriteframes[frame].lumpid[0];
|
|
|
|
return &spritecachedinfo[lump];
|
|
}
|
|
else
|
|
{
|
|
return get_state_sprcache(S_UNKNOWN);
|
|
}
|
|
}
|
|
|
|
static const sprcache_t *
|
|
get_state_sprcache (statenum_t statenum)
|
|
{
|
|
return get_sprcache(states[statenum].sprite,
|
|
states[statenum].frame & FF_FRAMEMASK);
|
|
}
|
|
|
|
static inline fixed_t
|
|
get_inradius
|
|
( fixed_t length,
|
|
INT32 nsides)
|
|
{
|
|
return FixedDiv(length, 2 * TRUETAN(FINE180 / nsides));
|
|
}
|
|
|
|
static inline void
|
|
center_item_sprite
|
|
( mobj_t * part,
|
|
fixed_t scale)
|
|
{
|
|
part->spriteyoffset = 25*FRACUNIT;
|
|
part->spritexscale = scale;
|
|
part->spriteyscale = scale;
|
|
}
|
|
|
|
static mobj_t *
|
|
spawn_part
|
|
( mobj_t * monitor,
|
|
statenum_t state)
|
|
{
|
|
mobj_t *part = P_SpawnMobjFromMobj(
|
|
monitor, 0, 0, 0, MT_MONITOR_PART);
|
|
|
|
P_SetMobjState(part, state);
|
|
P_SetTarget(&part_monitor(part), monitor);
|
|
|
|
part_type(part) = state;
|
|
|
|
switch (state)
|
|
{
|
|
case S_ITEMICON:
|
|
// The first frame of the monitor is TV static so
|
|
// this should be invisible on the first frame.
|
|
part->renderflags |= RF_DONTDRAW;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return part;
|
|
}
|
|
|
|
static void
|
|
spawn_part_side
|
|
( mobj_t * monitor,
|
|
fixed_t rad,
|
|
fixed_t ang,
|
|
const struct monitor_part_config * p,
|
|
size_t side)
|
|
{
|
|
INT32 i = 0;
|
|
|
|
while (p->states[i])
|
|
{
|
|
mobj_t *part = spawn_part(monitor, p->states[i]);
|
|
|
|
part->radius = rad;
|
|
part_theta(part) = ang;
|
|
|
|
// add one point for each layer (back to front order)
|
|
part->dispoffset = p->dispoffset + i;
|
|
|
|
part_index(part) = side;
|
|
|
|
i++;
|
|
}
|
|
}
|
|
|
|
static void
|
|
spawn_monitor_parts
|
|
( mobj_t * monitor,
|
|
const struct monitor_part_config *p)
|
|
{
|
|
const sprcache_t *info = get_state_sprcache(p->states[0]);
|
|
const fixed_t width = FixedMul(monitor->scale, info->width);
|
|
const fixed_t rad = get_inradius(width, p->nsides);
|
|
const fixed_t angle_factor = ANGLE_MAX / p->nsides;
|
|
|
|
INT32 i;
|
|
angle_t ang = 0;
|
|
|
|
for (i = 0; i < p->nsides; ++i)
|
|
{
|
|
spawn_part_side(monitor, rad, ang, p, i);
|
|
ang += angle_factor;
|
|
}
|
|
}
|
|
|
|
static inline boolean
|
|
can_shard_state_roll (statenum_t state)
|
|
{
|
|
switch (state)
|
|
{
|
|
case S_MONITOR_BIG_SHARD:
|
|
case S_MONITOR_SMALL_SHARD:
|
|
return true;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static void
|
|
spawn_shard
|
|
( mobj_t * part,
|
|
statenum_t state)
|
|
{
|
|
mobj_t *monitor = part_monitor(part);
|
|
|
|
// These divisions and multiplications are done on the
|
|
// offsets to give bigger increments of randomness.
|
|
|
|
const fixed_t half = FixedDiv(
|
|
monitor->height, monitor->scale) / 2;
|
|
|
|
const UINT16 rad = (monitor->radius / monitor->scale) / 4;
|
|
const UINT16 tall = (half / FRACUNIT) / 4;
|
|
|
|
mobj_t *p = P_SpawnMobjFromMobj(monitor,
|
|
P_RandomRange(PR_ITEM_DEBRIS, -(rad), rad) * 8 * FRACUNIT,
|
|
P_RandomRange(PR_ITEM_DEBRIS, -(rad), rad) * 8 * FRACUNIT,
|
|
(half / 4) + P_RandomKey(PR_ITEM_DEBRIS, tall + 1) * 4 * FRACUNIT,
|
|
MT_MONITOR_SHARD);
|
|
|
|
angle_t th = (part->angle + ANGLE_90);
|
|
|
|
th -= P_RandomKey(PR_ITEM_DEBRIS, ANGLE_45) - ANGLE_22h;
|
|
|
|
p->hitlag = 0;
|
|
|
|
P_Thrust(p, th, 6 * p->scale + monitor_rammingspeed(monitor));
|
|
p->momz = P_RandomRange(PR_ITEM_DEBRIS, 3, 10) * p->scale;
|
|
|
|
P_SetMobjState(p, state);
|
|
|
|
shard_can_roll(p) = can_shard_state_roll(state);
|
|
|
|
if (shard_can_roll(p))
|
|
{
|
|
p->rollangle = P_Random(PR_ITEM_DEBRIS);
|
|
}
|
|
|
|
if (P_RandomChance(PR_ITEM_DEBRIS, FRACUNIT/2))
|
|
{
|
|
p->renderflags |= RF_DONTDRAW;
|
|
}
|
|
}
|
|
|
|
static void
|
|
spawn_debris (mobj_t *part)
|
|
{
|
|
const mobj_t *monitor = part_monitor(part);
|
|
|
|
fixed_t i;
|
|
|
|
for (i = monitor->health;
|
|
i <= FRACUNIT; i += HEALTHFACTOR/2)
|
|
{
|
|
spawn_shard(part, S_MONITOR_BIG_SHARD);
|
|
spawn_shard(part, S_MONITOR_SMALL_SHARD);
|
|
spawn_shard(part, S_MONITOR_TWINKLE);
|
|
}
|
|
}
|
|
|
|
static void
|
|
spawn_monitor_explosion (mobj_t *monitor)
|
|
{
|
|
mobj_t *smoldering = P_SpawnMobjFromMobj(monitor, 0, 0, 0, MT_SMOLDERING);
|
|
|
|
UINT8 i;
|
|
|
|
// Note that a Broly Ki is purposefully not spawned. This
|
|
// is to reduce visual clutter since these monitors would
|
|
// probably get popped a lot.
|
|
|
|
K_MineFlashScreen(monitor);
|
|
|
|
P_SetScale(smoldering, (smoldering->destscale /= 3));
|
|
smoldering->tics = TICRATE*3;
|
|
|
|
for (i = 0; i < 8; ++i)
|
|
{
|
|
mobj_t *x = P_SpawnMobjFromMobj(monitor, 0, 0, 0, MT_BOOMEXPLODE);
|
|
x->hitlag = 0;
|
|
x->color = SKINCOLOR_WHITE;
|
|
x->momx = P_RandomRange(PR_EXPLOSION, -5, 5) * monitor->scale,
|
|
x->momy = P_RandomRange(PR_EXPLOSION, -5, 5) * monitor->scale,
|
|
x->momz = P_RandomRange(PR_EXPLOSION, 0, 6) * monitor->scale * P_MobjFlip(monitor);
|
|
P_SetScale(x, (x->destscale *= 3));
|
|
}
|
|
}
|
|
|
|
static void
|
|
kill_monitor_part (mobj_t *part)
|
|
{
|
|
const statenum_t statenum = part_type(part);
|
|
|
|
switch (statenum)
|
|
{
|
|
case S_ITEMICON:
|
|
P_RemoveMobj(part);
|
|
return;
|
|
|
|
case S_MONITOR_STAND:
|
|
part->momx = 0;
|
|
part->momy = 0;
|
|
break;
|
|
|
|
case S_MONITOR_SCREEN1A:
|
|
spawn_debris(part);
|
|
P_SetMobjState(part, S_MONITOR_SCREEN1B);
|
|
/*FALLTHRU*/
|
|
|
|
default:
|
|
/* To be clear, momx/y do not need to set because
|
|
those fields are set every tic to offset each
|
|
part. */
|
|
part->momz = (part->height / 8) * P_MobjFlip(part);
|
|
}
|
|
|
|
part->fuse = TICRATE;
|
|
part->flags &= ~(MF_NOGRAVITY);
|
|
}
|
|
|
|
static inline UINT32
|
|
restore_item_rng (UINT32 seed)
|
|
{
|
|
const UINT32 oldseed = P_GetRandSeed(PR_ITEM_ROULETTE);
|
|
|
|
P_SetRandSeedNet(PR_ITEM_ROULETTE,
|
|
P_GetInitSeed(PR_ITEM_ROULETTE), seed);
|
|
|
|
return oldseed;
|
|
}
|
|
|
|
static inline SINT8
|
|
get_item_result (void)
|
|
{
|
|
return K_GetTotallyRandomResult(0);
|
|
}
|
|
|
|
static SINT8
|
|
get_cycle_result
|
|
( const mobj_t * monitor,
|
|
size_t cycle)
|
|
{
|
|
const size_t rem = cycle %
|
|
get_monitor_itemcount(monitor);
|
|
|
|
SINT8 result;
|
|
size_t i;
|
|
|
|
const UINT32 oldseed = restore_item_rng(
|
|
monitor_rngseed(monitor));
|
|
|
|
for (i = 0; i <= rem; ++i)
|
|
{
|
|
result = get_item_result();
|
|
}
|
|
|
|
restore_item_rng(oldseed);
|
|
|
|
return result;
|
|
}
|
|
|
|
static inline tic_t
|
|
get_age (const mobj_t *monitor)
|
|
{
|
|
return (leveltime - monitor_spawntic(monitor));
|
|
}
|
|
|
|
static inline boolean
|
|
is_flickering (const mobj_t *part)
|
|
{
|
|
const mobj_t *monitor = part_monitor(part);
|
|
|
|
return monitor->fuse > 0 && monitor->fuse <= TICRATE;
|
|
}
|
|
|
|
static void
|
|
flicker
|
|
( mobj_t * part,
|
|
UINT8 interval)
|
|
{
|
|
const tic_t age = get_age(part_monitor(part));
|
|
|
|
if (age % interval)
|
|
{
|
|
part->renderflags |= RF_DONTDRAW;
|
|
}
|
|
else
|
|
{
|
|
part->renderflags &= ~(RF_DONTDRAW);
|
|
}
|
|
}
|
|
|
|
static void
|
|
project_icon (mobj_t *part)
|
|
{
|
|
const mobj_t *monitor = part_monitor(part);
|
|
const tic_t age = get_age(monitor);
|
|
|
|
// Item displayed on monitor cycles every N tics
|
|
if (age % 64 == 0)
|
|
{
|
|
const SINT8 result = get_cycle_result(monitor,
|
|
part_index(part) + (age / 64));
|
|
|
|
K_UpdateMobjItemOverlay(part,
|
|
K_ItemResultToType(result),
|
|
K_ItemResultToAmount(result));
|
|
|
|
center_item_sprite(part, 5*FRACUNIT/4);
|
|
}
|
|
|
|
flicker(part, is_flickering(part) ? 4 : 2);
|
|
}
|
|
|
|
static void
|
|
translate (mobj_t *part)
|
|
{
|
|
const angle_t ang = part_theta(part) +
|
|
part_monitor(part)->angle;
|
|
|
|
part->angle = (ang - ANGLE_90);
|
|
|
|
// Because of MF_NOCLIPTHING, no friction is applied.
|
|
// This object is teleported back to the monitor every
|
|
// tic so its position is in total only ever translated
|
|
// by this much.
|
|
part->momx = P_ReturnThrustX(NULL, ang, part->radius);
|
|
part->momy = P_ReturnThrustY(NULL, ang, part->radius);
|
|
}
|
|
|
|
static inline fixed_t
|
|
get_damage_multiplier (const mobj_t *monitor)
|
|
{
|
|
return FixedDiv(monitor_damage(monitor), HEALTHFACTOR);
|
|
}
|
|
|
|
static inline boolean
|
|
has_state
|
|
( const mobj_t * mobj,
|
|
statenum_t state)
|
|
{
|
|
return mobj->hitlag == 0 &&
|
|
(size_t)(mobj->state - states) == (size_t)state;
|
|
}
|
|
|
|
static mobj_t *
|
|
adjust_monitor_drop
|
|
( mobj_t * monitor,
|
|
mobj_t * drop)
|
|
{
|
|
if (drop->type == MT_EMERALD)
|
|
{
|
|
drop->momx = drop->momy = drop->momz = 0;
|
|
}
|
|
else
|
|
{
|
|
P_InstaThrust(drop, drop->angle, 8*mapobjectscale);
|
|
drop->momz *= 8;
|
|
}
|
|
|
|
K_FlipFromObject(drop, monitor);
|
|
|
|
return drop;
|
|
}
|
|
|
|
void
|
|
Obj_MonitorSpawnParts (mobj_t *monitor)
|
|
{
|
|
const size_t nparts =
|
|
sizeof monitor_parts / sizeof *monitor_parts;
|
|
|
|
size_t i;
|
|
|
|
P_SetScale(monitor, (monitor->destscale *= 2));
|
|
|
|
monitor_itemcount(monitor) = 0;
|
|
monitor_rngseed(monitor) = P_GetRandSeed(PR_ITEM_ROULETTE);
|
|
monitor_spawntic(monitor) = leveltime;
|
|
monitor_emerald(monitor) = 0;
|
|
|
|
for (i = 0; i < nparts; ++i)
|
|
{
|
|
spawn_monitor_parts(monitor, &monitor_parts[i]);
|
|
}
|
|
}
|
|
|
|
mobj_t *
|
|
Obj_SpawnMonitor
|
|
( mobj_t * origin,
|
|
UINT8 numItemTypes,
|
|
UINT8 emerald)
|
|
{
|
|
mobj_t *monitor = P_SpawnMobj(origin->x, origin->y,
|
|
origin->z, MT_MONITOR);
|
|
|
|
monitor->angle = P_Random(PR_DECORATION);
|
|
|
|
monitor_itemcount(monitor) = numItemTypes;
|
|
monitor_emerald(monitor) = emerald;
|
|
|
|
monitor->color = K_GetChaosEmeraldColor(emerald);
|
|
|
|
return monitor;
|
|
}
|
|
|
|
void
|
|
Obj_MonitorThink (mobj_t *monitor)
|
|
{
|
|
if (Obj_MonitorGetEmerald(monitor))
|
|
{
|
|
Obj_SpawnEmeraldSparks(monitor);
|
|
}
|
|
|
|
K_BattleOvertimeKiller(monitor);
|
|
}
|
|
|
|
void
|
|
Obj_MonitorPartThink (mobj_t *part)
|
|
{
|
|
const statenum_t statenum = part_type(part);
|
|
|
|
mobj_t *monitor = part_monitor(part);
|
|
|
|
if (part->fuse > 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (P_MobjWasRemoved(monitor))
|
|
{
|
|
P_RemoveMobj(part);
|
|
return;
|
|
}
|
|
|
|
if (has_state(monitor, monitor->info->deathstate))
|
|
{
|
|
kill_monitor_part(part);
|
|
return;
|
|
}
|
|
|
|
if (is_flickering(part))
|
|
{
|
|
flicker(part, 2);
|
|
}
|
|
|
|
if (monitor->hitlag)
|
|
{
|
|
const fixed_t shake = FixedMul(
|
|
2 * get_damage_multiplier(monitor),
|
|
monitor->radius / 8);
|
|
|
|
part->sprxoff = P_AltFlip(shake, 2);
|
|
part->spryoff = P_AltFlip(shake, 4);
|
|
}
|
|
else
|
|
{
|
|
part->sprxoff = 0;
|
|
part->spryoff = 0;
|
|
}
|
|
|
|
switch (statenum)
|
|
{
|
|
case S_MONITOR_SCREEN1A:
|
|
if (has_state(monitor, monitor->info->painstate))
|
|
{
|
|
spawn_debris(part);
|
|
}
|
|
break;
|
|
|
|
case S_MONITOR_CRACKA:
|
|
case S_MONITOR_CRACKB:
|
|
if (monitor->health < monitor->info->spawnhealth)
|
|
{
|
|
part->sprite = SPR_IMON; // initially SPR_NULL
|
|
part->frame = part->state->frame +
|
|
(monitor->health / HEALTHFACTOR);
|
|
}
|
|
break;
|
|
|
|
case S_ITEMICON:
|
|
project_icon(part);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
P_MoveOrigin(part, monitor->x, monitor->y, monitor->z);
|
|
|
|
translate(part);
|
|
}
|
|
|
|
fixed_t
|
|
Obj_MonitorGetDamage
|
|
( mobj_t * monitor,
|
|
mobj_t * inflictor,
|
|
UINT8 damagetype)
|
|
{
|
|
fixed_t damage;
|
|
|
|
if (leveltime - monitor_combohit(monitor) < 35) // Fast combo hits destroy monitors.
|
|
return FRACUNIT;
|
|
|
|
monitor_combohit(monitor) = leveltime;
|
|
|
|
switch (damagetype & DMG_TYPEMASK)
|
|
{
|
|
case DMG_VOLTAGE:
|
|
if (monitor->health < HEALTHFACTOR)
|
|
{
|
|
return HEALTHFACTOR;
|
|
}
|
|
else
|
|
{
|
|
// always reduce to final damage state
|
|
return (monitor->health - HEALTHFACTOR) + 1;
|
|
}
|
|
}
|
|
|
|
if (inflictor == NULL)
|
|
{
|
|
return HEALTHFACTOR;
|
|
}
|
|
|
|
if (inflictor->player)
|
|
{
|
|
const fixed_t weight =
|
|
K_GetMobjWeight(inflictor, monitor);
|
|
|
|
// HEALTHFACTOR is the minimum damage that can be
|
|
// dealt but player's weight (and speed) can buff the hit.
|
|
damage = HEALTHFACTOR +
|
|
(FixedMul(weight, HEALTHFACTOR) / 9);
|
|
|
|
|
|
if (inflictor->player->tiregrease > 0)
|
|
{
|
|
damage *= 3; // Do 3x the damage if the player is in spring grease state
|
|
}
|
|
|
|
if (inflictor->scale > mapobjectscale)
|
|
{
|
|
damage = P_ScaleFromMap(damage, inflictor->scale);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (inflictor->type == MT_INSTAWHIP)
|
|
{
|
|
damage = FRACUNIT/3;
|
|
if (K_IsPlayerWanted(inflictor->target->player))
|
|
damage = FRACUNIT; // Emerald hunting time!
|
|
}
|
|
else
|
|
{
|
|
damage = FRACUNIT; // kill instantly
|
|
}
|
|
}
|
|
|
|
return damage;
|
|
}
|
|
|
|
void
|
|
Obj_MonitorOnDamage
|
|
( mobj_t * monitor,
|
|
mobj_t * inflictor,
|
|
INT32 damage)
|
|
{
|
|
monitor->fuse = BATTLE_DESPAWN_TIME;
|
|
monitor_damage(monitor) = damage;
|
|
monitor_rammingspeed(monitor) = inflictor
|
|
? FixedDiv(FixedHypot(inflictor->momx, inflictor->momy), 4 * inflictor->radius) : 0;
|
|
monitor->hitlag =
|
|
3 * get_damage_multiplier(monitor) / FRACUNIT;
|
|
S_StartSound(monitor, sfx_kc40);
|
|
}
|
|
|
|
void
|
|
Obj_MonitorOnDeath
|
|
( mobj_t * monitor,
|
|
mobj_t * source)
|
|
{
|
|
const UINT8 itemcount = get_monitor_itemcount(monitor);
|
|
const angle_t ang = ANGLE_MAX / itemcount;
|
|
const SINT8 flip = P_MobjFlip(monitor);
|
|
|
|
INT32 i;
|
|
|
|
UINT32 sharedseed = restore_item_rng(
|
|
monitor_rngseed(monitor));
|
|
|
|
for (i = 0; i < itemcount; ++i)
|
|
{
|
|
const SINT8 result = get_item_result();
|
|
const UINT32 localseed = restore_item_rng(sharedseed);
|
|
|
|
mobj_t *drop = adjust_monitor_drop(monitor,
|
|
K_FlingPaperItem(
|
|
monitor->x, monitor->y, monitor->z + (128 * mapobjectscale * flip),
|
|
i * ang, flip,
|
|
K_ItemResultToType(result),
|
|
K_ItemResultToAmount(result)));
|
|
|
|
drop->momz /= 2; // This is player-locked, so no need to throw it high
|
|
|
|
if (!P_MobjWasRemoved(source) && source->player)
|
|
{
|
|
P_SetTarget(&drop->tracer, source);
|
|
drop->extravalue1 = 5*TICRATE;
|
|
drop->colorized = true;
|
|
drop->color = source->player->skincolor;
|
|
}
|
|
|
|
// K_FlingPaperItem may advance RNG, so update our
|
|
// copy of the seed afterward
|
|
sharedseed = restore_item_rng(localseed);
|
|
}
|
|
|
|
restore_item_rng(sharedseed);
|
|
|
|
if (monitor_emerald(monitor) != 0)
|
|
{
|
|
adjust_monitor_drop(monitor,
|
|
K_SpawnChaosEmerald(monitor->x, monitor->y, monitor->z + (128 * mapobjectscale * flip),
|
|
ang, flip, monitor_emerald(monitor)));
|
|
}
|
|
|
|
spawn_monitor_explosion(monitor);
|
|
|
|
S_StartSound(monitor, sfx_gshcc);
|
|
|
|
// 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
|
|
Obj_MonitorShardThink (mobj_t *shard)
|
|
{
|
|
if (shard_can_roll(shard))
|
|
{
|
|
shard->rollangle += ANGLE_45;
|
|
}
|
|
|
|
shard->renderflags ^= RF_DONTDRAW;
|
|
}
|
|
|
|
UINT32
|
|
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);
|
|
}
|