Merge branch 'metalrecording' into 'master'

Metal battle, recording, and playback improvements

Closes #283 and #215

See merge request STJr/SRB2Internal!431
This commit is contained in:
MascaraSnake 2019-11-12 17:49:53 -05:00
commit c5ea20a699
16 changed files with 684 additions and 313 deletions

View file

@ -2491,7 +2491,7 @@ static void CL_RemovePlayer(INT32 playernum, INT32 reason)
void CL_Reset(void)
{
if (metalrecording)
G_StopMetalRecording();
G_StopMetalRecording(false);
if (metalplayback)
G_StopMetalDemo();
if (demorecording)

View file

@ -1211,9 +1211,9 @@ static void SendNameAndColor(void)
players[consoleplayer].mo->color = players[consoleplayer].skincolor;
if (metalrecording)
{ // Metal Sonic is Sonic, obviously.
SetPlayerSkinByNum(consoleplayer, 0);
CV_StealthSet(&cv_skin, skins[0].name);
{ // Starring Metal Sonic as themselves, obviously.
SetPlayerSkinByNum(consoleplayer, 5);
CV_StealthSet(&cv_skin, skins[5].name);
}
else if ((foundskin = R_SkinAvailable(cv_skin.string)) != -1 && R_SkinUsable(consoleplayer, foundskin))
{

View file

@ -5375,25 +5375,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_CYBRAKDEMONVILEEXPLOSION3",
// Metal Sonic (Race)
// S_PLAY_STND
"S_METALSONIC_STAND",
// S_PLAY_TAP1
"S_METALSONIC_WAIT1",
"S_METALSONIC_WAIT2",
// S_PLAY_WALK
"S_METALSONIC_WALK1",
"S_METALSONIC_WALK2",
"S_METALSONIC_WALK3",
"S_METALSONIC_WALK4",
"S_METALSONIC_WALK5",
"S_METALSONIC_WALK6",
"S_METALSONIC_WALK7",
"S_METALSONIC_WALK8",
// S_PLAY_SPD1
"S_METALSONIC_RUN1",
"S_METALSONIC_RUN2",
"S_METALSONIC_RUN3",
"S_METALSONIC_RUN4",
"S_METALSONIC_RACE",
// Metal Sonic (Battle)
"S_METALSONIC_FLOAT",
"S_METALSONIC_VECTOR",

View file

@ -289,7 +289,7 @@ static struct {
// There is no conflict here.
typedef struct demoghost {
UINT8 checksum[16];
UINT8 *buffer, *p, color;
UINT8 *buffer, *p, color, fadein;
UINT16 version;
mobj_t oldmo, *mo;
struct demoghost *next;
@ -2755,6 +2755,8 @@ void G_DoReborn(INT32 playernum)
LUAh_MapChange(gamemap);
#endif
G_DoLoadLevel(true);
if (metalrecording)
G_BeginMetal();
return;
}
}
@ -2922,7 +2924,7 @@ boolean G_GametypeUsesLives(void)
{
// Coop, Competitive
if ((gametype == GT_COOP || gametype == GT_COMPETITION)
&& !modeattacking // No lives in Time Attack
&& !(modeattacking || metalrecording) // No lives in Time Attack
//&& !G_IsSpecialStage(gamemap)
&& !(maptol & TOL_NIGHTS)) // No lives in NiGHTS
return true;
@ -3053,7 +3055,7 @@ static void G_DoCompleted(void)
if (metalplayback)
G_StopMetalDemo();
if (metalrecording)
G_StopMetalRecording();
G_StopMetalRecording(false);
for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i])
@ -4049,7 +4051,7 @@ char *G_BuildMapTitle(INT32 mapnum)
// DEMO RECORDING
//
#define DEMOVERSION 0x000a
#define DEMOVERSION 0x000c
#define DEMOHEADER "\xF0" "SRB2Replay" "\x0F"
#define DF_GHOST 0x01 // This demo contains ghost data too!
@ -4065,6 +4067,8 @@ char *G_BuildMapTitle(INT32 mapnum)
#define ZT_BUTTONS 0x08
#define ZT_AIMING 0x10
#define DEMOMARKER 0x80 // demoend
#define METALDEATH 0x44
#define METALSNICE 0x69
static ticcmd_t oldcmd;
@ -4073,7 +4077,6 @@ static ticcmd_t oldcmd;
#define GZT_MOMXY 0x02
#define GZT_MOMZ 0x04
#define GZT_ANGLE 0x08
// Not used for Metal Sonic
#define GZT_FRAME 0x10 // Animation frame
#define GZT_SPR2 0x20 // Player animations
#define GZT_EXTRA 0x40
@ -4089,7 +4092,15 @@ static ticcmd_t oldcmd;
#define EZT_SCALE 0x10 // Changed size
#define EZT_HIT 0x20 // Damaged a mobj
#define EZT_SPRITE 0x40 // Changed sprite set completely out of PLAY (NiGHTS, SOCs, whatever)
// spare EZT slot 0x80
#define EZT_HEIGHT 0x80 // Changed height
// GZT_FOLLOW flags
#define FZT_SPAWNED 0x01 // just been spawned
#define FZT_SKIN 0x02 // has skin
#define FZT_LINKDRAW 0x04 // has linkdraw (combine with spawned only)
#define FZT_COLORIZED 0x08 // colorized (ditto)
#define FZT_SCALE 0x10 // different scale to object
// spare FZT slots 0x20 to 0x80
static mobj_t oldmetal, oldghost;
@ -4215,28 +4226,28 @@ void G_WriteDemoTiccmd(ticcmd_t *cmd, INT32 playernum)
void G_GhostAddThok(void)
{
if (!demorecording || !(demoflags & DF_GHOST))
if (!metalrecording && (!demorecording || !(demoflags & DF_GHOST)))
return;
ghostext.flags = (ghostext.flags & ~EZT_THOKMASK) | EZT_THOK;
}
void G_GhostAddSpin(void)
{
if (!demorecording || !(demoflags & DF_GHOST))
if (!metalrecording && (!demorecording || !(demoflags & DF_GHOST)))
return;
ghostext.flags = (ghostext.flags & ~EZT_THOKMASK) | EZT_SPIN;
}
void G_GhostAddRev(void)
{
if (!demorecording || !(demoflags & DF_GHOST))
if (!metalrecording && (!demorecording || !(demoflags & DF_GHOST)))
return;
ghostext.flags = (ghostext.flags & ~EZT_THOKMASK) | EZT_REV;
}
void G_GhostAddFlip(void)
{
if (!demorecording || !(demoflags & DF_GHOST))
if (!metalrecording && (!demorecording || !(demoflags & DF_GHOST)))
return;
ghostext.flags |= EZT_FLIP;
}
@ -4256,7 +4267,7 @@ void G_GhostAddColor(ghostcolor_t color)
void G_GhostAddScale(fixed_t scale)
{
if (!demorecording || !(demoflags & DF_GHOST))
if (!metalrecording && (!demorecording || !(demoflags & DF_GHOST)))
return;
if (ghostext.lastscale == scale)
{
@ -4282,6 +4293,7 @@ void G_WriteGhostTic(mobj_t *ghost)
char ziptic = 0;
UINT8 *ziptic_p;
UINT32 i;
fixed_t height;
if (!demo_p)
return;
@ -4371,6 +4383,12 @@ void G_WriteGhostTic(mobj_t *ghost)
ghostext.flags |= EZT_SPRITE;
}
if ((height = FixedDiv(ghost->height, ghost->scale)) != oldghost.height)
{
oldghost.height = height;
ghostext.flags |= EZT_HEIGHT;
}
if (ghostext.flags)
{
ziptic |= GZT_EXTRA;
@ -4410,26 +4428,60 @@ void G_WriteGhostTic(mobj_t *ghost)
ghostext.hitlist = NULL;
}
if (ghostext.flags & EZT_SPRITE)
WRITEUINT8(demo_p,oldghost.sprite);
WRITEUINT16(demo_p,oldghost.sprite);
if (ghostext.flags & EZT_HEIGHT)
{
height >>= FRACBITS;
WRITEINT16(demo_p, height);
}
ghostext.flags = 0;
}
if (ghost->player && ghost->player->followmobj) // bloats tails runs but what can ya do
if (ghost->player && ghost->player->followmobj && !(ghost->player->followmobj->sprite == SPR_NULL || (ghost->player->followmobj->flags2 & MF2_DONTDRAW))) // bloats tails runs but what can ya do
{
INT16 temp;
UINT8 *followtic_p = demo_p++;
UINT8 followtic = 0;
ziptic |= GZT_FOLLOW;
if (ghost->player->followmobj->skin)
followtic |= FZT_SKIN;
if (!(oldghost.flags2 & MF2_AMBUSH))
{
followtic |= FZT_SPAWNED;
WRITEINT16(demo_p,ghost->player->followmobj->info->height>>FRACBITS);
if (ghost->player->followmobj->flags2 & MF2_LINKDRAW)
followtic |= FZT_LINKDRAW;
if (ghost->player->followmobj->colorized)
followtic |= FZT_COLORIZED;
if (followtic & FZT_SKIN)
WRITEUINT8(demo_p,(UINT8)(((skin_t *)(ghost->player->followmobj->skin))-skins));
oldghost.flags2 |= MF2_AMBUSH;
}
temp = (INT16)((ghost->player->followmobj->x-ghost->x)>>8);
WRITEINT16(demo_p,temp);
temp = (INT16)((ghost->player->followmobj->y-ghost->y)>>8);
WRITEINT16(demo_p,temp);
temp = (INT16)((ghost->player->followmobj->z-ghost->z)>>8);
WRITEINT16(demo_p,temp);
WRITEUINT8(demo_p,ghost->player->followmobj->sprite);
WRITEUINT8(demo_p,ghost->player->followmobj->sprite2);
if (followtic & FZT_SKIN)
WRITEUINT8(demo_p,ghost->player->followmobj->sprite2);
WRITEUINT16(demo_p,ghost->player->followmobj->sprite);
WRITEUINT8(demo_p,(ghost->player->followmobj->frame & FF_FRAMEMASK));
WRITEUINT8(demo_p,ghost->player->followmobj->color);
if (ghost->player->followmobj->scale != ghost->scale)
{
followtic |= FZT_SCALE;
WRITEFIXED(demo_p,ghost->player->followmobj->scale);
}
*followtic_p = followtic;
}
else
oldghost.flags2 &= ~MF2_AMBUSH;
*ziptic_p = ziptic;
@ -4533,15 +4585,28 @@ void G_ConsGhostTic(void)
}
}
if (xziptic & EZT_SPRITE)
demo_p++;
demo_p += sizeof(UINT16);
if (xziptic & EZT_HEIGHT)
demo_p += sizeof(INT16);
}
if (ziptic & GZT_FOLLOW)
{ // Even more...
UINT8 followtic = READUINT8(demo_p);
if (followtic & FZT_SPAWNED)
{
demo_p += sizeof(INT16);
if (followtic & FZT_SKIN)
demo_p++;
}
if (followtic & FZT_SCALE)
demo_p += sizeof(fixed_t);
demo_p += sizeof(INT16);
demo_p += sizeof(INT16);
demo_p += sizeof(INT16);
demo_p++;
if (followtic & FZT_SKIN)
demo_p++;
demo_p += sizeof(UINT16);
demo_p++;
demo_p++;
}
@ -4629,6 +4694,11 @@ void G_GhostTicker(void)
g->mo->z = g->oldmo.z;
P_SetThingPosition(g->mo);
g->mo->frame = g->oldmo.frame | tr_trans30<<FF_TRANSSHIFT;
if (g->fadein)
{
g->mo->frame += (((--g->fadein)/6)<<FF_TRANSSHIFT); // this calc never exceeds 9 unless g->fadein is bad, and it's only set once, so...
g->mo->flags2 &= ~MF2_DONTDRAW;
}
g->mo->sprite2 = g->oldmo.sprite2;
if (ziptic & GZT_EXTRA)
@ -4686,33 +4756,38 @@ void G_GhostTicker(void)
break;
}
}
if (type == MT_GHOST)
if (type != MT_NULL)
{
mobj = P_SpawnGhostMobj(g->mo); // does a large portion of the work for us
mobj->frame = (mobj->frame & ~FF_FRAMEMASK)|tr_trans60<<FF_TRANSSHIFT; // P_SpawnGhostMobj sets trans50, we want trans60
}
else
{
mobj = P_SpawnMobj(g->mo->x, g->mo->y, g->mo->z - FixedDiv(FixedMul(g->mo->info->height, g->mo->scale) - g->mo->height,3*FRACUNIT), MT_THOK);
mobj->sprite = states[mobjinfo[type].spawnstate].sprite;
mobj->frame = (states[mobjinfo[type].spawnstate].frame & FF_FRAMEMASK) | tr_trans60<<FF_TRANSSHIFT;
mobj->tics = -1; // nope.
mobj->color = g->mo->color;
if (g->mo->eflags & MFE_VERTICALFLIP)
if (type == MT_GHOST)
{
mobj->flags2 |= MF2_OBJECTFLIP;
mobj->eflags |= MFE_VERTICALFLIP;
mobj = P_SpawnGhostMobj(g->mo); // does a large portion of the work for us
mobj->frame = (mobj->frame & ~FF_FRAMEMASK)|tr_trans60<<FF_TRANSSHIFT; // P_SpawnGhostMobj sets trans50, we want trans60
}
P_SetScale(mobj, g->mo->scale);
mobj->destscale = g->mo->scale;
else
{
mobj = P_SpawnMobjFromMobj(g->mo, 0, 0, -FixedDiv(FixedMul(g->mo->info->height, g->mo->scale) - g->mo->height,3*FRACUNIT), MT_THOK);
mobj->sprite = states[mobjinfo[type].spawnstate].sprite;
mobj->frame = (states[mobjinfo[type].spawnstate].frame & FF_FRAMEMASK) | tr_trans60<<FF_TRANSSHIFT;
mobj->color = g->mo->color;
mobj->skin = g->mo->skin;
P_SetScale(mobj, (mobj->destscale = g->mo->scale));
if (type == MT_THOK) // spintrail-specific modification for MT_THOK
{
mobj->frame = FF_TRANS80;
mobj->fuse = mobj->tics;
}
mobj->tics = -1; // nope.
}
mobj->floorz = mobj->z;
mobj->ceilingz = mobj->z+mobj->height;
P_UnsetThingPosition(mobj);
mobj->flags = MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY; // make an ATTEMPT to curb crazy SOCs fucking stuff up...
P_SetThingPosition(mobj);
if (!mobj->fuse)
mobj->fuse = 8;
P_SetTarget(&mobj->target, g->mo);
}
mobj->floorz = mobj->z;
mobj->ceilingz = mobj->z+mobj->height;
P_UnsetThingPosition(mobj);
mobj->flags = MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY; // make an ATTEMPT to curb crazy SOCs fucking stuff up...
P_SetThingPosition(mobj);
mobj->fuse = 8;
P_SetTarget(&mobj->target, g->mo);
}
if (xziptic & EZT_HIT)
{ // Spawn hit poofs for killing things!
@ -4742,7 +4817,12 @@ void G_GhostTicker(void)
}
}
if (xziptic & EZT_SPRITE)
g->mo->sprite = READUINT8(g->p);
g->mo->sprite = READUINT16(g->p);
if (xziptic & EZT_HEIGHT)
{
fixed_t temp = READINT16(g->p)<<FRACBITS;
g->mo->height = FixedMul(temp, g->mo->scale);
}
}
// Tick ghost colors (Super and Mario Invincibility flashing)
@ -4768,55 +4848,81 @@ void G_GhostTicker(void)
#define follow g->mo->tracer
if (ziptic & GZT_FOLLOW)
{ // Even more...
if (!follow)
UINT8 followtic = READUINT8(g->p);
fixed_t temp;
if (followtic & FZT_SPAWNED)
{
mobj_t *newmo = P_SpawnMobj(g->mo->x, g->mo->y, g->mo->z, MT_GHOST);
P_SetTarget(&g->mo->tracer, newmo);
P_SetTarget(&newmo->tracer, g->mo);
newmo->skin = g->mo->skin;
newmo->tics = -1;
newmo->flags2 |= MF2_LINKDRAW;
if (follow)
P_RemoveMobj(follow);
P_SetTarget(&follow, P_SpawnMobjFromMobj(g->mo, 0, 0, 0, MT_GHOST));
P_SetTarget(&follow->tracer, g->mo);
follow->tics = -1;
temp = READINT16(g->p)<<FRACBITS;
follow->height = FixedMul(follow->scale, temp);
follow->eflags = (follow->eflags & ~MFE_VERTICALFLIP)|(g->mo->eflags & MFE_VERTICALFLIP);
follow->destscale = g->mo->destscale;
if (followtic & FZT_LINKDRAW)
follow->flags2 |= MF2_LINKDRAW;
if (followtic & FZT_COLORIZED)
follow->colorized = true;
if (followtic & FZT_SKIN)
follow->skin = &skins[READUINT8(g->p)];
}
if (follow)
{
if (followtic & FZT_SCALE)
follow->destscale = READFIXED(g->p);
else
follow->destscale = g->mo->destscale;
if (follow->destscale != follow->scale)
P_SetScale(follow, follow->destscale);
}
else
{
if (xziptic & EZT_FLIP)
g->mo->eflags ^= MFE_VERTICALFLIP;
if (xziptic & EZT_SCALE)
P_UnsetThingPosition(follow);
temp = READINT16(g->p)<<8;
follow->x = g->mo->x + temp;
temp = READINT16(g->p)<<8;
follow->y = g->mo->y + temp;
temp = READINT16(g->p)<<8;
follow->z = g->mo->z + temp;
P_SetThingPosition(follow);
if (followtic & FZT_SKIN)
follow->sprite2 = READUINT8(g->p);
else
follow->sprite2 = 0;
follow->sprite = READUINT16(g->p);
follow->frame = (READUINT8(g->p)) | (g->mo->frame & FF_TRANSMASK);
follow->angle = g->mo->angle;
follow->color = READUINT8(g->p);
if (!(followtic & FZT_SPAWNED))
{
follow->destscale = g->mo->destscale;
if (follow->destscale != follow->scale)
P_SetScale(follow, follow->destscale);
if (xziptic & EZT_FLIP)
{
follow->flags2 ^= MF2_OBJECTFLIP;
follow->eflags ^= MFE_VERTICALFLIP;
}
}
}
P_UnsetThingPosition(follow);
follow->x = g->mo->x + (READINT16(g->p)<<8);
follow->y = g->mo->y + (READINT16(g->p)<<8);
follow->z = g->mo->z + (READINT16(g->p)<<8);
P_SetThingPosition(follow);
follow->sprite = READUINT8(g->p);
follow->sprite2 = READUINT8(g->p);
follow->frame = (READUINT8(g->p)) | tr_trans30<<FF_TRANSSHIFT;
follow->angle = g->mo->angle;
follow->color = g->mo->color;
}
else if (follow)
{
P_RemoveMobj(follow);
P_SetTarget(&follow, NULL);
}
#undef follow
// Demo ends after ghost data.
if (*g->p == DEMOMARKER)
{
g->mo->momx = g->mo->momy = g->mo->momz = 0;
#if 1 // freeze frame (maybe more useful for time attackers)
g->mo->colorized = true;
if (follow)
follow->colorized = true;
#else // dissapearing act
g->mo->fuse = TICRATE;
if (follow)
follow->fuse = TICRATE;
#endif
if (p)
p->next = g->next;
else
@ -4825,17 +4931,41 @@ void G_GhostTicker(void)
continue;
}
p = g;
#undef follow
}
}
void G_ReadMetalTic(mobj_t *metal)
{
UINT8 ziptic;
UINT16 speed;
UINT8 statetype;
UINT8 xziptic = 0;
if (!metal_p)
return;
if (!metal->health)
{
G_StopMetalDemo();
return;
}
switch (*metal_p)
{
case METALSNICE:
break;
case METALDEATH:
if (metal->tracer)
P_RemoveMobj(metal->tracer);
P_KillMobj(metal, NULL, NULL, 0);
/* FALLTHRU */
case DEMOMARKER:
default:
// end of demo data stream
G_StopMetalDemo();
return;
}
metal_p++;
ziptic = READUINT8(metal_p);
// Read changes from the tic
@ -4862,9 +4992,9 @@ void G_ReadMetalTic(mobj_t *metal)
if (ziptic & GZT_ANGLE)
metal->angle = READUINT8(metal_p)<<24;
if (ziptic & GZT_FRAME)
metal_p++; // Currently unused. (Metal Sonic figures out what he's doing his own damn self.)
oldmetal.frame = READUINT32(metal_p);
if (ziptic & GZT_SPR2)
metal_p++;
oldmetal.sprite2 = READUINT8(metal_p);
// Set movement, position, and angle
// oldmetal contains where you're supposed to be.
@ -4876,67 +5006,160 @@ void G_ReadMetalTic(mobj_t *metal)
metal->y = oldmetal.y;
metal->z = oldmetal.z;
P_SetThingPosition(metal);
metal->frame = oldmetal.frame;
metal->sprite2 = oldmetal.sprite2;
if (ziptic & GZT_EXTRA)
{ // But wait, there's more!
ziptic = READUINT8(metal_p);
if (ziptic & EZT_FLIP)
xziptic = READUINT8(metal_p);
if (xziptic & EZT_FLIP)
metal->eflags ^= MFE_VERTICALFLIP;
if (ziptic & EZT_SCALE)
if (xziptic & EZT_SCALE)
{
metal->destscale = READFIXED(metal_p);
if (metal->destscale != metal->scale)
P_SetScale(metal, metal->destscale);
}
}
if (xziptic & EZT_THOKMASK)
{ // Let's only spawn ONE of these per frame, thanks.
mobj_t *mobj;
INT32 type = -1;
if (metal->skin)
{
skin_t *skin = (skin_t *)metal->skin;
switch (xziptic & EZT_THOKMASK)
{
case EZT_THOK:
type = skin->thokitem < 0 ? (UINT32)mobjinfo[MT_PLAYER].painchance : (UINT32)skin->thokitem;
break;
case EZT_SPIN:
type = skin->spinitem < 0 ? (UINT32)mobjinfo[MT_PLAYER].damage : (UINT32)skin->spinitem;
break;
case EZT_REV:
type = skin->revitem < 0 ? (UINT32)mobjinfo[MT_PLAYER].raisestate : (UINT32)skin->revitem;
break;
}
}
if (type != MT_NULL)
{
if (type == MT_GHOST)
{
mobj = P_SpawnGhostMobj(metal); // does a large portion of the work for us
}
else
{
mobj = P_SpawnMobjFromMobj(metal, 0, 0, -FixedDiv(FixedMul(metal->info->height, metal->scale) - metal->height,3*FRACUNIT), MT_THOK);
mobj->sprite = states[mobjinfo[type].spawnstate].sprite;
mobj->frame = states[mobjinfo[type].spawnstate].frame;
mobj->angle = metal->angle;
mobj->color = metal->color;
mobj->skin = metal->skin;
P_SetScale(mobj, (mobj->destscale = metal->scale));
// Calculates player's speed based on distance-of-a-line formula
speed = FixedDiv(P_AproxDistance(oldmetal.momx, oldmetal.momy), metal->scale)>>FRACBITS;
// Use speed to decide an appropriate state
if (speed > 28) // default skin runspeed
statetype = 2;
else if (speed > 1) // stopspeed
statetype = 1;
else
statetype = 0;
// Set state
if (statetype != metal->threshold)
{
switch (statetype)
{
case 2: // run
P_SetMobjState(metal,metal->info->meleestate);
break;
case 1: // walk
P_SetMobjState(metal,metal->info->seestate);
break;
default: // stand
P_SetMobjState(metal,metal->info->spawnstate);
break;
if (type == MT_THOK) // spintrail-specific modification for MT_THOK
{
mobj->frame = FF_TRANS70;
mobj->fuse = mobj->tics;
}
mobj->tics = -1; // nope.
}
mobj->floorz = mobj->z;
mobj->ceilingz = mobj->z+mobj->height;
P_UnsetThingPosition(mobj);
mobj->flags = MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY; // make an ATTEMPT to curb crazy SOCs fucking stuff up...
P_SetThingPosition(mobj);
if (!mobj->fuse)
mobj->fuse = 8;
P_SetTarget(&mobj->target, metal);
}
}
if (xziptic & EZT_SPRITE)
metal->sprite = READUINT16(metal_p);
if (xziptic & EZT_HEIGHT)
{
fixed_t temp = READINT16(metal_p)<<FRACBITS;
metal->height = FixedMul(temp, metal->scale);
}
metal->threshold = statetype;
}
// TODO: Modify state durations based on movement speed, similar to players?
#define follow metal->tracer
if (ziptic & GZT_FOLLOW)
{ // Even more...
UINT8 followtic = READUINT8(metal_p);
fixed_t temp;
if (followtic & FZT_SPAWNED)
{
if (follow)
P_RemoveMobj(follow);
P_SetTarget(&follow, P_SpawnMobjFromMobj(metal, 0, 0, 0, MT_GHOST));
P_SetTarget(&follow->tracer, metal);
follow->tics = -1;
temp = READINT16(metal_p)<<FRACBITS;
follow->height = FixedMul(follow->scale, temp);
if (*metal_p == DEMOMARKER)
{
// end of demo data stream
G_StopMetalDemo();
return;
}
if (followtic & FZT_LINKDRAW)
follow->flags2 |= MF2_LINKDRAW;
if (followtic & FZT_COLORIZED)
follow->colorized = true;
if (followtic & FZT_SKIN)
follow->skin = &skins[READUINT8(metal_p)];
}
if (follow)
{
if (followtic & FZT_SCALE)
follow->destscale = READFIXED(metal_p);
else
follow->destscale = metal->destscale;
if (follow->destscale != follow->scale)
P_SetScale(follow, follow->destscale);
P_UnsetThingPosition(follow);
temp = READINT16(metal_p)<<8;
follow->x = metal->x + temp;
temp = READINT16(metal_p)<<8;
follow->y = metal->y + temp;
temp = READINT16(metal_p)<<8;
follow->z = metal->z + temp;
P_SetThingPosition(follow);
if (followtic & FZT_SKIN)
follow->sprite2 = READUINT8(metal_p);
else
follow->sprite2 = 0;
follow->sprite = READUINT16(metal_p);
follow->frame = READUINT32(metal_p); // NOT & FF_FRAMEMASK here, so 32 bits
follow->angle = metal->angle;
follow->color = READUINT8(metal_p);
if (!(followtic & FZT_SPAWNED))
{
if (xziptic & EZT_FLIP)
{
follow->flags2 ^= MF2_OBJECTFLIP;
follow->eflags ^= MFE_VERTICALFLIP;
}
}
}
}
else if (follow)
{
P_RemoveMobj(follow);
P_SetTarget(&follow, NULL);
}
#undef follow
}
void G_WriteMetalTic(mobj_t *metal)
{
UINT8 ziptic = 0;
UINT8 *ziptic_p;
fixed_t height;
if (!demo_p) // demo_p will be NULL until the race start linedef executor is triggered!
if (!demo_p) // demo_p will be NULL until the race start linedef executor is activated!
return;
WRITEUINT8(demo_p, METALSNICE);
ziptic_p = demo_p++; // the ziptic, written at the end of this function
#define MAXMOM (0xFFFF<<8)
@ -4949,10 +5172,10 @@ void G_WriteMetalTic(mobj_t *metal)
oldmetal.x = metal->x;
oldmetal.y = metal->y;
oldmetal.z = metal->z;
ziptic |= GZT_XYZ;
WRITEFIXED(demo_p,oldmetal.x);
WRITEFIXED(demo_p,oldmetal.y);
WRITEFIXED(demo_p,oldmetal.z);
ziptic |= GZT_XYZ;
}
else
{
@ -4965,16 +5188,16 @@ void G_WriteMetalTic(mobj_t *metal)
{
oldmetal.momx = momx;
oldmetal.momy = momy;
ziptic |= GZT_MOMXY;
WRITEINT16(demo_p,momx);
WRITEINT16(demo_p,momy);
ziptic |= GZT_MOMXY;
}
momx = (INT16)((metal->z-oldmetal.z)>>8);
if (momx != oldmetal.momz)
{
oldmetal.momz = momx;
WRITEINT16(demo_p,momx);
ziptic |= GZT_MOMZ;
WRITEINT16(demo_p,momx);
}
// This SHOULD set oldmetal.x/y/z to match metal->x/y/z
@ -4997,41 +5220,112 @@ void G_WriteMetalTic(mobj_t *metal)
WRITEUINT8(demo_p,oldmetal.angle);
}
// Metal Sonic does not need our state changes.
// ... currently.
// Store the sprite frame.
if ((metal->frame & FF_FRAMEMASK) != oldmetal.frame)
{
UINT8 *exttic_p = NULL;
UINT8 exttic = 0;
if ((metal->eflags & MFE_VERTICALFLIP) != (oldmetal.eflags & MFE_VERTICALFLIP))
{
if (!exttic_p)
exttic_p = demo_p++;
exttic |= EZT_FLIP;
oldmetal.eflags ^= MFE_VERTICALFLIP;
}
if (metal->scale != oldmetal.scale)
{
if (!exttic_p)
exttic_p = demo_p++;
exttic |= EZT_SCALE;
WRITEFIXED(demo_p,metal->scale);
oldmetal.scale = metal->scale;
}
if (exttic_p)
{
*exttic_p = exttic;
ziptic |= GZT_EXTRA;
}
oldmetal.frame = metal->frame; // NOT & FF_FRAMEMASK here, so 32 bits
ziptic |= GZT_FRAME;
WRITEUINT32(demo_p,oldmetal.frame);
}
if (metal->sprite == SPR_PLAY
&& metal->sprite2 != oldmetal.sprite2)
{
oldmetal.sprite2 = metal->sprite2;
ziptic |= GZT_SPR2;
WRITEUINT8(demo_p,oldmetal.sprite2);
}
// Check for sprite set changes
if (metal->sprite != oldmetal.sprite)
{
oldmetal.sprite = metal->sprite;
ghostext.flags |= EZT_SPRITE;
}
if ((height = FixedDiv(metal->height, metal->scale)) != oldmetal.height)
{
oldmetal.height = height;
ghostext.flags |= EZT_HEIGHT;
}
if (ghostext.flags & ~(EZT_COLOR|EZT_HIT)) // these two aren't handled by metal ever
{
ziptic |= GZT_EXTRA;
if (ghostext.scale == ghostext.lastscale)
ghostext.flags &= ~EZT_SCALE;
WRITEUINT8(demo_p,ghostext.flags);
if (ghostext.flags & EZT_SCALE)
{
WRITEFIXED(demo_p,ghostext.scale);
ghostext.lastscale = ghostext.scale;
}
if (ghostext.flags & EZT_SPRITE)
WRITEUINT16(demo_p,oldmetal.sprite);
if (ghostext.flags & EZT_HEIGHT)
{
height >>= FRACBITS;
WRITEINT16(demo_p, height);
}
ghostext.flags = 0;
}
if (metal->player && metal->player->followmobj && !(metal->player->followmobj->sprite == SPR_NULL || (metal->player->followmobj->flags2 & MF2_DONTDRAW)))
{
INT16 temp;
UINT8 *followtic_p = demo_p++;
UINT8 followtic = 0;
ziptic |= GZT_FOLLOW;
if (metal->player->followmobj->skin)
followtic |= FZT_SKIN;
if (!(oldmetal.flags2 & MF2_AMBUSH))
{
followtic |= FZT_SPAWNED;
WRITEINT16(demo_p,metal->player->followmobj->info->height>>FRACBITS);
if (metal->player->followmobj->flags2 & MF2_LINKDRAW)
followtic |= FZT_LINKDRAW;
if (metal->player->followmobj->colorized)
followtic |= FZT_COLORIZED;
if (followtic & FZT_SKIN)
WRITEUINT8(demo_p,(UINT8)(((skin_t *)(metal->player->followmobj->skin))-skins));
oldmetal.flags2 |= MF2_AMBUSH;
}
if (metal->player->followmobj->scale != metal->scale)
{
followtic |= FZT_SCALE;
WRITEFIXED(demo_p,metal->player->followmobj->scale);
}
temp = (INT16)((metal->player->followmobj->x-metal->x)>>8);
WRITEINT16(demo_p,temp);
temp = (INT16)((metal->player->followmobj->y-metal->y)>>8);
WRITEINT16(demo_p,temp);
temp = (INT16)((metal->player->followmobj->z-metal->z)>>8);
WRITEINT16(demo_p,temp);
if (followtic & FZT_SKIN)
WRITEUINT8(demo_p,metal->player->followmobj->sprite2);
WRITEUINT16(demo_p,metal->player->followmobj->sprite);
WRITEUINT32(demo_p,metal->player->followmobj->frame); // NOT & FF_FRAMEMASK here, so 32 bits
WRITEUINT8(demo_p,metal->player->followmobj->color);
*followtic_p = followtic;
}
else
oldmetal.flags2 &= ~MF2_AMBUSH;
*ziptic_p = ziptic;
// attention here for the ticcmd size!
// latest demos with mouse aiming byte in ticcmd
if (demo_p >= demoend - 32)
{
G_StopMetalRecording(); // no more space
G_StopMetalRecording(false); // no more space
return;
}
}
@ -5165,22 +5459,36 @@ void G_BeginRecording(void)
// And mobjtype_t is best with UINT32 too...
WRITEUINT32(demo_p, player->followitem);
// Save pflag data
// Save pflag data - see SendWeaponPref()
{
UINT8 buf = 0;
if (player->pflags & PF_FLIPCAM)
pflags_t pflags = 0;
if (cv_flipcam.value)
{
buf |= 0x01;
if (player->pflags & PF_ANALOGMODE)
pflags |= PF_FLIPCAM;
}
if (cv_analog.value)
{
buf |= 0x02;
if (player->pflags & PF_DIRECTIONCHAR)
pflags |= PF_ANALOGMODE;
}
if (cv_directionchar.value)
{
buf |= 0x04;
if (player->pflags & PF_AUTOBRAKE)
pflags |= PF_DIRECTIONCHAR;
}
if (cv_autobrake.value)
{
buf |= 0x08;
pflags |= PF_AUTOBRAKE;
}
if (cv_usejoystick.value)
buf |= 0x10;
CV_SetValue(&cv_showinputjoy, !!(cv_usejoystick.value));
WRITEUINT8(demo_p,buf);
player->pflags = pflags;
}
// Save netvar data
@ -5209,8 +5517,10 @@ void G_BeginMetal(void)
{
mobj_t *mo = players[consoleplayer].mo;
#if 0
if (demo_p)
return;
#endif
demo_p = demobuffer;
@ -5225,6 +5535,9 @@ void G_BeginMetal(void)
M_Memcpy(demo_p, "METL", 4); demo_p += 4;
memset(&ghostext,0,sizeof(ghostext));
ghostext.lastscale = ghostext.scale = FRACUNIT;
// Set up our memory.
memset(&oldmetal,0,sizeof(oldmetal));
oldmetal.x = mo->x;
@ -5873,7 +6186,9 @@ void G_AddGhost(char *defdemoname)
gh->mo->state = states+S_PLAY_STND;
gh->mo->sprite = gh->mo->state->sprite;
gh->mo->sprite2 = (gh->mo->state->frame & FF_FRAMEMASK);
gh->mo->frame = tr_trans20<<FF_TRANSSHIFT;
//gh->mo->frame = tr_trans30<<FF_TRANSSHIFT;
gh->mo->flags2 |= MF2_DONTDRAW;
gh->fadein = (9-3)*6; // fade from invisible to trans30 over as close to 35 tics as possible
gh->mo->tics = -1;
CONS_Printf(M_GetText("Added ghost %s from %s\n"), name, pdemoname);
@ -5994,19 +6309,23 @@ void G_StopMetalDemo(void)
}
// Stops metal sonic recording.
ATTRNORETURN void FUNCNORETURN G_StopMetalRecording(void)
ATTRNORETURN void FUNCNORETURN G_StopMetalRecording(boolean kill)
{
boolean saved = false;
if (demo_p)
{
UINT8 *p = demobuffer+16; // checksum position
if (kill)
WRITEUINT8(demo_p, METALDEATH); // add the metal death marker
else
WRITEUINT8(demo_p, DEMOMARKER); // add the demo end marker
#ifdef NOMD5
UINT8 i;
WRITEUINT8(demo_p, DEMOMARKER); // add the demo end marker
for (i = 0; i < 16; i++, p++)
*p = P_RandomByte(); // This MD5 was chosen by fair dice roll and most likely < 50% correct.
{
UINT8 i;
for (i = 0; i < 16; i++, p++)
*p = P_RandomByte(); // This MD5 was chosen by fair dice roll and most likely < 50% correct.
}
#else
WRITEUINT8(demo_p, DEMOMARKER); // add the demo end marker
md5_buffer((char *)p+16, demo_p - (p+16), (void *)p); // make a checksum of everything after the checksum in the file.
#endif
saved = FIL_WriteFile(va("%sMS.LMP", G_BuildMapName(gamemap)), demobuffer, demo_p - demobuffer); // finally output the file.

View file

@ -175,7 +175,7 @@ void G_AddGhost(char *defdemoname);
void G_DoPlayMetal(void);
void G_DoneLevelLoad(void);
void G_StopMetalDemo(void);
ATTRNORETURN void FUNCNORETURN G_StopMetalRecording(void);
ATTRNORETURN void FUNCNORETURN G_StopMetalRecording(boolean kill);
void G_StopDemo(void);
boolean G_CheckDemoStatus(void);

View file

@ -1800,38 +1800,24 @@ state_t states[NUMSTATES] =
{SPR_NULL, 0, 1, {A_BossScream}, 0, 0, S_CYBRAKDEMONVILEEXPLOSION1}, //S_CYBRAKDEMONVILEEXPLOSION3,
// Metal Sonic
{SPR_METL, 0, 35, {NULL}, 0, 0, S_METALSONIC_WAIT1}, // S_METALSONIC_STAND
{SPR_METL, 1, 8, {NULL}, 0, 0, S_METALSONIC_WAIT2}, // S_METALSONIC_WAIT1
{SPR_METL, 2, 8, {NULL}, 0, 0, S_METALSONIC_WAIT1}, // S_METALSONIC_WAIT2
{SPR_METL, 3, 4, {NULL}, 0, 0, S_METALSONIC_WALK2}, // S_METALSONIC_WALK1
{SPR_METL, 4, 4, {NULL}, 0, 0, S_METALSONIC_WALK3}, // S_METALSONIC_WALK2
{SPR_METL, 5, 4, {NULL}, 0, 0, S_METALSONIC_WALK4}, // S_METALSONIC_WALK3
{SPR_METL, 6, 4, {NULL}, 0, 0, S_METALSONIC_WALK5}, // S_METALSONIC_WALK4
{SPR_METL, 7, 4, {NULL}, 0, 0, S_METALSONIC_WALK6}, // S_METALSONIC_WALK5
{SPR_METL, 6, 4, {NULL}, 0, 0, S_METALSONIC_WALK7}, // S_METALSONIC_WALK6
{SPR_METL, 5, 4, {NULL}, 0, 0, S_METALSONIC_WALK8}, // S_METALSONIC_WALK7
{SPR_METL, 4, 4, {NULL}, 0, 0, S_METALSONIC_WALK1}, // S_METALSONIC_WALK8
{SPR_METL, 8, 2, {NULL}, 0, 0, S_METALSONIC_RUN2}, // S_METALSONIC_RUN1
{SPR_METL, 9, 2, {NULL}, 0, 0, S_METALSONIC_RUN3}, // S_METALSONIC_RUN2
{SPR_METL, 10, 2, {NULL}, 0, 0, S_METALSONIC_RUN4}, // S_METALSONIC_RUN3
{SPR_METL, 9, 2, {NULL}, 0, 0, S_METALSONIC_RUN1}, // S_METALSONIC_RUN4
{SPR_PLAY, SPR2_STND, -1, {NULL}, 0, 0, S_METALSONIC_RACE}, // S_METALSONIC_RACE
{SPR_METL, 4, -1, {NULL}, 0, 0, S_NULL}, // S_METALSONIC_FLOAT
{SPR_METL, 12|FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_METALSONIC_STUN}, // S_METALSONIC_VECTOR
{SPR_METL, 11, -1, {NULL}, 0, 0, S_METALSONIC_FLOAT}, // S_METALSONIC_STUN
{SPR_METL, 13, 20, {NULL}, 0, 0, S_METALSONIC_GATHER},// S_METALSONIC_RAISE
{SPR_METL, 14, -1, {NULL}, 0, 0, S_NULL}, // S_METALSONIC_GATHER
{SPR_METL, 15, -1, {NULL}, 0, 0, S_METALSONIC_BOUNCE},// S_METALSONIC_DASH
{SPR_METL, 14, -1, {NULL}, 0, 0, S_NULL}, // S_METALSONIC_BOUNCE
{SPR_METL, 16, -1, {NULL}, 0, 0, S_NULL}, // S_METALSONIC_BADBOUNCE
{SPR_METL, 13, -1, {NULL}, 0, 0, S_METALSONIC_GATHER},// S_METALSONIC_SHOOT
{SPR_METL, 11, 40, {A_Pain}, 0, 0, S_METALSONIC_FLOAT}, // S_METALSONIC_PAIN
{SPR_METL, 13, 2, {A_Fall}, 0, 0, S_METALSONIC_DEATH2},// S_METALSONIC_DEATH1
{SPR_METL, 13, 4, {A_BossScream}, 0, 0, S_METALSONIC_DEATH3},// S_METALSONIC_DEATH2
{SPR_METL, 13, 0, {A_Repeat}, 17, S_METALSONIC_DEATH2, S_METALSONIC_DEATH4}, // S_METALSONIC_DEATH3
{SPR_METL, 13, -1, {A_BossDeath}, 0, 0, S_NULL}, // S_METALSONIC_DEATH4
{SPR_METL, 11, 1, {A_BossScream}, 0, 0, S_METALSONIC_FLEE2}, // S_METALSONIC_FLEE1
{SPR_METL, 11, 7, {NULL}, 0, 0, S_METALSONIC_FLEE1}, // S_METALSONIC_FLEE2
{SPR_METL, 16|FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_METALSONIC_STUN}, // S_METALSONIC_VECTOR
{SPR_METL, 15, -1, {NULL}, 0, 0, S_METALSONIC_FLOAT}, // S_METALSONIC_STUN
{SPR_METL, 17, 20, {NULL}, 0, 0, S_METALSONIC_GATHER},// S_METALSONIC_RAISE
{SPR_METL, 18, -1, {NULL}, 0, 0, S_NULL}, // S_METALSONIC_GATHER
{SPR_METL, 6|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, -1, {NULL}, 1, 2, S_METALSONIC_BOUNCE},// S_METALSONIC_DASH
{SPR_METL, 18|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, -1, {NULL}, 1, 2, S_NULL}, // S_METALSONIC_BOUNCE
{SPR_METL, 14, -1, {NULL}, 0, 0, S_NULL}, // S_METALSONIC_BADBOUNCE
{SPR_METL, 17, -1, {NULL}, 0, 0, S_METALSONIC_GATHER},// S_METALSONIC_SHOOT
{SPR_METL, 15, 40, {A_Pain}, 0, 0, S_METALSONIC_FLOAT}, // S_METALSONIC_PAIN
{SPR_METL, 17, 2, {A_Fall}, 0, 0, S_METALSONIC_DEATH2},// S_METALSONIC_DEATH1
{SPR_METL, 17, 4, {A_BossScream}, 0, 0, S_METALSONIC_DEATH3},// S_METALSONIC_DEATH2
{SPR_METL, 17, 0, {A_Repeat}, 17, S_METALSONIC_DEATH2, S_METALSONIC_DEATH4}, // S_METALSONIC_DEATH3
{SPR_METL, 17, -1, {A_BossDeath}, 0, 0, S_NULL}, // S_METALSONIC_DEATH4
{SPR_METL, 15, 1, {A_BossScream}, 0, 0, S_METALSONIC_FLEE2}, // S_METALSONIC_FLEE1
{SPR_METL, 15, 7, {NULL}, 0, 0, S_METALSONIC_FLEE1}, // S_METALSONIC_FLEE2
{SPR_MSCF, FF_FULLBRIGHT|FF_TRANS30|FF_ANIMATE, -1, {NULL}, 11, 1, S_NULL}, // S_MSSHIELD_F1
{SPR_MSCF, FF_FULLBRIGHT|FF_ANIMATE|12, -1, {NULL}, 8, 2, S_NULL}, // S_MSSHIELD_F2
@ -6636,18 +6622,18 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
{ // MT_METALSONIC_RACE
207, // doomednum
S_METALSONIC_STAND, // spawnstate
S_METALSONIC_RACE, // spawnstate
8, // spawnhealth
S_METALSONIC_WALK1, // seestate
S_NULL, // seestate
sfx_None, // seesound
0, // reactiontime
sfx_None, // attacksound
S_NULL, // painstate
0, // painchance
sfx_None, // painsound
S_METALSONIC_RUN1, // meleestate
S_NULL, // meleestate
S_NULL, // missilestate
S_NULL, // deathstate
S_PLAY_DEAD, // deathstate
S_NULL, // xdeathstate
sfx_None, // deathsound
0, // speed
@ -6657,7 +6643,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
0, // mass
0, // damage
sfx_None, // activesound
MF_SCENERY|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
S_NULL // raisestate
},
@ -6707,7 +6693,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
0, // speed
32*FRACUNIT, // radius
52*FRACUNIT, // height
0, // display offset
1, // display offset
0, // mass
0, // damage
sfx_None, // activesound

View file

@ -1934,25 +1934,7 @@ typedef enum state
S_CYBRAKDEMONVILEEXPLOSION3,
// Metal Sonic (Race)
// S_PLAY_STND
S_METALSONIC_STAND,
// S_PLAY_TAP1
S_METALSONIC_WAIT1,
S_METALSONIC_WAIT2,
// S_PLAY_WALK
S_METALSONIC_WALK1,
S_METALSONIC_WALK2,
S_METALSONIC_WALK3,
S_METALSONIC_WALK4,
S_METALSONIC_WALK5,
S_METALSONIC_WALK6,
S_METALSONIC_WALK7,
S_METALSONIC_WALK8,
// S_PLAY_SPD1
S_METALSONIC_RUN1,
S_METALSONIC_RUN2,
S_METALSONIC_RUN3,
S_METALSONIC_RUN4,
S_METALSONIC_RACE,
// Metal Sonic (Battle)
S_METALSONIC_FLOAT,
S_METALSONIC_VECTOR,

View file

@ -9107,10 +9107,11 @@ void A_BossJetFume(mobj_t *actor)
P_SetTarget(&filler->target, actor);
filler->fuse = 59;
P_SetTarget(&actor->tracer, filler);
filler->destscale = actor->scale/3;
P_SetScale(filler, filler->destscale);
P_SetScale(filler, (filler->destscale = actor->scale/3));
if (actor->eflags & MFE_VERTICALFLIP)
filler->flags2 |= MF2_OBJECTFLIP;
filler->color = SKINCOLOR_ICY;
filler->colorized = true;
}
else if (locvar1 == 3) // Boss 4 jet flame
{

View file

@ -2372,7 +2372,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
if (target->player && !target->player->spectator)
{
if (metalrecording) // Ack! Metal Sonic shouldn't die! Cut the tape, end recording!
G_StopMetalRecording();
G_StopMetalRecording(true);
if (gametype == GT_MATCH // note, no team match suicide penalty
&& ((target == source) || (source == NULL && inflictor == NULL) || (source && !source->player)))
{ // Suicide penalty
@ -2763,6 +2763,12 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
}
}
break;
case MT_METALSONIC_RACE:
target->fuse = TICRATE*3;
target->momx = target->momy = target->momz = 0;
P_SetObjectMomZ(target, 14*FRACUNIT, false);
target->flags = (target->flags & ~MF_NOGRAVITY)|(MF_NOCLIP|MF_NOCLIPTHING);
break;
default:
break;
}
@ -3617,11 +3623,12 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
}
else if (inflictor && inflictor->flags & MF_MISSILE)
return false; // Metal Sonic walk through flame !!
else
else if (!player->powers[pw_flashing])
{ // Oh no! Metal Sonic is hit !!
P_ShieldDamage(player, inflictor, source, damage, damagetype);
return true;
}
return false;
}
else if (player->powers[pw_invulnerability] || player->powers[pw_flashing] || player->powers[pw_super]) // ignore bouncing & such in invulnerability
{

View file

@ -2563,19 +2563,30 @@ static boolean P_ZMovement(mobj_t *mo)
if (!mo->player && P_CheckDeathPitCollide(mo))
{
if (mo->flags & MF_ENEMY || mo->flags & MF_BOSS || mo->type == MT_MINECART)
switch (mo->type)
{
// Kill enemies, bosses and minecarts that fall into death pits.
if (mo->health)
{
P_KillMobj(mo, NULL, NULL, 0);
return false;
}
}
else
{
P_RemoveMobj(mo);
return false;
case MT_GHOST:
case MT_METALSONIC_RACE:
case MT_EXPLODE:
case MT_BOSSEXPLODE:
case MT_SONIC3KBOSSEXPLODE:
break;
default:
if (mo->flags & MF_ENEMY || mo->flags & MF_BOSS || mo->type == MT_MINECART)
{
// Kill enemies, bosses and minecarts that fall into death pits.
if (mo->health)
{
P_KillMobj(mo, NULL, NULL, 0);
}
return false;
}
else
{
P_RemoveMobj(mo);
return false;
}
break;
}
}
@ -5567,9 +5578,9 @@ static void P_Boss9Thinker(mobj_t *mobj)
P_InstaThrust(mobj, mobj->angle, -4*FRACUNIT);
P_TryMove(mobj, mobj->x+mobj->momx, mobj->y+mobj->momy, true);
mobj->momz -= gravity;
if (mobj->z < mobj->watertop)
if (mobj->z < mobj->watertop || mobj->z < (mobj->floorz + 16*FRACUNIT))
{
mobj->watertop = mobj->target->floorz + 32*FRACUNIT;
mobj->watertop = mobj->floorz + 32*FRACUNIT;
P_SetMobjState(mobj, mobj->info->spawnstate);
}
return;
@ -5577,16 +5588,22 @@ static void P_Boss9Thinker(mobj_t *mobj)
if ((!mobj->target || !(mobj->target->flags & MF_SHOOTABLE)))
{
if (mobj->tracer)
P_RemoveMobj(mobj->tracer);
if (mobj->hprev)
{
P_RemoveMobj(mobj->hprev);
P_SetTarget(&mobj->hprev, NULL);
}
P_BossTargetPlayer(mobj, false);
if (mobj->target && (!P_IsObjectOnGround(mobj->target) || mobj->target->player->pflags & PF_SPINNING))
P_SetTarget(&mobj->target, NULL); // Wait for them to hit the ground first
if (!mobj->target) // Still no target, aww.
{
// Reset the boss.
if (mobj->tracer)
P_RemoveMobj(mobj->tracer);
if (mobj->hprev)
{
P_RemoveMobj(mobj->hprev);
P_SetTarget(&mobj->hprev, NULL);
}
P_SetMobjState(mobj, mobj->info->spawnstate);
mobj->fuse = 0;
mobj->momx = FixedDiv(mobj->momx, FRACUNIT + (FRACUNIT>>2));
@ -5600,7 +5617,7 @@ static void P_Boss9Thinker(mobj_t *mobj)
return;
}
else if (!mobj->fuse)
mobj->fuse = 10*TICRATE;
mobj->fuse = 8*TICRATE;
}
// AI goes here.
@ -5627,16 +5644,18 @@ static void P_Boss9Thinker(mobj_t *mobj)
mobj->angle -= InvAngle(angle)/8;
// Alter your energy bubble's size/position
if (mobj->health > 3)
if (mobj->health > mobj->info->damage)
{
mobj->tracer->destscale = FRACUNIT + (4*TICRATE - mobj->fuse)*(FRACUNIT/2)/TICRATE + FixedMul(FINECOSINE(angle>>ANGLETOFINESHIFT),FRACUNIT/2);
P_SetScale(mobj->tracer, mobj->tracer->destscale);
}
if (mobj->hprev)
{
mobj->hprev->destscale = FRACUNIT + (2*TICRATE - mobj->fuse)*(FRACUNIT/2)/TICRATE + FixedMul(FINECOSINE(angle>>ANGLETOFINESHIFT),FRACUNIT/2);
P_SetScale(mobj->hprev, mobj->hprev->destscale);
P_TeleportMove(mobj->tracer, mobj->x, mobj->y, mobj->z + mobj->height/2 - mobj->tracer->height/2);
mobj->tracer->momx = mobj->momx;
mobj->tracer->momy = mobj->momy;
mobj->tracer->momz = mobj->momz;
P_TeleportMove(mobj->hprev, mobj->x, mobj->y, mobj->z + mobj->height/2 - mobj->hprev->height/2);
mobj->hprev->momx = mobj->momx;
mobj->hprev->momy = mobj->momy;
mobj->hprev->momz = mobj->momz;
}
// Firin' mah lazors - INDICATOR
if (mobj->fuse > TICRATE/2)
@ -5724,6 +5743,7 @@ static void P_Boss9Thinker(mobj_t *mobj)
S_StartSound(mobj, sfx_s3kb3);
}
}
}
// up...
mobj->z += mobj->height/2;
@ -5750,12 +5770,12 @@ static void P_Boss9Thinker(mobj_t *mobj)
if (mobj->health > mobj->info->damage)
{
P_SetScale(missile, FRACUNIT/3);
missile->color = SKINCOLOR_GOLD; // sonic cd electric power
missile->color = SKINCOLOR_MAGENTA; // sonic OVA/4 purple power
}
else
{
P_SetScale(missile, FRACUNIT/5);
missile->color = SKINCOLOR_MAGENTA; // sonic OVA/4 purple power
missile->color = SKINCOLOR_SUNSET; // sonic cd electric power
}
missile->destscale = missile->scale*2;
missile->scalespeed = abs(missile->scale - missile->destscale)/missile->fuse;
@ -5774,8 +5794,10 @@ static void P_Boss9Thinker(mobj_t *mobj)
if (mobj->movedir == 0 || mobj->movedir == 2) { // Pausing between bounces in the pinball phase
if (mobj->target->player->powers[pw_tailsfly]) // Trying to escape, eh?
mobj->watertop = mobj->target->z + mobj->target->momz*6; // Readjust your aim. >:3
else
else if (mobj->target->floorz > mobj->floorz)
mobj->watertop = mobj->target->floorz + 16*FRACUNIT;
else
mobj->watertop = mobj->floorz + 16*FRACUNIT;
if (!(mobj->threshold%4)) {
mobj->angle = R_PointToAngle2(mobj->x, mobj->y, mobj->target->x + mobj->target->momx*4, mobj->target->y + mobj->target->momy*4);
@ -5867,8 +5889,6 @@ static void P_Boss9Thinker(mobj_t *mobj)
return;
}
P_SpawnGhostMobj(mobj);
// Pinball attack!
if (mobj->movecount == 3 && (mobj->movedir == 0 || mobj->movedir == 2))
{
@ -5883,20 +5903,20 @@ static void P_Boss9Thinker(mobj_t *mobj)
if (!P_TryMove(mobj, mobj->x+mobj->momx, mobj->y+mobj->momy, true))
{ // Hit a wall? Find a direction to bounce
mobj->threshold--;
P_SetMobjState(mobj, mobj->state->nextstate);
if (!mobj->threshold) { // failed bounce!
S_StartSound(mobj, sfx_mspogo);
P_BounceMove(mobj);
mobj->angle = R_PointToAngle2(mobj->momx, mobj->momy,0,0);
mobj->momz = 4*FRACUNIT;
mobj->flags &= ~MF_PAIN;
mobj->fuse = 10*TICRATE;
mobj->fuse = 8*TICRATE;
mobj->movecount = 0;
P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_CYBRAKDEMON_VILE_EXPLOSION);
P_SetMobjState(mobj, mobj->info->meleestate);
}
else if (!(mobj->threshold%4))
{ // We've decided to lock onto the player this bounce.
P_SetMobjState(mobj, mobj->state->nextstate);
S_StartSound(mobj, sfx_s3k5a);
mobj->angle = R_PointToAngle2(mobj->x, mobj->y, mobj->target->x + mobj->target->momx*4, mobj->target->y + mobj->target->momy*4);
mobj->reactiontime = TICRATE - 5*(mobj->info->damage - mobj->health); // targetting time
@ -5913,6 +5933,8 @@ static void P_Boss9Thinker(mobj_t *mobj)
return;
}
P_SpawnGhostMobj(mobj)->colorized = false;
// Vector form dodge!
mobj->angle += mobj->movedir;
P_InstaThrust(mobj, mobj->angle, -speed);
@ -6009,7 +6031,7 @@ static void P_Boss9Thinker(mobj_t *mobj)
if (mobj->health > mobj->info->damage)
{ // No more bubble if we're broken (pinch phase)
mobj_t *shield = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_MSSHIELD_FRONT);
P_SetTarget(&mobj->tracer, shield);
P_SetTarget(&mobj->hprev, shield);
P_SetTarget(&shield->target, mobj);
// Attack 2: Energy shot!
@ -6040,14 +6062,15 @@ static void P_Boss9Thinker(mobj_t *mobj)
}
else
{
mobj_t *shield = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_MSSHIELD_FRONT);
/*mobj_t *shield = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_MSSHIELD_FRONT);
P_SetTarget(&mobj->tracer, shield);
P_SetTarget(&shield->target, mobj);
shield->height -= 20*FRACUNIT; // different offset...
P_SetMobjState(shield, S_MSSHIELD_F2);
P_SetMobjState(shield, S_MSSHIELD_F2);*/
P_SetMobjState(mobj, S_METALSONIC_BOUNCE);
//P_LinedefExecute(LE_PINCHPHASE, mobj, NULL); -- why does this happen twice? see case 2...
}
mobj->fuse = 4*TICRATE;
mobj->fuse = 3*TICRATE;
mobj->flags |= MF_PAIN;
if (mobj->info->attacksound)
S_StartSound(mobj, mobj->info->attacksound);
@ -6058,14 +6081,14 @@ static void P_Boss9Thinker(mobj_t *mobj)
case 2:
{
// We're all charged and ready now! Unleash the fury!!
mobj_t *removemobj = mobj->tracer;
S_StopSound(mobj);
P_SetTarget(&mobj->tracer, mobj->hnext);
P_RemoveMobj(removemobj);
if (mobj->hprev)
{
P_RemoveMobj(mobj->hprev);
P_SetTarget(&mobj->hprev, NULL);
}
if (mobj->health <= mobj->info->damage)
{
mobj_t *whoosh;
// Attack 1: Pinball dash!
if (mobj->health == 1)
mobj->movedir = 0;
@ -6078,9 +6101,13 @@ static void P_Boss9Thinker(mobj_t *mobj)
mobj->threshold = 12; // bounce 12 times
else
mobj->threshold = 24; // bounce 24 times
mobj->watertop = mobj->target->floorz + 16*FRACUNIT;
if (mobj->floorz >= mobj->target->floorz)
mobj->watertop = mobj->floorz + 16*FRACUNIT;
else
mobj->watertop = mobj->target->floorz + 16*FRACUNIT;
P_LinedefExecute(LE_PINCHPHASE, mobj, NULL);
#if 0
whoosh = P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_GHOST); // done here so the offset is correct
whoosh->frame = FF_FULLBRIGHT;
whoosh->sprite = SPR_ARMA;
@ -6088,9 +6115,13 @@ static void P_Boss9Thinker(mobj_t *mobj)
whoosh->scalespeed = FixedMul(whoosh->scalespeed, whoosh->scale);
whoosh->height = 38*whoosh->scale;
whoosh->fuse = 10;
whoosh->color = SKINCOLOR_MAGENTA;
whoosh->color = SKINCOLOR_SUNSET;
whoosh->colorized = true;
whoosh->flags |= MF_NOCLIPHEIGHT;
#endif
P_SetMobjState(mobj->tracer, S_JETFUMEFLASH);
P_SetScale(mobj->tracer, mobj->scale << 1);
}
else
{
@ -6102,10 +6133,13 @@ static void P_Boss9Thinker(mobj_t *mobj)
}
case 3:
// Return to idle.
mobj->watertop = mobj->target->floorz + 32*FRACUNIT;
if (mobj->floorz >= mobj->target->floorz)
mobj->watertop = mobj->floorz + 32*FRACUNIT;
else
mobj->watertop = mobj->target->floorz + 32*FRACUNIT;
P_SetMobjState(mobj, mobj->info->spawnstate);
mobj->flags &= ~MF_PAIN;
mobj->fuse = 10*TICRATE;
mobj->fuse = 8*TICRATE;
break;
}
mobj->movecount++;
@ -8264,6 +8298,20 @@ void P_MobjThinker(mobj_t *mobj)
P_SetObjectMomZ(mobj, -2 * FRACUNIT / 3, true);
}
break;
case MT_METALSONIC_RACE:
{
if (!(mobj->fuse % 8))
{
fixed_t r = mobj->radius >> FRACBITS;
mobj_t *explosion = P_SpawnMobj(
mobj->x + (P_RandomRange(r, -r) << FRACBITS),
mobj->y + (P_RandomRange(r, -r) << FRACBITS),
mobj->z + (P_RandomKey(mobj->height >> FRACBITS) << FRACBITS),
MT_SONIC3KBOSSEXPLODE);
S_StartSound(explosion, sfx_s3kb4);
}
P_SetObjectMomZ(mobj, -2 * FRACUNIT / 3, true);
}
default:
break;
}
@ -8706,11 +8754,17 @@ void P_MobjThinker(mobj_t *mobj)
}
else if (mobj->fuse == 59)
{
boolean dashmod = ((mobj->target->flags & MF_PAIN) && (mobj->target->health <= mobj->target->info->damage));
jetx = mobj->target->x + P_ReturnThrustX(mobj->target, mobj->target->angle, -mobj->target->radius);
jety = mobj->target->y + P_ReturnThrustY(mobj->target, mobj->target->angle, -mobj->target->radius);
P_UnsetThingPosition(mobj);
mobj->x = jetx;
mobj->y = jety;
mobj->destscale = mobj->target->scale;
if (!(dashmod && mobj->target->state == states+S_METALSONIC_BOUNCE))
{
mobj->destscale = (mobj->destscale + FixedDiv(R_PointToDist2(0, 0, mobj->target->momx, mobj->target->momy), 36*mobj->target->scale))/3;
}
if (mobj->target->eflags & MFE_VERTICALFLIP)
mobj->z = mobj->target->z + mobj->target->height/2 + mobj->height/2;
else
@ -8718,6 +8772,14 @@ void P_MobjThinker(mobj_t *mobj)
mobj->floorz = mobj->z;
mobj->ceilingz = mobj->z+mobj->height;
P_SetThingPosition(mobj);
if (dashmod)
{
mobj->color = SKINCOLOR_SUNSET;
if (mobj->target->movecount == 3 && !mobj->target->reactiontime && (mobj->target->movedir == 0 || mobj->target->movedir == 2))
P_SpawnGhostMobj(mobj);
}
else
mobj->color = SKINCOLOR_ICY;
}
mobj->fuse++;
}
@ -10402,8 +10464,11 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
if (nummaprings >= 0)
nummaprings++;
break;
case MT_METALSONIC_BATTLE:
case MT_METALSONIC_RACE:
mobj->skin = &skins[5];
/* FALLTHRU */
case MT_METALSONIC_BATTLE:
mobj->color = skins[5].prefcolor;
sc = 5;
break;
case MT_FANG:
@ -10474,6 +10539,12 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
if (!(mobj->flags & MF_NOTHINK))
P_AddThinker(THINK_MOBJ, &mobj->thinker);
if (mobj->skin) // correct inadequecies above.
{
mobj->sprite2 = P_GetSkinSprite2(mobj->skin, (mobj->frame & FF_FRAMEMASK), NULL);
mobj->frame &= ~FF_FRAMEMASK;
}
// Call action functions when the state is set
if (st->action.acp1 && (mobj->flags & MF_RUNSPAWNFUNC))
{
@ -11256,6 +11327,8 @@ void P_MovePlayerToSpawn(INT32 playernum, mapthing_t *mthing)
}
if (mthing->options & MTF_AMBUSH)
P_SetPlayerMobjState(mobj, S_PLAY_FALL);
else if (metalrecording)
P_SetPlayerMobjState(mobj, S_PLAY_WAIT);
}
else
z = floor;

View file

@ -2050,8 +2050,7 @@ void P_SpawnThokMobj(player_t *player)
mobj->eflags |= (player->mo->eflags & MFE_VERTICALFLIP);
// scale
P_SetScale(mobj, player->mo->scale);
mobj->destscale = player->mo->scale;
P_SetScale(mobj, (mobj->destscale = player->mo->scale));
if (type == MT_THOK) // spintrail-specific modification for MT_THOK
{
@ -2061,8 +2060,7 @@ void P_SpawnThokMobj(player_t *player)
}
P_SetTarget(&mobj->target, player->mo); // the one thing P_SpawnGhostMobj doesn't do
if (demorecording)
G_GhostAddThok();
G_GhostAddThok();
}
//
@ -4576,8 +4574,7 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd)
if (player->revitem && !(leveltime % 5)) // Now spawn the color thok circle.
{
P_SpawnSpinMobj(player, player->revitem);
if (demorecording)
G_GhostAddRev();
G_GhostAddRev();
}
}
@ -8346,8 +8343,7 @@ static void P_MovePlayer(player_t *player)
if (player->pflags & PF_SPINNING && P_AproxDistance(player->speed, player->mo->momz) > FixedMul(15<<FRACBITS, player->mo->scale) && !(player->pflags & PF_JUMPED))
{
P_SpawnSpinMobj(player, player->spinitem);
if (demorecording)
G_GhostAddSpin();
G_GhostAddSpin();
}
@ -11114,6 +11110,7 @@ static void P_DoMetalJetFume(player_t *player, mobj_t *fume)
}
fume->movecount = dashmode; // keeps track of previous dashmode value so we know whether Metal is entering or leaving it
fume->eflags = (fume->flags2 & ~MF2_OBJECTFLIP) | (mo->flags2 & MF2_OBJECTFLIP); // Make sure to flip in reverse gravity!
fume->eflags = (fume->eflags & ~MFE_VERTICALFLIP) | (mo->eflags & MFE_VERTICALFLIP); // Make sure to flip in reverse gravity!
// Finally, set its position
@ -11122,10 +11119,7 @@ static void P_DoMetalJetFume(player_t *player, mobj_t *fume)
P_UnsetThingPosition(fume);
fume->x = mo->x + P_ReturnThrustX(fume, angle, dist);
fume->y = mo->y + P_ReturnThrustY(fume, angle, dist);
if (fume->eflags & MFE_VERTICALFLIP)
fume->z = mo->z + ((mo->height + fume->height) >> 1);
else
fume->z = mo->z + ((mo->height - fume->height) >> 1);
fume->z = mo->z + ((mo->height - fume->height) >> 1);
P_SetThingPosition(fume);
}
@ -11517,7 +11511,7 @@ void P_PlayerThink(player_t *player)
// deez New User eXperiences.
{
angle_t diff = 0;
angle_t oldang = player->drawangle, diff = 0;
UINT8 factor;
// Directionchar!
// Camera angle stuff.
@ -11531,6 +11525,13 @@ void P_PlayerThink(player_t *player)
player->drawangle = player->mo->angle;
else if (P_PlayerInPain(player))
;
else if (player->powers[pw_justsprung]) // restricted, potentially by lua
{
#ifdef SPRINGSPIN
if (player->powers[pw_justsprung] & (1<<15))
player->drawangle += (player->powers[pw_justsprung] & ~(1<<15))*(ANG2+ANG1);
#endif
}
else if (player->powers[pw_carry] && player->mo->tracer) // carry
{
switch (player->powers[pw_carry])
@ -11568,13 +11569,6 @@ void P_PlayerThink(player_t *player)
break;
}
}
else if (player->powers[pw_justsprung])
{
#ifdef SPRINGSPIN
if (player->powers[pw_justsprung] & (1<<15))
player->drawangle += (player->powers[pw_justsprung] & ~(1<<15))*(ANG2+ANG1);
#endif
}
else if ((player->skidtime > (TICRATE/2 - 2) || ((player->pflags & (PF_SPINNING|PF_STARTDASH)) == PF_SPINNING)) && (abs(player->rmomx) > 5*player->mo->scale || abs(player->rmomy) > 5*player->mo->scale)) // spin/skid force
player->drawangle = R_PointToAngle2(0, 0, player->rmomx, player->rmomy);
else if (((player->charability2 == CA2_GUNSLINGER || player->charability2 == CA2_MELEE) && player->panim == PA_ABILITY2) || player->pflags & PF_STASIS || player->skidtime)
@ -11635,6 +11629,22 @@ void P_PlayerThink(player_t *player)
player->drawangle += diff;
}
// reset from waiting to standing when turning on the spot
if (player->panim == PA_IDLE)
{
diff = player->drawangle - oldang;
if (diff > ANGLE_180)
diff = InvAngle(diff);
if (diff > ANG10/2)
{
statenum_t stat = player->mo->state-states;
if (stat == S_PLAY_WAIT)
P_SetPlayerMobjState(player->mo, S_PLAY_STND);
else if (stat == S_PLAY_STND && player->mo->tics != -1)
player->mo->tics++;
}
}
// Autobrake! check ST_drawInput if you modify this
{
boolean currentlyonground = P_IsObjectOnGround(player->mo);
@ -11874,7 +11884,7 @@ void P_PlayerThink(player_t *player)
#define dashmode player->dashmode
// Dash mode - thanks be to VelocitOni
if ((player->charflags & SF_DASHMODE) && !player->gotflag && !player->powers[pw_carry] && !player->exiting && !(maptol & TOL_NIGHTS)) // woo, dashmode! no nights tho.
if ((player->charflags & SF_DASHMODE) && !player->gotflag && !player->powers[pw_carry] && !player->exiting && !(maptol & TOL_NIGHTS) && !metalrecording) // woo, dashmode! no nights tho.
{
boolean totallyradical = player->speed >= FixedMul(player->runspeed, player->mo->scale);
boolean floating = (player->secondjump == 1);

View file

@ -567,8 +567,12 @@ static void R_GenerateTranslationColormap(UINT8 *dest_colormap, INT32 skinnum, U
else if (skinnum == TC_METALSONIC)
{
for (i = 0; i < 6; i++)
{
dest_colormap[Color_Index[SKINCOLOR_BLUE-1][12-i]] = Color_Index[SKINCOLOR_BLUE-1][i];
}
dest_colormap[159] = dest_colormap[253] = dest_colormap[254] = 0;
for (i = 0; i < 16; i++)
dest_colormap[96+i] = dest_colormap[Color_Index[SKINCOLOR_COBALT-1][i]];
}
else if (skinnum == TC_DASHMODE) // This is a long one, because MotorRoach basically hand-picked the indices
{

View file

@ -2701,6 +2701,7 @@ boolean R_SkinUsable(INT32 playernum, INT32 skinnum)
|| (modeattacking) // If you have someone else's run you might as well take a look
|| (Playing() && (R_SkinAvailable(mapheaderinfo[gamemap-1]->forcecharacter) == skinnum)) // Force 1.
|| (netgame && (cv_forceskin.value == skinnum)) // Force 2.
|| (metalrecording && skinnum == 5) // Force 3.
);
}

View file

@ -2181,7 +2181,7 @@ void I_Quit(void)
if (demorecording)
G_CheckDemoStatus();
if (metalrecording)
G_StopMetalRecording();
G_StopMetalRecording(false);
D_QuitNetGame();
I_ShutdownMusic();
@ -2299,7 +2299,7 @@ void I_Error(const char *error, ...)
if (demorecording)
G_CheckDemoStatus();
if (metalrecording)
G_StopMetalRecording();
G_StopMetalRecording(false);
D_QuitNetGame();
I_ShutdownMusic();

View file

@ -837,7 +837,13 @@ static void ST_drawLivesArea(void)
}
// Lives number
if (G_GametypeUsesLives() || gametype == GT_RACE)
if (metalrecording)
{
if (((2*leveltime)/TICRATE) & 1)
V_DrawRightAlignedString(hudinfo[HUD_LIVES].x+58, hudinfo[HUD_LIVES].y+8,
hudinfo[HUD_LIVES].f|V_PERPLAYER|V_REDMAP|V_HUDTRANS, "REC");
}
else if (G_GametypeUsesLives() || gametype == GT_RACE)
{
// x
V_DrawScaledPatch(hudinfo[HUD_LIVES].x+22, hudinfo[HUD_LIVES].y+10,

View file

@ -647,7 +647,7 @@ void I_Error(const char *error, ...)
if (demorecording)
G_CheckDemoStatus();
if (metalrecording)
G_StopMetalRecording();
G_StopMetalRecording(false);
D_QuitNetGame();
@ -733,7 +733,7 @@ void I_Quit(void)
if (demorecording)
G_CheckDemoStatus();
if (metalrecording)
G_StopMetalRecording();
G_StopMetalRecording(false);
M_SaveConfig(NULL); // save game config, cvars..
#ifndef NONET