* Improve the Metal Sonic recording/playback system.

* Make the skin to record with Metal rather than Flesh Sonic. (Allowed even if not unlocked!)
    * Make the object that plays back the recording actually use the Metal skin, rather than just a seperate spriteset. (The boss will still need the spriteset, though.)
    * Actively record the player's sprite2, frame, and followmobj, just like regular ghosts do.
    * Disable dashmode while recording, for a fairer race.
    * Fix a probably long-standing bug where, while recording, being "hurt" would get Metal stuck in pain frames until they physically left the area of hurt.
    * Always start Metal recording in wait frames for bonus taunting.

Other relevant changes:
* Increment DEMOVERSION *again*.
* Improve the Record Attack ghost followmobj recording to accomodate Metal's jet.
* Increase the datatype width of spritenum_t read/write for Record Attack ghosts because SUGOI 4: Back With A Revengance will probably also use more than 255 sprites alone.
* Return to standing frames (or prolong them if you're in them, rather than going to wait frames) if the player rotates on the spot with enough force.
    * This was specifically done *for* Metal recording, but I decided it looked good enough to enable all the time.
This commit is contained in:
toaster 2019-10-28 22:12:47 +00:00
parent 97dd22e0e9
commit 1906709cf2
9 changed files with 239 additions and 140 deletions

View File

@ -1206,9 +1206,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

@ -5260,25 +5260,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

@ -4044,7 +4044,7 @@ char *G_BuildMapTitle(INT32 mapnum)
// DEMO RECORDING
//
#define DEMOVERSION 0x000a
#define DEMOVERSION 0x000b
#define DEMOHEADER "\xF0" "SRB2Replay" "\x0F"
#define DF_GHOST 0x01 // This demo contains ghost data too!
@ -4068,7 +4068,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
@ -4086,6 +4085,14 @@ static ticcmd_t oldcmd;
#define EZT_SPRITE 0x40 // Changed sprite set completely out of PLAY (NiGHTS, SOCs, whatever)
// spare EZT slot 0x80
// 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;
void G_SaveMetal(UINT8 **buffer)
@ -4405,26 +4412,54 @@ void G_WriteGhostTic(mobj_t *ghost)
ghostext.hitlist = NULL;
}
if (ghostext.flags & EZT_SPRITE)
WRITEUINT8(demo_p,oldghost.sprite);
WRITEUINT16(demo_p,oldghost.sprite);
ghostext.flags = 0;
}
if (ghost->player && ghost->player->followmobj) // 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;
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;
@ -4528,17 +4563,24 @@ void G_ConsGhostTic(void)
}
}
if (xziptic & EZT_SPRITE)
demo_p++;
demo_p += sizeof(UINT16);
}
if (ziptic & GZT_FOLLOW)
{ // Even more...
UINT8 followtic = READUINT8(demo_p);
if ((followtic & (FZT_SPAWNED|FZT_SKIN)) == (FZT_SPAWNED|FZT_SKIN))
demo_p++;
demo_p += sizeof(INT16);
demo_p += sizeof(INT16);
demo_p += sizeof(INT16);
if (followtic & FZT_SKIN)
demo_p++;
demo_p += sizeof(UINT16);
demo_p++;
demo_p++;
demo_p++;
if (followtic & FZT_SCALE)
demo_p += sizeof(fixed_t);
}
// Re-synchronise
@ -4737,7 +4779,7 @@ void G_GhostTicker(void)
}
}
if (xziptic & EZT_SPRITE)
g->mo->sprite = READUINT8(g->p);
g->mo->sprite = READUINT16(g->p);
}
// Tick ghost colors (Super and Mario Invincibility flashing)
@ -4763,43 +4805,52 @@ void G_GhostTicker(void)
#define follow g->mo->tracer
if (ziptic & GZT_FOLLOW)
{ // Even more...
if (!follow)
UINT8 followtic = READUINT8(g->p);
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_SpawnMobj(g->mo->x, g->mo->y, g->mo->z, MT_GHOST));
P_SetTarget(&follow->tracer, g->mo);
follow->tics = -1;
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)];
follow->eflags = (follow->eflags & ~MFE_VERTICALFLIP)|(g->mo->eflags & MFE_VERTICALFLIP);
follow->destscale = g->mo->destscale;
if (follow->destscale != follow->scale)
P_SetScale(follow, follow->destscale);
}
else
if (follow)
{
if (xziptic & EZT_FLIP)
g->mo->eflags ^= MFE_VERTICALFLIP;
if (xziptic & EZT_SCALE)
if (!(followtic & FZT_SPAWNED))
{
follow->destscale = g->mo->destscale;
if (follow->destscale != follow->scale)
P_SetScale(follow, follow->destscale);
if (xziptic & EZT_FLIP)
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);
if (followtic & FZT_SKIN)
follow->sprite2 = READUINT8(g->p);
else
follow->sprite2 = 0;
follow->sprite = READUINT16(g->p);
follow->frame = (READUINT8(g->p)) | tr_trans30<<FF_TRANSSHIFT;
follow->angle = g->mo->angle;
follow->color = READUINT8(g->p);
if (followtic & FZT_SCALE)
P_SetScale(follow, (follow->destscale = READFIXED(g->p)));
else
P_SetScale(follow, (follow->destscale = g->mo->destscale));
}
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)
{
@ -4826,11 +4877,11 @@ void G_GhostTicker(void)
void G_ReadMetalTic(mobj_t *metal)
{
UINT8 ziptic;
UINT16 speed;
UINT8 statetype;
UINT8 xziptic = 0;
if (!metal_p)
return;
metal_p++;
ziptic = READUINT8(metal_p);
// Read changes from the tic
@ -4857,9 +4908,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.
@ -4871,13 +4922,15 @@ 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)
@ -4885,36 +4938,52 @@ void G_ReadMetalTic(mobj_t *metal)
}
}
// Calculates player's speed based on distance-of-a-line formula
speed = FixedDiv(P_AproxDistance(oldmetal.momx, oldmetal.momy), metal->scale)>>FRACBITS;
#define follow metal->tracer
if (ziptic & GZT_FOLLOW)
{ // Even more...
UINT8 followtic = READUINT8(metal_p);
if (followtic & FZT_SPAWNED)
{
if (follow)
P_RemoveMobj(follow);
P_SetTarget(&follow, P_SpawnMobj(metal->x, metal->y, metal->z, MT_GHOST));
P_SetTarget(&follow->tracer, metal);
follow->tics = -1;
// 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;
if (followtic & FZT_COLORIZED)
follow->colorized = true;
// 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;
follow->eflags = (follow->eflags & ~MFE_VERTICALFLIP)|(metal->eflags & MFE_VERTICALFLIP);
}
if (follow)
{
if (!(followtic & FZT_SPAWNED))
{
if (xziptic & EZT_FLIP)
follow->eflags ^= MFE_VERTICALFLIP;
}
P_UnsetThingPosition(follow);
follow->x = metal->x + (READINT16(metal_p)<<8);
follow->y = metal->y + (READINT16(metal_p)<<8);
follow->z = metal->z + (READINT16(metal_p)<<8);
P_SetThingPosition(follow);
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_SCALE)
P_SetScale(follow, (follow->destscale = READFIXED(metal_p)));
else
P_SetScale(follow, (follow->destscale = metal->destscale));
}
}
metal->threshold = statetype;
}
// TODO: Modify state durations based on movement speed, similar to players?
else if (follow)
{
P_RemoveMobj(follow);
P_SetTarget(&follow, NULL);
}
#undef follow
if (*metal_p == DEMOMARKER)
{
@ -4931,7 +5000,7 @@ void G_WriteMetalTic(mobj_t *metal)
if (!demo_p) // demo_p will be NULL until the race start linedef executor is triggered!
return;
demo_p++;
ziptic_p = demo_p++; // the ziptic, written at the end of this function
#define MAXMOM (0xFFFF<<8)
@ -4944,10 +5013,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
{
@ -4960,16 +5029,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
@ -4992,8 +5061,21 @@ 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)
{
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);
}
{
UINT8 *exttic_p = NULL;
@ -5020,6 +5102,48 @@ void G_WriteMetalTic(mobj_t *metal)
}
}
if (metal->player && metal->player->followmobj)
{
INT16 temp;
UINT8 *followtic_p = demo_p++;
UINT8 followtic = 0;
ziptic |= GZT_FOLLOW;
if (!(oldmetal.flags2 & MF2_AMBUSH))
{
followtic |= FZT_SPAWNED;
/*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;
}
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);
if (metal->player->followmobj->scale != metal->scale)
{
followtic |= FZT_SCALE;
WRITEFIXED(demo_p,metal->player->followmobj->scale);
}
*followtic_p = followtic;
}
else
oldmetal.flags2 &= ~MF2_AMBUSH;
*ziptic_p = ziptic;
// attention here for the ticcmd size!

View File

@ -1800,21 +1800,7 @@ 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
@ -6677,16 +6663,16 @@ 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_NULL, // xdeathstate

View File

@ -1933,25 +1933,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

@ -3615,11 +3615,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

@ -10399,8 +10399,13 @@ 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];
mobj->color = skins[5].prefcolor;
mobj->sprite2 = P_GetSkinSprite2(mobj->skin, mobj->frame & FF_FRAMEMASK, NULL);
mobj->frame &= ~FF_FRAMEMASK;
/* FALLTHRU */
case MT_METALSONIC_BATTLE:
sc = 5;
break;
case MT_FANG:
@ -11248,6 +11253,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

@ -11455,7 +11455,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.
@ -11573,6 +11573,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++;
}
}
// Autobrake! check ST_drawInput if you modify this
{
boolean currentlyonground = P_IsObjectOnGround(player->mo);
@ -11812,7 +11828,7 @@ void P_PlayerThink(player_t *player)
#define dashmode player->dashmode
// Dash mode - thanks be to Iceman404
if ((player->charflags & SF_DASHMODE) && !(player->gotflag) && !(maptol & TOL_NIGHTS)) // woo, dashmode! no nights tho.
if ((player->charflags & SF_DASHMODE) && !(player->gotflag) && !(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

@ -2692,6 +2692,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.
);
}