mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2026-02-20 20:41:10 +00:00
- Extracted functions so some state can be set up at spawn - Fake Puyo chamber load for snapshotmaps - Mobj starts off at shadowscale 0, but gets full-size when leaving the ground for the first time
1652 lines
45 KiB
C
1652 lines
45 KiB
C
// DR. ROBOTNIK'S RING RACERS
|
|
//-----------------------------------------------------------------------------
|
|
// Copyright (C) 2025 by Vivian "toastergrl" Grannell.
|
|
// Copyright (C) 2025 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 k_boss.h
|
|
/// \brief Blend Eye boss encounter
|
|
|
|
#include "../../p_local.h"
|
|
#include "../../m_random.h"
|
|
#include "../../k_kart.h"
|
|
#include "../../k_hitlag.h"
|
|
#include "../../k_battle.h"
|
|
#include "../../k_boss.h"
|
|
#include "../../k_respawn.h"
|
|
#include "../../s_sound.h"
|
|
#include "../../g_game.h"
|
|
#include "../../r_main.h" // R_PointToAngle2, R_PointToDist2
|
|
|
|
#define PUYOARCMULTIPLIER (3)
|
|
|
|
typedef enum
|
|
{
|
|
BLENDEYE_RESTART = 0,
|
|
BLENDEYE_LOADAMMO,
|
|
BLENDEYE_WAITAMMO,
|
|
BLENDEYE_BLENDING,
|
|
BLENDEYE_EXPLODING,
|
|
BLENDEYE_PINCH_THROWN,
|
|
BLENDEYE_PINCH_BOBBING,
|
|
BLENDEYE_PINCH_WHISKING,
|
|
BLENDEYE_PINCH_DRILLING,
|
|
BLENDEYE_PINCH_EXPLODING,
|
|
BLENDEYE_PINCH_BLOWNUP,
|
|
} blendeye_phase_e;
|
|
|
|
#define EYECOLOR \
|
|
(encoremode \
|
|
? SKINCOLOR_JAWZ \
|
|
: SKINCOLOR_KETCHUP \
|
|
)
|
|
|
|
#define K_SpawnBlendEyeExplosion(mo) \
|
|
K_SpawnMineExplosion( \
|
|
mo, \
|
|
EYECOLOR, \
|
|
0 \
|
|
)
|
|
|
|
/// - MAIN BODY - ///
|
|
|
|
static void VS_BlendEye_Eye_Parts(mobj_t *mobj, INT32 angledelta)
|
|
{
|
|
fixed_t x, y;
|
|
|
|
// Before angle update, move the shield.
|
|
if (mobj->tracer && mobj->tracer->hnext && mobj->movedir < BLENDEYE_EXPLODING)
|
|
{
|
|
mobj_t *ref = mobj->tracer->hnext;
|
|
while (ref)
|
|
{
|
|
x = mobj->x + P_ReturnThrustX(mobj, mobj->tracer->movedir + ref->movedir, 40*mobj->scale);
|
|
y = mobj->y + P_ReturnThrustY(mobj, mobj->tracer->movedir + ref->movedir, 40*mobj->scale);
|
|
P_MoveOrigin(ref, x, y, mobj->z);
|
|
ref->angle = mobj->tracer->movedir + ref->movedir - ANGLE_90;
|
|
ref = ref->hnext;
|
|
}
|
|
mobj->tracer->movedir = mobj->angle;
|
|
}
|
|
|
|
if (mobj->movedir != BLENDEYE_PINCH_DRILLING || mobj->movecount > TICRATE/2)
|
|
mobj->angle += angledelta;
|
|
|
|
// And after, move the eye.
|
|
if (mobj->tracer)
|
|
{
|
|
x = mobj->x + P_ReturnThrustX(mobj, mobj->angle, 38*mobj->scale);
|
|
y = mobj->y + P_ReturnThrustY(mobj, mobj->angle, 38*mobj->scale);
|
|
P_MoveOrigin(mobj->tracer, x, y, mobj->z + (mobj->height - mobj->tracer->height)/2);
|
|
mobj->tracer->angle = mobj->angle - ANGLE_90;
|
|
if (mobj->tracer->hprev)
|
|
{
|
|
P_MoveOrigin(mobj->tracer->hprev, mobj->x, mobj->y, max(mobj->floorz, mobj->z - (mobj->tracer->hprev->height)));
|
|
}
|
|
}
|
|
}
|
|
|
|
static mobj_t *VS_BlendEye_LoadAmmo(mobj_t *mobj, INT32 id)
|
|
{
|
|
angle_t ang = mobj->tracer->cusval + FixedAngle(id*360*FRACUNIT/3);
|
|
fixed_t h = mobj->z + mobj->height - 4*mapobjectscale;
|
|
fixed_t dist = mobj->cvmem - 2*FRACUNIT;
|
|
if (id <= 3)
|
|
ang += ANGLE_180;
|
|
|
|
mobj_t *ammo = P_SpawnMobjFromMobj(mobj,
|
|
P_ReturnThrustX(mobj, ang, dist),
|
|
P_ReturnThrustY(mobj, ang, dist),
|
|
256*FRACUNIT,
|
|
MT_BLENDEYE_PUYO);
|
|
P_SetScale(ammo, (ammo->destscale *= 2));
|
|
|
|
if (id <= 3)
|
|
h += ammo->height;
|
|
else
|
|
ammo->cusval = (id-3)*TICRATE;
|
|
|
|
P_SetObjectMomZ(ammo, -10*FRACUNIT, false);
|
|
ammo->angle = ang;
|
|
ammo->cvmem = dist;
|
|
ammo->movefactor = (TICRATE/2) + P_RandomKey(PR_MOVINGTARGET, TICRATE/8);
|
|
P_SetTarget(&ammo->tracer, mobj);
|
|
ammo->extravalue1 = h;
|
|
ammo->flags2 |= MF2_STRONGBOX;
|
|
|
|
return ammo;
|
|
}
|
|
|
|
void VS_BlendEye_Init(mobj_t *mobj)
|
|
{
|
|
UINT8 i;
|
|
mobj_t *prev, *newmo;
|
|
angle_t ang = 0;
|
|
|
|
// necessary preamble
|
|
P_SetScale(mobj, (mobj->destscale = mobj->scale*2));
|
|
|
|
// spawn the glass
|
|
prev = mobj;
|
|
for (i = 0; i < 8; i++)
|
|
{
|
|
ang = mobj->angle + FixedAngle(i*360*FRACUNIT/8);
|
|
newmo = P_SpawnMobjFromMobj(mobj,
|
|
P_ReturnThrustX(mobj, ang, 39*FRACUNIT),
|
|
P_ReturnThrustY(mobj, ang, 39*FRACUNIT),
|
|
44*FRACUNIT,
|
|
MT_BLENDEYE_GLASS);
|
|
newmo->angle = ang - ANGLE_90;
|
|
P_SetTarget(&prev->hnext, newmo);
|
|
prev = newmo;
|
|
}
|
|
|
|
// spawn the generators
|
|
prev = mobj;
|
|
ang = mobj->angle + ANGLE_45;
|
|
UINT8 numgenerators = (encoremode ? 4 : 3);
|
|
for (i = 0; i < numgenerators; i++)
|
|
{
|
|
newmo = P_SpawnMobjFromMobj(mobj,
|
|
P_ReturnThrustX(mobj, ang, 62*FRACUNIT),
|
|
P_ReturnThrustY(mobj, ang, 62*FRACUNIT),
|
|
0,
|
|
MT_BLENDEYE_GENERATOR);
|
|
P_SetScale(newmo, newmo->destscale = (3*newmo->destscale)/4);
|
|
newmo->momz = -512*FRACUNIT; // cba to hardcode a diff. z coord
|
|
newmo->angle = ang - ANGLE_90;
|
|
P_SetTarget(&newmo->tracer, mobj);
|
|
P_SetTarget(&prev->hprev, newmo);
|
|
prev = newmo;
|
|
if (i == 1 && numgenerators == 3)
|
|
ang -= ANGLE_135;
|
|
else
|
|
ang -= ANGLE_90;
|
|
}
|
|
|
|
// spawn the eye...
|
|
{
|
|
P_SetTarget(&mobj->tracer, P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_BLENDEYE_EYE));
|
|
P_SetTarget(&mobj->tracer->target, mobj);
|
|
mobj->tracer->angle = mobj->angle - ANGLE_90;
|
|
// ...and shield!
|
|
prev = mobj->tracer;
|
|
for (i = 0; i < 8; i++)
|
|
{
|
|
ang = FixedAngle(i*360*FRACUNIT/8);
|
|
newmo = P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_BLENDEYE_SHIELD);
|
|
newmo->angle = mobj->angle + ang-ANGLE_90;
|
|
newmo->movedir = ang;
|
|
if (i == 0)
|
|
P_SetMobjState(newmo, S_INVISIBLE);
|
|
else if (i == 1)
|
|
P_SetMobjState(newmo, S_BLENDEYE_SHIELD_R);
|
|
else if (i == 7)
|
|
P_SetMobjState(newmo, S_BLENDEYE_SHIELD_L);
|
|
newmo->cvmem = i;
|
|
P_SetTarget(&prev->hnext, newmo);
|
|
prev = newmo;
|
|
}
|
|
}
|
|
|
|
// initialise other important stuff
|
|
if (encoremode)
|
|
mobj->health++;
|
|
mobj->tracer->health = mobj->health;
|
|
mobj->health += numgenerators;
|
|
mobj->tracer->cvmem = mobj->health;
|
|
|
|
mobj->movedir = BLENDEYE_RESTART;
|
|
mobj->movecount = TICRATE;
|
|
|
|
mobj->reactiontime = mobj->z + (3*mobj->height)/2;
|
|
|
|
mobj->cvmem = 24*FRACUNIT;
|
|
|
|
VS_BlendEye_Eye_Parts(mobj, 0);
|
|
|
|
{
|
|
const char *enemyname, *subtitle;
|
|
if (!encoremode)
|
|
{
|
|
enemyname = "Blend Eye";
|
|
subtitle = "Here to serve";
|
|
}
|
|
else
|
|
{
|
|
enemyname = "Mean Blend Eye";
|
|
subtitle = "Promoted to assistant manager";
|
|
}
|
|
|
|
K_InitBossHealthBar(enemyname, subtitle, 0, mobj->tracer->health*(FRACUNIT/mobj->health), mobj->tracer->cvmem);
|
|
K_DeclareWeakspot(mobj, SPOT_NONE, EYECOLOR, true);
|
|
}
|
|
|
|
if (roundqueue.snapshotmaps)
|
|
{
|
|
mobj->tracer->cusval = ANGLE_45/2; // chosen by dice roll guaranteed to be random
|
|
|
|
// Test load
|
|
for (i = 6; i > 0; i--)
|
|
{
|
|
mobj_t *ammo = VS_BlendEye_LoadAmmo(mobj, i);
|
|
ammo->momz = 0;
|
|
ammo->z = ammo->extravalue1;
|
|
ammo->flags |= MF_NOGRAVITY;
|
|
P_SetMobjState(ammo, S_BLENDEYE_PUYO);
|
|
if (i != 6) // one random
|
|
ammo->sprite = mobj->movedir = SPR_PUYA + i - 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
static mobj_t *sourceofmurder;
|
|
|
|
static inline BlockItReturn_t PIT_MurderPuyos(mobj_t *thing)
|
|
{
|
|
if (thing->type != MT_BLENDEYE_PUYO)
|
|
return BMIT_CONTINUE; // not a puyo
|
|
|
|
if (thing->z < sourceofmurder->z)
|
|
return BMIT_CONTINUE; // too low
|
|
|
|
P_InstaThrust(thing, R_PointToAngle2(sourceofmurder->x, sourceofmurder->y, thing->x, thing->y), (thing->z - sourceofmurder->z)/4);
|
|
thing->momz = (thing->z - sourceofmurder->z)/16;
|
|
P_KillMobj(thing, sourceofmurder, sourceofmurder, DMG_NORMAL);
|
|
|
|
return BMIT_CONTINUE; // Indiscriminate
|
|
}
|
|
|
|
static void VS_BlendEye_MurderPuyos(mobj_t *mobj)
|
|
{
|
|
INT32 bx, by, xl, xh, yl, yh;
|
|
|
|
sourceofmurder = mobj;
|
|
|
|
yh = (unsigned)(mobj->y + (mobj->radius + MAXRADIUS) - bmaporgy)>>MAPBLOCKSHIFT;
|
|
yl = (unsigned)(mobj->y - (mobj->radius + MAXRADIUS) - bmaporgy)>>MAPBLOCKSHIFT;
|
|
xh = (unsigned)(mobj->x + (mobj->radius + MAXRADIUS) - bmaporgx)>>MAPBLOCKSHIFT;
|
|
xl = (unsigned)(mobj->x - (mobj->radius + MAXRADIUS) - bmaporgx)>>MAPBLOCKSHIFT;
|
|
|
|
BMBOUNDFIX (xl, xh, yl, yh);
|
|
|
|
for (by = yl; by <= yh; by++)
|
|
for (bx = xl; bx <= xh; bx++)
|
|
P_BlockThingsIterator(bx, by, PIT_MurderPuyos);
|
|
}
|
|
|
|
void VS_BlendEye_Thinker(mobj_t *mobj)
|
|
{
|
|
boolean deathwatch = false; // may be useful for later
|
|
|
|
// Init.
|
|
{
|
|
if (!mobj->tracer)
|
|
{
|
|
CONS_Alert(CONS_ERROR, "VS_BlendEye_Thinker: Reached thinker but init failed");
|
|
P_RemoveMobj(mobj);
|
|
return;
|
|
}
|
|
|
|
if (!mobj->tracer->tracer)
|
|
{
|
|
P_SetTarget(&mobj->tracer->tracer, VS_GetArena(mobj->thing_args[0]));
|
|
if (!mobj->tracer->tracer)
|
|
{
|
|
P_RemoveMobj(mobj);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
mobj->flags2 &= ~MF2_FRET;
|
|
|
|
// Targeting.
|
|
if (P_MobjWasRemoved(mobj->target)
|
|
|| !mobj->target->player
|
|
|| mobj->target->player->respawn.state != RESPAWNST_NONE
|
|
|| mobj->target->health <= 0)
|
|
{
|
|
P_SupermanLook4Players(mobj);
|
|
deathwatch = (P_MobjWasRemoved(mobj->target)
|
|
|| !mobj->target->player
|
|
|| mobj->target->player->respawn.state != RESPAWNST_NONE
|
|
|| mobj->target->health <= 0);
|
|
}
|
|
|
|
INT32 i;
|
|
fixed_t tiltstrength;
|
|
|
|
// phases and attacks
|
|
switch (mobj->movedir)
|
|
{
|
|
case BLENDEYE_LOADAMMO:
|
|
{
|
|
// SPAWN YOUR AMMO
|
|
if ((mobj->movecount % 5) == 0)
|
|
{
|
|
VS_BlendEye_LoadAmmo(mobj, (mobj->movecount/5));
|
|
}
|
|
|
|
if ((--mobj->movecount) == 0)
|
|
{
|
|
mobj->movedir = BLENDEYE_WAITAMMO;
|
|
mobj->movecount = TICRATE;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case BLENDEYE_WAITAMMO:
|
|
{
|
|
// WAIT #1
|
|
if (deathwatch)
|
|
;
|
|
else if ((--mobj->movecount) == 0)
|
|
{
|
|
mobj->movedir = BLENDEYE_BLENDING;
|
|
mobj->movecount = 4*TICRATE;
|
|
mobj->extravalue1 = mobj->reactiontime;
|
|
mobj->flags2 |= MF2_AMBUSH;
|
|
S_StartSound(NULL, sfx_befan1);
|
|
if (mobj->tracer)
|
|
P_SetMobjState(mobj->tracer, mobj->tracer->info->raisestate);
|
|
}
|
|
|
|
break;
|
|
}
|
|
case BLENDEYE_BLENDING:
|
|
{
|
|
// BLENDING IN ACTION
|
|
if (!S_SoundPlaying(NULL, sfx_befan1) && !S_SoundPlaying(NULL, sfx_befan2))
|
|
S_StartSound(NULL, sfx_befan2);
|
|
|
|
tiltstrength = 5;
|
|
if (leveltime & 1)
|
|
tiltstrength = -tiltstrength;
|
|
|
|
mobj->rollangle = FixedAngle(tiltstrength*FRACUNIT);
|
|
|
|
if ((--mobj->movecount) == 0)
|
|
{
|
|
mobj->rollangle = 0;
|
|
mobj->movedir = 0;
|
|
mobj->movecount = 3*TICRATE/2;
|
|
mobj->flags2 &= ~MF2_AMBUSH;
|
|
//S_StopSoundByID(NULL, sfx_befan2);
|
|
//S_StartSound(NULL, sfx_s3k85);
|
|
if (mobj->tracer)
|
|
P_SetMobjState(mobj->tracer, mobj->tracer->info->spawnstate);
|
|
}
|
|
|
|
break;
|
|
}
|
|
case BLENDEYE_EXPLODING:
|
|
{
|
|
// BLOWS UP!?
|
|
if ((--mobj->movecount) == 0)
|
|
{
|
|
// PINCH TRANSITION BEGIN !
|
|
mobj->rollangle = 0;
|
|
mobj->shadowscale = FRACUNIT;
|
|
mobj->movedir = BLENDEYE_PINCH_THROWN;
|
|
mobj_t *ref = mobj->hnext;
|
|
mobj_t *refnext = NULL;
|
|
while (ref)
|
|
{
|
|
refnext = ref->hnext;
|
|
P_KillMobj(ref, mobj, mobj, DMG_NORMAL);
|
|
ref = refnext;
|
|
}
|
|
S_StartSound(NULL, sfx_shattr);
|
|
if (mobj->tracer)
|
|
{
|
|
boolean lastturned = false;
|
|
ref = mobj->tracer->hnext;
|
|
P_SetTarget(&mobj->tracer->hnext, NULL);
|
|
while (ref)
|
|
{
|
|
if (ref->cvmem == 0)
|
|
ref->flags |= MF_NOCLIPTHING;
|
|
else if (ref->cvmem == 1)
|
|
P_SetMobjState(ref, S_BLENDEYE_SHIELD_BUSTED_R);
|
|
else if (ref->cvmem == 7)
|
|
P_SetMobjState(ref, S_BLENDEYE_SHIELD_BUSTED_L);
|
|
else if (lastturned == true)
|
|
lastturned = false;
|
|
else if (P_RandomChance(PR_DECORATION, FRACUNIT/2))
|
|
{
|
|
P_SetMobjState(ref, S_BLENDEYE_SHIELD_BUSTED);
|
|
if (P_RandomChance(PR_DECORATION, FRACUNIT/2))
|
|
{
|
|
ref->frame++;
|
|
}
|
|
lastturned = true;
|
|
}
|
|
ref = ref->hnext;
|
|
}
|
|
mobj->tracer->renderflags |= RF_DONTDRAW;
|
|
mobj->flags &= ~MF_NOGRAVITY;
|
|
mobj->flags2 |= MF2_FRET;
|
|
|
|
mobj_t *boomo = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_FZEROBOOM);
|
|
boomo->extravalue1 = 2;
|
|
P_SetScale(boomo, boomo->scale/3);
|
|
boomo->destscale = mapobjectscale;
|
|
K_SpawnBlendEyeExplosion(boomo);
|
|
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
|
{
|
|
if (!playeringame[i])
|
|
continue;
|
|
P_FlashPal(&players[i], PAL_WHITE, 1);
|
|
}
|
|
S_StartSound(NULL, sfx_s3k4e);
|
|
}
|
|
|
|
VS_BlendEye_MurderPuyos(mobj);
|
|
|
|
mobj->momz = 12*mobj->scale;
|
|
P_SetMobjState(mobj, mobj->info->raisestate);
|
|
mobj->extravalue1 = mobj->reactiontime;
|
|
if (!deathwatch)
|
|
{
|
|
mobj->angle = R_PointToAngle2(mobj->x, mobj->y, mobj->target->x, mobj->target->y);
|
|
}
|
|
mobj->movecount = P_RandomChance(PR_MOVINGTARGET, FRACUNIT/2) ? 1 : -1;
|
|
P_InstaThrust(mobj, mobj->angle + mobj->movecount*ANGLE_90, 4*mobj->scale);
|
|
}
|
|
else if ((mobj->movecount % 3) == 0)
|
|
{
|
|
// SPAWN A MINI-POP
|
|
angle_t rot = FixedAngle(P_RandomKey(PR_EXPLOSION, 360)*FRACUNIT);
|
|
fixed_t offs = P_RandomKey(PR_EXPLOSION, (mobj->info->height - mobjinfo[MT_SONIC3KBOSSEXPLODE].height)/FRACUNIT)*FRACUNIT;
|
|
P_SpawnMobjFromMobj(mobj,
|
|
P_ReturnThrustX(mobj, rot, 56*FRACUNIT),
|
|
P_ReturnThrustY(mobj, rot, 56*FRACUNIT),
|
|
offs,
|
|
MT_SONIC3KBOSSEXPLODE);
|
|
S_StartSound(NULL, sfx_s3kb4);
|
|
mobj_t *ref = mobj->hnext;
|
|
while (ref)
|
|
{
|
|
P_SetMobjState(ref, ref->info->painstate);
|
|
ref = ref->hnext;
|
|
}
|
|
|
|
tiltstrength = 5;
|
|
if (((mobj->movecount/2) % 3) == 0)
|
|
tiltstrength = -tiltstrength;
|
|
|
|
mobj->rollangle = FixedAngle(tiltstrength*FRACUNIT);
|
|
}
|
|
else
|
|
{
|
|
mobj_t *ref = mobj->hnext;
|
|
while (ref)
|
|
{
|
|
P_SetMobjState(ref, ref->info->spawnstate);
|
|
ref = ref->hnext;
|
|
}
|
|
mobj->rollangle = 0;
|
|
}
|
|
|
|
return;
|
|
}
|
|
case BLENDEYE_PINCH_THROWN:
|
|
{
|
|
// WOOOAAAAH, MY HEAD'S SPINNING
|
|
mobj->rollangle += (mobj->movecount*ANG20);
|
|
if (mobj->momz < 0 && mobj->z < mobj->extravalue1)
|
|
{
|
|
mobj->movedir = BLENDEYE_PINCH_BOBBING;
|
|
mobj->flags |= MF_NOGRAVITY|MF_SHOOTABLE;
|
|
mobj->flags &= ~MF_NOCLIPTHING;
|
|
mobj->flags2 &= ~MF2_FRET;
|
|
mobj->momx = mobj->momy = mobj->momz = 0;
|
|
mobj->rollangle = 0;
|
|
mobj->movecount = -TICRATE/2;
|
|
P_SetMobjState(mobj, mobj->info->spawnstate);
|
|
if (mobj->tracer)
|
|
mobj->tracer->renderflags &= ~RF_DONTDRAW;
|
|
|
|
if (!deathwatch)
|
|
{
|
|
mobj->angle = R_PointToAngle2(mobj->x, mobj->y, mobj->target->x, mobj->target->y);
|
|
}
|
|
|
|
P_SetTarget(&mobj->hprev, P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_OVERLAY));
|
|
P_SetTarget(&mobj->hprev->target, mobj);
|
|
mobj->hprev->flags |= MF_DONTENCOREMAP;
|
|
mobj->hprev->threshold |= OV_DONTROLL;
|
|
P_SetMobjState(mobj->hprev, S_BLENDEYE_FLAME);
|
|
S_StartSound(NULL, sfx_s3k4f);
|
|
mobj->cvmem = mobj->info->radius + 4*FRACUNIT;
|
|
}
|
|
|
|
//return;
|
|
break;
|
|
}
|
|
case BLENDEYE_PINCH_BOBBING:
|
|
case BLENDEYE_PINCH_WHISKING:
|
|
{
|
|
// BOBBING UP AND DOWN
|
|
fixed_t bobsize = 6*mobj->scale;
|
|
fixed_t bob = mobj->movecount;
|
|
bob = -P_ReturnThrustY(mobj, FixedAngle(bob*10*FRACUNIT), bobsize);
|
|
mobj->movecount++;
|
|
if (mobj->movedir == BLENDEYE_PINCH_WHISKING)
|
|
{
|
|
if (encoremode) // RUN FOR LONGER
|
|
mobj->extravalue1 -= (mobj->scale/4);
|
|
else
|
|
mobj->extravalue1 -= (2*mobj->scale/7);
|
|
|
|
if (!S_SoundPlaying(NULL, sfx_befan1) && !S_SoundPlaying(NULL, sfx_befan2))
|
|
S_StartSound(NULL, sfx_befan2);
|
|
|
|
tiltstrength = 5;
|
|
if (leveltime & 1)
|
|
tiltstrength = -tiltstrength;
|
|
mobj->rollangle = FixedAngle(tiltstrength*FRACUNIT);
|
|
|
|
if (deathwatch)
|
|
{
|
|
mobj->movedir = BLENDEYE_PINCH_BOBBING;
|
|
mobj->movecount = 0;
|
|
mobj->rollangle = 0;
|
|
mobj->extravalue1 = mobj->reactiontime;
|
|
mobj->flags2 &= ~MF2_AMBUSH;
|
|
if (mobj->tracer && mobj->tracer->hprev)
|
|
{
|
|
P_SetMobjState(mobj->tracer->hprev, S_BLENDEYE_EGGBEATER);
|
|
P_SetMobjState(mobj->tracer, mobj->tracer->info->spawnstate);
|
|
}
|
|
}
|
|
else if (mobj->tracer && mobj->tracer->tracer && mobj->tracer->hprev)
|
|
{
|
|
// MOVE TOWARDS TARGET
|
|
angle_t mompoint = R_PointToAngle2(mobj->x, mobj->y, mobj->target->x, mobj->target->y); // replace first two with mobj->tracer->hnext->x and ->y for alt behaviour
|
|
fixed_t mommag = FixedHypot(mobj->target->x - mobj->x, mobj->target->y - mobj->y);
|
|
fixed_t bestspeed = 5*mobj->scale;
|
|
fixed_t accel = (mobj->scale/2);
|
|
boolean forcegoaround = false;
|
|
angle_t test = R_PointToAngle2(mobj->tracer->tracer->x, mobj->tracer->tracer->y, mobj->x, mobj->y)
|
|
- R_PointToAngle2(mobj->tracer->tracer->x, mobj->tracer->tracer->y, mobj->target->x, mobj->target->y);
|
|
if (test >= ANGLE_180)
|
|
test = InvAngle(test);
|
|
|
|
if (test > ANGLE_90)
|
|
{
|
|
bestspeed += (AngleFixed(test-ANGLE_90)/(encoremode ? 20 : 30));
|
|
forcegoaround = true;
|
|
}
|
|
|
|
if (mobj->tracer->hprev->extravalue1 + accel < bestspeed)
|
|
mobj->tracer->hprev->extravalue1 += accel;
|
|
else
|
|
mobj->tracer->hprev->extravalue1 = bestspeed;
|
|
|
|
if (mommag > mobj->tracer->hprev->extravalue1)
|
|
mommag = mobj->tracer->hprev->extravalue1;
|
|
|
|
if (mommag)
|
|
{
|
|
fixed_t *predict = VS_PredictAroundArena(mobj->tracer->tracer, mobj, mommag, mompoint, mobj->radius, forcegoaround, FRACUNIT);
|
|
P_MoveOrigin(mobj, predict[0], predict[1], mobj->z);
|
|
}
|
|
|
|
// Slight bonus lifting hazard for those who get too close!
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
|
{
|
|
if (!playeringame[i])
|
|
continue;
|
|
|
|
if (players[i].spectator)
|
|
continue;
|
|
|
|
if (P_MobjWasRemoved(players[i].mo))
|
|
continue;
|
|
|
|
if (players[i].mo->health == 0 || players[i].mo->hitlag)
|
|
continue;
|
|
|
|
if (players[i].mo->z > mobj->z)
|
|
continue;
|
|
|
|
if (FixedHypot(players[i].mo->x - mobj->x, players[i].mo->y - mobj->y) > (mobj->radius + 4*mapobjectscale))
|
|
continue;
|
|
|
|
players[i].mo->momz += 4*mapobjectscale;
|
|
}
|
|
}
|
|
}
|
|
else if (!mobj->tracer->hprev && mobj->movecount == TICRATE/3)
|
|
{
|
|
// SPAWN THE EGGBEATER
|
|
P_SetTarget(&mobj->tracer->hprev,
|
|
P_SpawnMobjFromMobj(mobj, 0, 0,
|
|
-mobjinfo[MT_BLENDEYE_EGGBEATER].height,
|
|
MT_BLENDEYE_EGGBEATER
|
|
)
|
|
);
|
|
P_SetTarget(&mobj->tracer->hprev->tracer, mobj);
|
|
mobj->tracer->hprev->extravalue1 = 0;
|
|
S_StartSound(NULL, sfx_cdfm39);
|
|
|
|
// Gainax sharpness
|
|
mobj_t *shwing = P_SpawnMobjFromMobj(mobj->tracer->hprev, 0, 0, 0, MT_OVERLAY);
|
|
shwing->threshold = OV_DONTSCREENOFFSET;
|
|
P_SetTarget(&shwing->target, mobj->tracer->hprev);
|
|
P_SetMobjState(shwing, S_EMERALDFLARE1);
|
|
shwing->fuse = (TICRATE - mobj->movecount);
|
|
shwing->spritexoffset = -24*FRACUNIT;
|
|
shwing->spriteyoffset = 14*FRACUNIT;
|
|
|
|
// Dr. Robontik's Rage Stagers ?!
|
|
mobj_t *angery = P_SpawnMobjFromMobj(mobj->tracer,
|
|
P_ReturnThrustX(mobj, mobj->angle, FRACUNIT),
|
|
P_ReturnThrustY(mobj, mobj->angle, FRACUNIT),
|
|
15*FRACUNIT,
|
|
MT_GHOST
|
|
);
|
|
|
|
P_SetMobjState(angery, S_SPIKEDLENS);
|
|
P_SetScale(angery, angery->scale/2);
|
|
angery->height = 1; // prevent z correction in P_MobjScaleThink
|
|
angery->destscale *= 4;
|
|
angery->fuse = TICRATE/7; // 5
|
|
angery->scalespeed = ((angery->destscale - angery->scale)/angery->fuse);
|
|
angery->colorized = true;
|
|
angery->color = EYECOLOR;
|
|
angery->flags2 |= MF2_BOSSNOTRAP; // Quicker fadeout
|
|
}
|
|
else if (mobj->movecount >= TICRATE && !deathwatch)
|
|
{
|
|
mobj->movedir = BLENDEYE_PINCH_WHISKING;
|
|
if (mobj->tracer)
|
|
{
|
|
P_SetMobjState(mobj->tracer, mobj->tracer->info->raisestate);
|
|
if (mobj->tracer->hprev)
|
|
P_SetMobjState(mobj->tracer->hprev, S_BLENDEYE_EGGBEATER_SPIN);
|
|
|
|
mobj->tracer->cusval = (encoremode ? 6 : 5);
|
|
}
|
|
S_StartSound(NULL, sfx_befan1);
|
|
mobj->flags2 |= MF2_AMBUSH;
|
|
}
|
|
if (mobj->z < mobj->extravalue1 - bobsize)
|
|
mobj->z = (2*mobj->z + mobj->extravalue1 + bob)/3;
|
|
else
|
|
mobj->z = mobj->extravalue1 + bob;
|
|
|
|
if (P_MobjWasRemoved(mobj->hprev))
|
|
;
|
|
else if (mobj->z <= mobj->extravalue1)
|
|
{
|
|
if ((mobj->hprev->state-states) != S_BLENDEYE_FLAME)
|
|
P_SetMobjState(mobj->hprev, S_BLENDEYE_FLAME);
|
|
|
|
if (!S_SoundPlaying(mobj->hprev, sfx_s3k7f))
|
|
S_StartSound(mobj->hprev, sfx_s3k7f);
|
|
}
|
|
else
|
|
{
|
|
if ((mobj->hprev->state-states) != S_INVISIBLE)
|
|
P_SetMobjState(mobj->hprev, S_INVISIBLE);
|
|
}
|
|
|
|
if (mobj->tracer && mobj->tracer->hprev)
|
|
{
|
|
if ((mobj->extravalue1 + bob) <= (mobj->floorz + mobj->tracer->hprev->height))
|
|
{
|
|
mobj->movedir = BLENDEYE_PINCH_DRILLING;
|
|
mobj->movecount = 0;
|
|
S_StopSoundByID(NULL, sfx_befan2);
|
|
S_StartSound(NULL, sfx_s3k85);
|
|
mobj->flags2 &= ~MF2_AMBUSH;
|
|
if (mobj->hprev)
|
|
P_SetMobjState(mobj->hprev, S_INVISIBLE);
|
|
mobj->tracer->hprev->flags &= ~MF_PAIN;
|
|
}
|
|
else if (deathwatch)
|
|
;
|
|
else if (mobj->tracer->cusval && !(mobj->movecount % (TICRATE)))
|
|
{
|
|
angle_t ang = FixedAngle(P_RandomKey(PR_MOVINGTARGET, 360)*FRACUNIT);
|
|
fixed_t dist = 2*FRACUNIT;
|
|
mobj_t *ammo = P_SpawnMobjFromMobj(mobj,
|
|
P_ReturnThrustX(mobj, ang, dist),
|
|
P_ReturnThrustY(mobj, ang, dist),
|
|
0,
|
|
MT_BLENDEYE_PUYO);
|
|
P_SetOrigin(ammo, ammo->x, ammo->y, ammo->floorz+1);
|
|
P_SetScale(ammo, (ammo->destscale *= 2));
|
|
ammo->angle = ang;
|
|
ammo->cvmem = dist;
|
|
ammo->movefactor = (TICRATE/2) + P_RandomKey(PR_MOVINGTARGET, TICRATE/8);
|
|
P_SetTarget(&ammo->tracer, mobj);
|
|
ammo->extravalue1 = mobj->floorz;
|
|
if (mobj->tracer->cusval & 1)
|
|
ammo->flags2 |= MF2_BOSSNOTRAP;
|
|
mobj->tracer->cusval--;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case BLENDEYE_PINCH_DRILLING:
|
|
{
|
|
// ACCIDENTIALLY DRILLING INTO THE GROUND
|
|
if (mobj->z > mobj->floorz+FRACUNIT)
|
|
{
|
|
tiltstrength = 10;
|
|
if (leveltime & 1)
|
|
tiltstrength = -tiltstrength;
|
|
mobj->rollangle = FixedAngle(tiltstrength*FRACUNIT);
|
|
mobj->angle -= (ANG10+ANG20);
|
|
mobj->z = (2*mobj->z + mobj->floorz)/3;
|
|
mobj->tracer->cusval = FixedAngle(P_RandomKey(PR_MOVINGTARGET, 360)*FRACUNIT);
|
|
if (!mobj->tracer->hprev)
|
|
;
|
|
else for (i = 0; i < 6; i++)
|
|
{
|
|
angle_t angle = mobj->tracer->cusval + FixedAngle(i*360*FRACUNIT/12);
|
|
fixed_t momx = P_ReturnThrustX(mobj, angle, mobj->tracer->hprev->radius);
|
|
fixed_t momy = P_ReturnThrustY(mobj, angle, mobj->tracer->hprev->radius);
|
|
mobj_t *dustmo = P_SpawnMobjFromMobj(
|
|
mobj->tracer->hprev,
|
|
0, 0, 0,
|
|
(encoremode
|
|
? MT_BLENDEYE_PUYO_DUST
|
|
: MT_BLENDEYE_PUYO_DUST_COFFEE
|
|
)
|
|
);
|
|
P_SetScale(dustmo, (dustmo->destscale *= 2));
|
|
dustmo->momx = momx/(3-(i & 1));
|
|
dustmo->momy = momy/(3-(i & 1));
|
|
dustmo->momz = (mobj->z - mobj->floorz)/(2+(i & 1));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mobj->z = mobj->floorz;
|
|
if ((mobj->tracer->hprev->state-states) != S_BLENDEYE_EGGBEATER)
|
|
{
|
|
mobj->rollangle = 0;
|
|
P_SetMobjState(mobj->tracer->hprev, S_BLENDEYE_EGGBEATER);
|
|
P_SetMobjState(mobj->tracer, mobj->tracer->info->spawnstate);
|
|
}
|
|
|
|
if ((mobj->movecount == TICRATE/2) && !deathwatch)
|
|
K_DeclareWeakspot(mobj, SPOT_WEAK, SKINCOLOR_EMERALD, false);
|
|
|
|
if ((mobj->movecount > 2*TICRATE) && (leveltime & 1))
|
|
{
|
|
if (mobj->hprev && (mobj->hprev->state-states) != S_BLENDEYE_FLAME)
|
|
{
|
|
P_SetMobjState(mobj->hprev, S_BLENDEYE_FLAME);
|
|
S_StartSound(NULL, sfx_s3k4f);
|
|
}
|
|
mobj->z += FRACUNIT;
|
|
if (!S_SoundPlaying(NULL, sfx_s3k69))
|
|
S_StartSound(NULL, sfx_s3k69);
|
|
}
|
|
|
|
if ((++mobj->movecount) == 3*TICRATE)
|
|
{
|
|
mobj->movedir = BLENDEYE_PINCH_BOBBING;
|
|
mobj->movecount = 0;
|
|
mobj->rollangle = 0;
|
|
mobj->extravalue1 = mobj->reactiontime;
|
|
S_StopSoundByID(NULL, sfx_s3k69); // it's like poetry
|
|
S_StartSound(NULL, sfx_mbs53);
|
|
if (mobj->tracer)
|
|
{
|
|
P_SetMobjState(mobj->tracer, mobj->tracer->info->spawnstate);
|
|
if (mobj->tracer->hprev && !(mobj->tracer->hprev->flags & MF_PAIN))
|
|
{
|
|
mobj->tracer->hprev->flags |= MF_PAIN;
|
|
mobj->tracer->hprev->extravalue1 = 0;
|
|
if (mobj->tracer->hprev->z != mobj->floorz)
|
|
;
|
|
else for (i = 0; i < 6; i++)
|
|
{
|
|
angle_t angle = mobj->tracer->cusval + FixedAngle(i*360*FRACUNIT/12);
|
|
fixed_t momx = P_ReturnThrustX(mobj, angle, mobj->tracer->hprev->radius);
|
|
fixed_t momy = P_ReturnThrustY(mobj, angle, mobj->tracer->hprev->radius);
|
|
mobj_t *dustmo = P_SpawnMobjFromMobj(
|
|
mobj->tracer->hprev,
|
|
0, 0, 0,
|
|
(encoremode
|
|
? MT_BLENDEYE_PUYO_DUST
|
|
: MT_BLENDEYE_PUYO_DUST_COFFEE
|
|
)
|
|
);
|
|
P_SetScale(dustmo, (dustmo->destscale *= 2));
|
|
dustmo->momx = momx/(3-(i & 1));
|
|
dustmo->momy = momy/(3-(i & 1));
|
|
dustmo->momz = (mobj->tracer->hprev->radius)/(2+(i & 1));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case BLENDEYE_PINCH_EXPLODING:
|
|
{
|
|
// SHE'S DYING...
|
|
if ((--mobj->movecount) == 0)
|
|
{
|
|
// ALL DONE
|
|
{
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
|
{
|
|
if (!playeringame[i])
|
|
continue;
|
|
if (players[i].spectator)
|
|
continue;
|
|
if (players[i].pflags & PF_NOCONTEST)
|
|
continue;
|
|
|
|
P_DoAllPlayersExit(0, true);
|
|
break;
|
|
}
|
|
}
|
|
|
|
mobj->movedir = BLENDEYE_PINCH_BLOWNUP;
|
|
|
|
fixed_t offs = P_RandomChance(PR_EXPLOSION, FRACUNIT/2)
|
|
? ANGLE_135
|
|
: InvAngle(ANGLE_135);
|
|
P_InstaThrust(mobj, mobj->angle + offs, 8*mapobjectscale);
|
|
mobj->momz = 16*mapobjectscale;
|
|
mobj->flags |= MF_NOCLIPHEIGHT;
|
|
mobj->flags &= ~MF_NOGRAVITY;
|
|
mobj->z++;
|
|
|
|
P_InstaThrust(mobj->tracer, mobj->angle, 8*mapobjectscale);
|
|
mobj->tracer->momz = 12*mapobjectscale;
|
|
mobj->tracer->flags |= MF_NOCLIPHEIGHT;
|
|
mobj->tracer->flags &= ~MF_NOGRAVITY;
|
|
mobj->tracer->z++;
|
|
|
|
if (mobj->tracer->hprev)
|
|
{
|
|
P_InstaThrust(mobj->tracer->hprev, mobj->angle - offs, 8*mapobjectscale);
|
|
mobj->tracer->hprev->momz = 6*mapobjectscale;
|
|
mobj->tracer->hprev->flags |= MF_NOCLIPHEIGHT;
|
|
mobj->tracer->hprev->flags &= ~MF_NOGRAVITY;
|
|
mobj->tracer->hprev->z++;
|
|
P_SetScale(mobj->hprev, mapobjectscale/4);
|
|
K_SpawnBlendEyeExplosion(mobj->hprev);
|
|
S_StartSound(NULL, sfx_s3k4e);
|
|
}
|
|
|
|
P_RemoveMobj(mobj->hprev);
|
|
P_SetTarget(&mobj->hprev, NULL);
|
|
}
|
|
else if ((mobj->movecount % 2) == 0)
|
|
{
|
|
// SPAWN A MINI-POP (2)
|
|
angle_t rot = FixedAngle(P_RandomKey(PR_EXPLOSION, 360)*FRACUNIT);
|
|
fixed_t offs = P_RandomKey(PR_EXPLOSION, (mobj->info->height - mobjinfo[MT_SONIC3KBOSSEXPLODE].height)/FRACUNIT)*FRACUNIT;
|
|
P_SpawnMobjFromMobj(mobj,
|
|
P_ReturnThrustX(mobj, rot, mobj->info->radius),
|
|
P_ReturnThrustY(mobj, rot, mobj->info->radius),
|
|
offs,
|
|
MT_SONIC3KBOSSEXPLODE);
|
|
S_StartSound(NULL, sfx_s3kb4);
|
|
tiltstrength = 5;
|
|
if (((mobj->movecount/2) % 2) == 0)
|
|
tiltstrength = -tiltstrength;
|
|
|
|
mobj->rollangle = FixedAngle(tiltstrength*FRACUNIT);
|
|
}
|
|
else
|
|
mobj->rollangle = 0;
|
|
|
|
return;
|
|
}
|
|
case BLENDEYE_PINCH_BLOWNUP:
|
|
{
|
|
// CURTAINS FOR YOU
|
|
if (mobj->z != mobj->floorz)
|
|
mobj->rollangle += ANG20;
|
|
else
|
|
mobj->rollangle = ANGLE_180;
|
|
|
|
if (mobj->tracer->hprev->z != mobj->floorz)
|
|
mobj->tracer->hprev->rollangle += ANG20;
|
|
else
|
|
mobj->tracer->hprev->rollangle = (mobj->tracer->hprev->rollangle >= ANGLE_180)
|
|
? ANGLE_135
|
|
: InvAngle(ANGLE_135);
|
|
|
|
mobj->renderflags ^= RF_DONTDRAW;
|
|
mobj->tracer->renderflags ^= RF_DONTDRAW;
|
|
if (mobj->tracer->hprev)
|
|
mobj->tracer->hprev->renderflags ^= RF_DONTDRAW;
|
|
|
|
return;
|
|
}
|
|
default:
|
|
{
|
|
// RESTART CYCLE
|
|
if (deathwatch)
|
|
;
|
|
else if ((--mobj->movecount) == 0)
|
|
{
|
|
mobj->movedir = BLENDEYE_LOADAMMO;
|
|
mobj->movecount = 6*5;
|
|
mobj->tracer->cusval = FixedAngle(P_RandomKey(PR_MOVINGTARGET, 360)*FRACUNIT);
|
|
}
|
|
else if (mobj->movecount == TICRATE)
|
|
{
|
|
mobj_t *ref = mobj->hprev;
|
|
while (ref)
|
|
{
|
|
if (ref->health > 0)
|
|
K_DeclareWeakspot(ref, SPOT_WEAK, SKINCOLOR_EMERALD, false);
|
|
ref = ref->hprev;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Look around.
|
|
if (mobj->target) // !deathwatch
|
|
{
|
|
INT32 angledelta = AngleDeltaSigned(R_PointToAngle2(mobj->x, mobj->y, mobj->target->x, mobj->target->y), mobj->angle)/4;
|
|
const INT32 maxdelta = (ANGLE_45/2);
|
|
|
|
if (angledelta > maxdelta)
|
|
angledelta = maxdelta;
|
|
else if (angledelta < -maxdelta)
|
|
angledelta = -maxdelta;
|
|
|
|
VS_BlendEye_Eye_Parts(mobj, angledelta);
|
|
}
|
|
}
|
|
|
|
boolean VS_BlendEye_Touched(mobj_t *special, mobj_t *toucher)
|
|
{
|
|
if (toucher->hitlag > 0)
|
|
return false;
|
|
|
|
fixed_t thrust = FixedHypot(toucher->momx, toucher->momy);
|
|
angle_t ang = R_PointToAngle2(special->x, special->y, toucher->x - toucher->momx, toucher->y - toucher->momy);
|
|
P_InstaThrust(toucher, ang + ANGLE_90, P_ReturnThrustX(toucher, ang + ANGLE_90, thrust));
|
|
thrust = P_ReturnThrustX(toucher, ang, thrust);
|
|
if (thrust < 4*mapobjectscale)
|
|
thrust = (4*mapobjectscale);
|
|
P_Thrust(toucher, ang, thrust);
|
|
return true;
|
|
}
|
|
|
|
void VS_BlendEye_Damage(mobj_t *mobj, mobj_t *inflictor, mobj_t *source, INT32 damage)
|
|
{
|
|
(void)inflictor;
|
|
|
|
S_StartSound(NULL, mobj->info->painsound);
|
|
K_UpdateBossHealthBar((mobj->health - damage)*(FRACUNIT/mobj->tracer->cvmem), 8);
|
|
if (mobj->tracer)
|
|
{
|
|
if (mobj->movedir == BLENDEYE_PINCH_BOBBING)
|
|
{
|
|
if (mobj->hprev)
|
|
P_SetMobjState(mobj->hprev, S_INVISIBLE);
|
|
}
|
|
else if (mobj->movedir == BLENDEYE_PINCH_WHISKING)
|
|
{
|
|
mobj->movedir = BLENDEYE_PINCH_BOBBING;
|
|
mobj->movecount = 0;
|
|
mobj->rollangle = 0;
|
|
mobj->flags2 &= ~MF2_AMBUSH;
|
|
mobj->extravalue1 = mobj->reactiontime;
|
|
if (mobj->hprev && (mobj->hprev->state-states) != S_BLENDEYE_FLAME)
|
|
{
|
|
P_SetMobjState(mobj->hprev, S_BLENDEYE_FLAME);
|
|
S_StartSound(NULL, sfx_s3k4f);
|
|
}
|
|
if (mobj->tracer->hprev)
|
|
{
|
|
P_SetMobjState(mobj->tracer->hprev, S_BLENDEYE_EGGBEATER);
|
|
mobj->tracer->hprev->flags |= MF_PAIN;
|
|
P_SetMobjState(mobj->tracer, mobj->tracer->info->spawnstate);
|
|
}
|
|
}
|
|
else if (mobj->movedir == BLENDEYE_PINCH_DRILLING)
|
|
{
|
|
if (mobj->tracer->hprev)
|
|
mobj->tracer->hprev->flags &= ~MF_PAIN;
|
|
|
|
mobj->movecount = (2*TICRATE)+(TICRATE/2);
|
|
P_SetMobjState(mobj->tracer, mobj->tracer->info->spawnstate);
|
|
}
|
|
else if ((mobj->health - damage) == mobj->tracer->health) //pre-pinch
|
|
{
|
|
mobj->flags2 &= ~MF2_AMBUSH;
|
|
mobj->rollangle = 0;
|
|
mobj->movedir = BLENDEYE_EXPLODING;
|
|
mobj->movecount = 2*TICRATE;
|
|
P_SetMobjState(mobj->tracer, mobj->tracer->info->spawnstate);
|
|
}
|
|
}
|
|
|
|
// make the glass stress
|
|
mobj_t *ref = mobj->hnext;
|
|
while (ref)
|
|
{
|
|
P_SetMobjState(ref, ref->info->painstate);
|
|
ref = ref->hnext;
|
|
}
|
|
|
|
if (source && source->player)
|
|
{
|
|
K_GivePointsToPlayer(source->player, NULL, 1);
|
|
}
|
|
}
|
|
|
|
void VS_BlendEye_Death(mobj_t *mobj)
|
|
{
|
|
mobj->flags2 &= ~MF2_AMBUSH;
|
|
mobj->rollangle = 0;
|
|
mobj->movedir = BLENDEYE_PINCH_EXPLODING;
|
|
mobj->movecount = 3*TICRATE/2;
|
|
|
|
if (mobj->tracer)
|
|
{
|
|
P_SetMobjState(mobj->tracer, mobj->tracer->info->spawnstate);
|
|
mobj->tracer->flags |= MF_NOCLIP|MF_NOCLIPTHING;
|
|
|
|
if (mobj->hprev)
|
|
mobj->tracer->hprev->flags |= MF_NOCLIP|MF_NOCLIPTHING;
|
|
|
|
if (mobj->tracer->hprev)
|
|
{
|
|
P_SetMobjState(mobj->tracer->hprev, S_BLENDEYE_EGGBEATER);
|
|
}
|
|
}
|
|
|
|
if (mobj->hprev)
|
|
P_SetMobjState(mobj->hprev, S_INVISIBLE);
|
|
|
|
mobj->flags |= MF_NOCLIP|MF_NOCLIPTHING;
|
|
|
|
K_AddHitLag(mobj, 6, true);
|
|
}
|
|
|
|
/// - AUXILLIARY OBJECTS - ///
|
|
|
|
boolean VS_BlendEye_Eye_Thinker(mobj_t *mobj)
|
|
{
|
|
if (P_MobjWasRemoved(mobj->target))
|
|
{
|
|
P_RemoveMobj(mobj);
|
|
return false;
|
|
}
|
|
|
|
if (mobj->target->hitlag)
|
|
{
|
|
P_InstaThrust(mobj, mobj->angle, 25*mobj->scale);
|
|
K_AddHitLag(mobj, mobj->target->hitlag, (mobj->target->eflags & MFE_DAMAGEHITLAG) == MFE_DAMAGEHITLAG);
|
|
return false;
|
|
}
|
|
|
|
if (!(mobj->flags & MF_NOCLIPHEIGHT))
|
|
{
|
|
mobj->momx = mobj->momy = 0;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void VS_BlendEye_Glass_Death(mobj_t *mobj)
|
|
{
|
|
UINT8 i = 0;
|
|
fixed_t thrust = 30*mapobjectscale;
|
|
for (i = 0; i <= 8; i++)
|
|
{
|
|
fixed_t along = P_RandomRange(PR_DECORATION, -mobj->radius/FRACUNIT, mobj->radius/FRACUNIT)*FRACUNIT;
|
|
fixed_t up = (mobj->height - FixedMul(mobjinfo[MT_BATTLEBUMPER_DEBRIS].height, mobj->scale));
|
|
if (up > 0)
|
|
up = P_RandomKey(PR_DECORATION, mobj->height/FRACUNIT)*FRACUNIT;
|
|
mobj_t *glassmo = P_SpawnMobj(
|
|
mobj->x + P_ReturnThrustX(mobj, mobj->angle, along),
|
|
mobj->y + P_ReturnThrustY(mobj, mobj->angle, along),
|
|
mobj->z + up,
|
|
MT_BATTLEBUMPER_DEBRIS);
|
|
glassmo->color = SKINCOLOR_WHITE;
|
|
glassmo->renderflags |= RF_ADD;
|
|
angle_t vang = R_PointToAngle2(0, mobj->z, 39*FRACUNIT, glassmo->z);
|
|
P_InstaThrust(glassmo, mobj->angle + ANGLE_90, P_ReturnThrustX(glassmo, vang, thrust));
|
|
glassmo->momz = P_ReturnThrustX(glassmo, vang, thrust);
|
|
}
|
|
}
|
|
|
|
void VS_BlendEye_Eggbeater_Touched(mobj_t *t1, mobj_t *t2)
|
|
{
|
|
if (t2->hitlag || (t1->health <= 0 || t2->health <= 0))
|
|
return;
|
|
|
|
if (t1->z == t1->floorz)
|
|
return;
|
|
|
|
INT32 minextravalue1 = -(P_IsObjectOnGround(t2) ? 5 : 15)*t1->scale;
|
|
if (t1->extravalue1 > minextravalue1)
|
|
t1->extravalue1 = minextravalue1;
|
|
|
|
if ((t2->player->tumbleBounces > 0) && (t2->momz > 0))
|
|
return;
|
|
|
|
fixed_t thrust = FixedHypot(t2->momx, t2->momy);
|
|
if (thrust < 20*mapobjectscale)
|
|
thrust = 20*mapobjectscale;
|
|
|
|
P_Thrust(
|
|
t2,
|
|
R_PointToAngle2(t1->x, t1->y, t2->x - t2->momx, t2->y - t2->momy),
|
|
thrust
|
|
);
|
|
}
|
|
|
|
void VS_BlendEye_Generator_DeadThinker(mobj_t *mobj)
|
|
{
|
|
if (leveltime & 1)
|
|
return;
|
|
|
|
if (P_RandomChance(PR_DECORATION, FRACUNIT/4))
|
|
return;
|
|
|
|
angle_t twist = mobj->angle;
|
|
fixed_t dist = 18*FRACUNIT;
|
|
fixed_t z = 26*FRACUNIT;
|
|
fixed_t fb = 6*FRACUNIT;
|
|
|
|
if (P_RandomChance(PR_DECORATION, FRACUNIT/2))
|
|
{
|
|
z = 42*FRACUNIT;
|
|
dist = 28*FRACUNIT;
|
|
twist += ANGLE_180;
|
|
fb = -6*FRACUNIT;
|
|
}
|
|
|
|
if ((mobj->state-states) != S_BLENDEYE_GENERATOR_BUSTED_R)
|
|
twist += ANGLE_180;
|
|
|
|
mobj_t *dust = P_SpawnMobjFromMobj(
|
|
mobj,
|
|
P_ReturnThrustX(mobj, twist, dist) + P_ReturnThrustY(mobj, twist, fb),
|
|
P_ReturnThrustY(mobj, twist, dist) + P_ReturnThrustX(mobj, twist, fb),
|
|
z,
|
|
encoremode
|
|
? MT_BLENDEYE_PUYO_DUST
|
|
: MT_BLENDEYE_PUYO_DUST_COFFEE
|
|
);
|
|
dust->angle = FixedAngle(P_RandomKey(PR_DECORATION, 360)*FRACUNIT);
|
|
P_InstaThrust(dust, dust->angle, 4*mapobjectscale);
|
|
dust->momz = 8*mapobjectscale;
|
|
}
|
|
|
|
/// - PUYO HAZARDS - ///
|
|
|
|
boolean VS_PuyoTouched(mobj_t *special, mobj_t *toucher)
|
|
{
|
|
if (!special->health || !toucher->health)
|
|
return false; // too dead
|
|
|
|
if (special->state-states < S_BLENDEYE_PUYO)
|
|
return false; // too small
|
|
|
|
P_DamageMobj(toucher, special, special, 1, DMG_NORMAL);
|
|
|
|
special->momx = 0;
|
|
special->momy = 0;
|
|
special->momz = 0;
|
|
|
|
return true;
|
|
}
|
|
|
|
// Basically a duplication of A_Boss5Jump. I'm not adding A_ action calls to header files.
|
|
|
|
static void VS_PuyoJump(mobj_t *shot, mobj_t *target)
|
|
{
|
|
fixed_t v; // Velocity to jump at
|
|
fixed_t a1, a2, aToUse; // Velocity squared
|
|
fixed_t g; // Gravity
|
|
fixed_t x; // Horizontal difference
|
|
INT32 x_int; // x! But in integer form!
|
|
fixed_t y; // Vertical difference (yes that's normally z in SRB2 shut up)
|
|
INT32 y_int; // y! But in integer form!
|
|
INT32 intHypotenuse; // x^2 + y^2. Frequently overflows fixed point, hence why we need integers proper.
|
|
fixed_t fixedHypotenuse; // However, we can work around that and still get a fixed-point number.
|
|
angle_t theta; // Angle of attack
|
|
// INT32 locvar1 = var1;
|
|
// INT32 locvar2 = var2;
|
|
|
|
if (!shot || !target)
|
|
return; // Don't even bother if we've got nothing to aim at.
|
|
|
|
// Scale with map
|
|
g = FixedMul(gravity, mapobjectscale);
|
|
|
|
// Look up distance between shot and its target
|
|
x = FixedHypot(target->x - shot->x, target->y - shot->y);
|
|
// Look up height difference between shot and its target
|
|
y = target->z - shot->z;
|
|
|
|
// Get x^2 + y^2. Have to do it in a roundabout manner, because this overflows fixed_t way too easily otherwise.
|
|
x_int = x>>FRACBITS;
|
|
y_int = y>>FRACBITS;
|
|
intHypotenuse = (x_int*x_int) + (y_int*y_int);
|
|
fixedHypotenuse = FixedSqrt(intHypotenuse) *256;
|
|
|
|
// a = g(y+/-sqrt(x^2+y^2)). a1 can be +, a2 can be -.
|
|
a1 = FixedMul(g,y+fixedHypotenuse);
|
|
a2 = FixedMul(g,y-fixedHypotenuse);
|
|
|
|
// Determine which one isn't actually an imaginary number (or the smaller of the two, if both are real), and use that for v.
|
|
if (a1 < 0 || a2 < 0)
|
|
{
|
|
if (a1 < 0 && a2 < 0)
|
|
{
|
|
//Somehow, v^2 is negative in both cases. v is therefore imaginary and something is horribly wrong. Abort!
|
|
return;
|
|
}
|
|
// Just find which one's NOT negative, and use that
|
|
aToUse = max(a1,a2);
|
|
}
|
|
else
|
|
{
|
|
// Both are positive; use whichever's smaller so it can decay faster
|
|
aToUse = min(a1,a2);
|
|
}
|
|
v = FixedSqrt(aToUse);
|
|
// Okay, so we know the velocity. Let's actually find theta.
|
|
// We can cut the "+/- sqrt" part out entirely, since v was calculated specifically for it to equal zero. So:
|
|
//theta = tantoangle[FixedDiv(aToUse,FixedMul(g,x)) >> DBITS];
|
|
theta = tantoangle[SlopeDiv(aToUse,FixedMul(g,x))];
|
|
|
|
// Okay, complicated math done. Let's make this object jump already.
|
|
|
|
shot->angle = R_PointToAngle2(shot->x, shot->y, target->x, target->y);
|
|
|
|
if (shot->eflags & MFE_VERTICALFLIP)
|
|
shot->z--;
|
|
else
|
|
shot->z++;
|
|
|
|
// Horizontal axes first. First parameter is initial horizontal impulse, second is to correct its angle.
|
|
fixedHypotenuse = FixedMul(v, FINECOSINE(theta >> ANGLETOFINESHIFT)); // variable reuse
|
|
shot->momx = FixedMul(fixedHypotenuse, FINECOSINE(shot->angle >> ANGLETOFINESHIFT));
|
|
shot->momy = FixedMul(fixedHypotenuse, FINESINE(shot->angle >> ANGLETOFINESHIFT));
|
|
// Then the vertical axis. No angle-correction needed here.
|
|
shot->momz = FixedMul(v, FINESINE(theta >> ANGLETOFINESHIFT));
|
|
// I hope that's all that's needed, ugh
|
|
}
|
|
|
|
static mobj_t *VS_PredictedPuyoShot(mobj_t *arena, mobj_t *source, mobj_t *shot, mobj_t *target, mobj_t *reticule)
|
|
{
|
|
fixed_t x = 0, y = 0, z = INT32_MIN;
|
|
fixed_t momx = 0, momy = 0, mommag = 0;
|
|
angle_t mompoint = 0;
|
|
|
|
if (P_MobjWasRemoved(arena))
|
|
{
|
|
CONS_Alert(CONS_ERROR, "VS_PredictedPuyoShot: No Versus arena provided.");
|
|
return NULL;
|
|
}
|
|
|
|
// handle coords
|
|
if (P_MobjWasRemoved(target) == false) // aimed leading shot
|
|
{
|
|
x = target->x;
|
|
y = target->y;
|
|
z = target->floorz;
|
|
|
|
if (P_MobjWasRemoved(shot) == false
|
|
&& (target->player == NULL || P_PlayerInPain(target->player) == false))
|
|
{
|
|
// a beyond mediocre guess - intentionally not DIRECTLY tied to maxarena because
|
|
// this was determined via trial and error and shouldn't change
|
|
fixed_t possiblearcmultiplier = (
|
|
((TICRATE/4)*FRACUNIT)
|
|
+ (3*TICRATE*
|
|
FixedDiv(
|
|
FixedHypot(x - shot->x, y - shot->y),
|
|
2*280*FRACUNIT
|
|
)
|
|
)
|
|
);
|
|
|
|
momx = FixedMul(target->momx, possiblearcmultiplier);
|
|
momy = FixedMul(target->momy, possiblearcmultiplier);
|
|
|
|
mommag = FixedHypot(momx, momy);
|
|
mompoint = R_PointToAngle2(0, 0, momx, momy);
|
|
|
|
fixed_t *predict = VS_PredictAroundArena(
|
|
arena,
|
|
target,
|
|
mommag,
|
|
mompoint,
|
|
shot->radius,
|
|
false,
|
|
0
|
|
);
|
|
|
|
momx = predict[0] - x;
|
|
momy = predict[1] - y;
|
|
}
|
|
}
|
|
else // unaimed random shot
|
|
{
|
|
fixed_t *predict = VS_RandomPointOnArena(
|
|
arena,
|
|
(P_MobjWasRemoved(shot) ? 0 : shot->radius)
|
|
);
|
|
|
|
x = predict[0];
|
|
y = predict[1];
|
|
}
|
|
|
|
// handle reticule
|
|
fixed_t tempz = ((z == INT32_MIN && !P_MobjWasRemoved(source)) ? source->z : z);
|
|
|
|
if (P_MobjWasRemoved(reticule) == false)
|
|
{
|
|
P_SetOrigin(reticule, x, y, tempz);
|
|
}
|
|
else
|
|
{
|
|
reticule = P_SpawnMobj(x, y, tempz, MT_SPIKEDTARGET);
|
|
reticule->renderflags |= RF_NOSPLATBILLBOARD;
|
|
reticule->destscale = 2*reticule->destscale;
|
|
reticule->radius = FixedMul(mobjinfo[MT_PLAYER].radius, mapobjectscale)/2;
|
|
//P_SetScale(reticule, reticule->destscale); -- intentionally not here, for animation
|
|
|
|
if (P_MobjWasRemoved(shot) == false)
|
|
P_SetTarget(&reticule->target, shot);
|
|
else if (P_MobjWasRemoved(source) == false)
|
|
P_SetTarget(&reticule->target, source);
|
|
}
|
|
|
|
if (z == INT32_MIN)
|
|
{
|
|
z = reticule->old_z = reticule->floorz;
|
|
}
|
|
|
|
reticule->z = z + FixedMul(mobjinfo[MT_PLAYER].height, mapobjectscale);
|
|
|
|
// handle reticle movement
|
|
if (momx != 0 || momy != 0)
|
|
{
|
|
UINT8 attempt = 0;
|
|
boolean lastsuccess = true;
|
|
// tries to immediately jump to the final location.
|
|
// if that fails, tries to xeno's paradox it:
|
|
// halve the distance and try TWO steps at this magnitude
|
|
while (attempt < 5)
|
|
{
|
|
if (P_TryMove(reticule, reticule->x + momx, reticule->y + momy, false, NULL))
|
|
{
|
|
if (lastsuccess)
|
|
break;
|
|
|
|
lastsuccess = true;
|
|
continue;
|
|
}
|
|
lastsuccess = false;
|
|
momx /= 2;
|
|
momy /= 2;
|
|
attempt++;
|
|
}
|
|
}
|
|
|
|
// handle launching
|
|
if (P_MobjWasRemoved(shot) == false)
|
|
{
|
|
P_SetTarget(&shot->tracer, reticule);
|
|
|
|
VS_PuyoJump(shot, reticule);
|
|
|
|
P_Thrust(shot, shot->angle, 3*mapobjectscale); // needs this little extra kick just to make it, for some reason
|
|
shot->momz *= PUYOARCMULTIPLIER;
|
|
shot->flags |= (MF_NOGRAVITY|MF_SHOOTABLE);
|
|
if (P_RandomChance(PR_DECORATION, FRACUNIT/2))
|
|
shot->flags2 |= MF2_BOSSNOTRAP;
|
|
}
|
|
|
|
// handle finalisation
|
|
P_SetOrigin(reticule, reticule->x, reticule->y, z);
|
|
return reticule;
|
|
}
|
|
|
|
static mobj_t *referencepuyo = NULL;
|
|
static mobj_t *bestpuyo = NULL;
|
|
fixed_t bestpuyodist = INT32_MAX;
|
|
|
|
static inline BlockItReturn_t PIT_GetBestLaunchablePuyo(mobj_t *thing)
|
|
{
|
|
if (thing->type != MT_BLENDEYE_PUYO)
|
|
return BMIT_CONTINUE; // not a puyo
|
|
|
|
if (thing->cusval > 0)
|
|
return BMIT_CONTINUE; // a bottom puyo
|
|
|
|
if (P_MobjWasRemoved(thing->tracer))
|
|
return BMIT_CONTINUE; // a launched puyo
|
|
|
|
fixed_t dist = FixedHypot(referencepuyo->x - thing->x, referencepuyo->y - thing->y);
|
|
|
|
if (dist >= bestpuyodist)
|
|
return BMIT_CONTINUE; // too far away
|
|
|
|
bestpuyo = thing;
|
|
bestpuyodist = dist;
|
|
|
|
return BMIT_CONTINUE; // Still could be a better selection
|
|
}
|
|
|
|
static void VS_FindBestPuyo(mobj_t *reference, mobj_t *source)
|
|
{
|
|
INT32 bx, by, xl, xh, yl, yh;
|
|
|
|
referencepuyo = reference;
|
|
bestpuyo = NULL;
|
|
bestpuyodist = INT32_MAX;
|
|
|
|
yh = (unsigned)(source->y + (source->radius + MAXRADIUS) - bmaporgy)>>MAPBLOCKSHIFT;
|
|
yl = (unsigned)(source->y - (source->radius + MAXRADIUS) - bmaporgy)>>MAPBLOCKSHIFT;
|
|
xh = (unsigned)(source->x + (source->radius + MAXRADIUS) - bmaporgx)>>MAPBLOCKSHIFT;
|
|
xl = (unsigned)(source->x - (source->radius + MAXRADIUS) - bmaporgx)>>MAPBLOCKSHIFT;
|
|
|
|
BMBOUNDFIX (xl, xh, yl, yh);
|
|
|
|
for (by = yl; by <= yh; by++)
|
|
for (bx = xl; bx <= xh; bx++)
|
|
P_BlockThingsIterator(bx, by, PIT_GetBestLaunchablePuyo);
|
|
}
|
|
|
|
void VS_PuyoThinker(mobj_t *mobj)
|
|
{
|
|
if (!mobj->health)
|
|
return;
|
|
|
|
if (mobj->z <= mobj->floorz)
|
|
{
|
|
if (mobj->flags & MF_NOCLIPHEIGHT)
|
|
{
|
|
mobj->z = mobj->floorz;
|
|
mobj->momx = mobj->momy = mobj->momz = 0;
|
|
mobj->flags &= ~(MF_NOCLIPHEIGHT);
|
|
//P_KillMobj(mobj);
|
|
P_SetMobjState(mobj, S_BLENDEYE_PUYO_LAND_1);
|
|
mobj->fuse = 20*TICRATE;
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (P_MobjWasRemoved(mobj->tracer) || (mobj->tracer->type == MT_SPIKEDTARGET)) // being thrown...
|
|
{
|
|
if (mobj->flags2 & MF2_BOSSNOTRAP)
|
|
mobj->rollangle -= ANG10;
|
|
else
|
|
mobj->rollangle += ANG10;
|
|
|
|
mobj->momz -= (PUYOARCMULTIPLIER*FixedMul(gravity, mapobjectscale));
|
|
}
|
|
else if (mobj->tracer->flags2 & MF2_AMBUSH) // being whirled
|
|
{
|
|
if (mobj->movefactor)
|
|
{
|
|
if (mobj->movefactor > 2 || ((mobj->state-states) == S_BLENDEYE_PUYO))
|
|
{
|
|
if ((--mobj->movefactor) == 0)
|
|
P_SetMobjState(mobj, S_BLENDEYE_PUYO_SHOCK);
|
|
}
|
|
}
|
|
else if (mobj->cusval > 0) // popping and throwing the object on top of them
|
|
{
|
|
if ((--mobj->cusval) == 1)
|
|
{
|
|
P_InstaThrust(mobj, mobj->angle + ANGLE_90, 5*FRACUNIT);
|
|
mobj->momz = 6*FRACUNIT;
|
|
|
|
VS_FindBestPuyo(mobj, mobj->tracer);
|
|
if (bestpuyo != NULL)
|
|
{
|
|
VS_PredictedPuyoShot(
|
|
(P_MobjWasRemoved(mobj->tracer->tracer) == false
|
|
? mobj->tracer->tracer->tracer
|
|
: NULL
|
|
),
|
|
mobj->tracer,
|
|
bestpuyo,
|
|
mobj->tracer->target,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
P_KillMobj(mobj, mobj->tracer, mobj->tracer, DMG_NORMAL);
|
|
return;
|
|
}
|
|
}
|
|
fixed_t x = mobj->tracer->x, y = mobj->tracer->y, z = mobj->z+1;
|
|
fixed_t dist = mobj->cvmem;
|
|
|
|
if (mobj->extravalue1 == mobj->tracer->extravalue1 || mobj->cvmem == mobj->tracer->cvmem)
|
|
;
|
|
else if (mobj->z >= mobj->tracer->extravalue1)
|
|
dist = mobj->tracer->cvmem;
|
|
else
|
|
dist = mobj->cvmem + FixedMul(FixedDiv(mobj->z - mobj->extravalue1, mobj->tracer->extravalue1 - mobj->extravalue1), mobj->tracer->cvmem - mobj->cvmem);
|
|
|
|
mobj->angle += FixedAngle(((3*TICRATE/4) - mobj->movefactor)*FRACUNIT);
|
|
x += P_ReturnThrustX(mobj, mobj->angle, dist/2);
|
|
y += P_ReturnThrustY(mobj, mobj->angle, dist/2);
|
|
if (mobj->flags2 & MF2_STRONGBOX)
|
|
{
|
|
z = mobj->extravalue1+1;
|
|
if (((leveltime & 1) == 1) != (mobj->cusval > 0))
|
|
z += mobj->scale;
|
|
}
|
|
else // being thrown off the side of pinch mode
|
|
{
|
|
if (mobj->z >= mobj->tracer->extravalue1)
|
|
{
|
|
mobj_t *target = ((mobj->flags2 & MF2_BOSSNOTRAP) == 0)
|
|
? mobj->tracer->target
|
|
: NULL;
|
|
mobj->flags2 &= ~MF2_BOSSNOTRAP;
|
|
VS_PredictedPuyoShot(
|
|
(P_MobjWasRemoved(mobj->tracer->tracer) == false
|
|
? mobj->tracer->tracer->tracer
|
|
: NULL
|
|
),
|
|
mobj->tracer,
|
|
mobj,
|
|
target,
|
|
NULL
|
|
);
|
|
S_StartSound(NULL, sfx_mbs5b);
|
|
return;
|
|
}
|
|
z += mobj->scale;
|
|
}
|
|
P_MoveOrigin(mobj, x, y, z);
|
|
mobj->flags |= MF_NOGRAVITY;
|
|
}
|
|
else // being inserted into the blender
|
|
{
|
|
if (mobj->momz < 0)
|
|
{
|
|
if (mobj->z < mobj->extravalue1)
|
|
{
|
|
mobj->momz = 0;
|
|
mobj->flags |= MF_NOGRAVITY;
|
|
mobj->z = mobj->extravalue1;
|
|
S_StartSound(NULL, sfx_mbs42);
|
|
P_SetMobjState(mobj, S_BLENDEYE_PUYO_LAND_1);
|
|
}
|
|
}
|
|
else if (mobj->z > mobj->extravalue1)
|
|
mobj->flags &= ~MF_NOGRAVITY;
|
|
|
|
if (
|
|
(mobj->state-states) != S_BLENDEYE_PUYO_SHOCK
|
|
&& mobj->tracer->movedir == BLENDEYE_EXPLODING
|
|
&& (
|
|
(mobj->tracer->movecount == 3)
|
|
|| (
|
|
mobj->tracer->movecount == 4
|
|
&& P_RandomChance(PR_DECORATION, FRACUNIT/2)
|
|
)
|
|
)
|
|
)
|
|
{
|
|
P_SetMobjState(mobj, S_BLENDEYE_PUYO_SHOCK);
|
|
}
|
|
}
|
|
mobj->sprite = mobj->movedir;
|
|
}
|
|
|
|
void VS_PuyoDeath(mobj_t *mobj)
|
|
{
|
|
mobjtype_t dusttype = (encoremode ? MT_BLENDEYE_PUYO_DUST : MT_BLENDEYE_PUYO_DUST_COFFEE);
|
|
UINT8 i;
|
|
fixed_t momx, momy;
|
|
mobj_t *dustmo;
|
|
|
|
mobj->renderflags &= ~RF_DONTDRAW;
|
|
mobj->rollangle = 0;
|
|
|
|
mobj->angle = FixedAngle(P_RandomKey(PR_DECORATION, 360)*FRACUNIT);
|
|
for (i = 0; i <= 2; i++)
|
|
{
|
|
momx = P_ReturnThrustX(mobj, mobj->angle, 3*mobj->scale);
|
|
momy = P_ReturnThrustY(mobj, mobj->angle, 3*mobj->scale);
|
|
|
|
dustmo = P_SpawnMobjFromMobj(mobj, 0, 0, 0, dusttype);
|
|
dustmo->momx = mobj->momx + momx;
|
|
dustmo->momy = mobj->momy + momy;
|
|
dustmo->momz = mobj->momz + 4*mobj->scale;
|
|
dustmo->movedir = dustmo->sprite = mobj->movedir;
|
|
|
|
dustmo = P_SpawnMobjFromMobj(mobj, 0, 0, 0, dusttype);
|
|
dustmo->momx = mobj->momx - momx;
|
|
dustmo->momy = mobj->momy - momy;
|
|
dustmo->momz = mobj->momz - 4*mobj->scale;
|
|
dustmo->movedir = dustmo->sprite = mobj->movedir;
|
|
|
|
mobj->angle += ANGLE_135;
|
|
}
|
|
S_StartSound(NULL, ((mobj->tracer && mobj->tracer->type != MT_SPIKEDTARGET) ? sfx_mbs4c : sfx_mbs45));
|
|
}
|