From 1906709cf2f555c8cfbfe95f322078bc619b4e93 Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 28 Oct 2019 22:12:47 +0000 Subject: [PATCH 1/9] * 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. --- src/d_netcmd.c | 6 +- src/dehacked.c | 20 +--- src/g_game.c | 278 +++++++++++++++++++++++++++++++++++-------------- src/info.c | 22 +--- src/info.h | 20 +--- src/p_inter.c | 3 +- src/p_mobj.c | 9 +- src/p_user.c | 20 +++- src/r_things.c | 1 + 9 files changed, 239 insertions(+), 140 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index b14f92b33..5c8c327c8 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -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)) { diff --git a/src/dehacked.c b/src/dehacked.c index 34ee1f170..6369d5d7f 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -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", diff --git a/src/g_game.c b/src/g_game.c index e2f43e4f2..27cece73d 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -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<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<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! diff --git a/src/info.c b/src/info.c index cfaad552d..99cd12862 100644 --- a/src/info.c +++ b/src/info.c @@ -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 diff --git a/src/info.h b/src/info.h index e7f41f585..bc3626b92 100644 --- a/src/info.h +++ b/src/info.h @@ -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, diff --git a/src/p_inter.c b/src/p_inter.c index 9017f795d..44215d691 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -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 { diff --git a/src/p_mobj.c b/src/p_mobj.c index 5735dc27b..b74e08e54 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -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; diff --git a/src/p_user.c b/src/p_user.c index f81f6d956..a4e500c7c 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -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); diff --git a/src/r_things.c b/src/r_things.c index 00eaae1c2..22f9c5d89 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -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. ); } From f0f8b544deb9b7006a11bcbc629ba591b071688c Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 29 Oct 2019 15:32:22 +0000 Subject: [PATCH 2/9] * Add thok auras to Metal playback. * Add height changes to demos and Metal playback (to properly place thok aura relative to object). * Fix followmobj recordings in reverse gravity (done via more strenuous flag setting + info height serialisation). * Make RA ghosts fade in from start tic, and go colorized on their final one. * Fix thok auras potentially doing bad things if they're set to MT_NULL. * Add sprite change support to Metal playback. * Re-enable some stuff I previously disabled for Metal recording/playback. * Make ALL objects spawned with skins properly acknowledge that their spawnstate's frame is a sprite2, rather than just specific objects. * Fix placement of Metal Sonic fume in reverse gravity. * Since not backwards compatible with the previous test build, increment DEMOVERSION again. (Don't worry, we've got like 65524 more versions allowed before we run out of possible DEMOVERSIONs...) --- src/g_game.c | 361 ++++++++++++++++++++++++++++++++++++--------------- src/p_mobj.c | 8 +- src/p_user.c | 18 +-- 3 files changed, 270 insertions(+), 117 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 27cece73d..b5e8a9509 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -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; @@ -4044,7 +4044,7 @@ char *G_BuildMapTitle(INT32 mapnum) // DEMO RECORDING // -#define DEMOVERSION 0x000b +#define DEMOVERSION 0x000c #define DEMOHEADER "\xF0" "SRB2Replay" "\x0F" #define DF_GHOST 0x01 // This demo contains ghost data too! @@ -4083,7 +4083,7 @@ 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 @@ -4217,28 +4217,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; } @@ -4258,7 +4258,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) { @@ -4284,6 +4284,7 @@ void G_WriteGhostTic(mobj_t *ghost) char ziptic = 0; UINT8 *ziptic_p; UINT32 i; + fixed_t height; if (!demo_p) return; @@ -4373,6 +4374,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; @@ -4413,10 +4420,15 @@ void G_WriteGhostTic(mobj_t *ghost) } if (ghostext.flags & EZT_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++; @@ -4430,6 +4442,7 @@ void G_WriteGhostTic(mobj_t *ghost) 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) @@ -4564,13 +4577,21 @@ void G_ConsGhostTic(void) } if (xziptic & EZT_SPRITE) 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|FZT_SKIN)) == (FZT_SPAWNED|FZT_SKIN)) - 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); @@ -4579,8 +4600,6 @@ void G_ConsGhostTic(void) demo_p += sizeof(UINT16); demo_p++; demo_p++; - if (followtic & FZT_SCALE) - demo_p += sizeof(fixed_t); } // Re-synchronise @@ -4666,6 +4685,11 @@ void G_GhostTicker(void) g->mo->z = g->oldmo.z; P_SetThingPosition(g->mo); g->mo->frame = g->oldmo.frame | tr_trans30<fadein) + { + g->mo->frame += (((--g->fadein)/6)<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) @@ -4723,33 +4747,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<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<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<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<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! @@ -4780,6 +4809,11 @@ void G_GhostTicker(void) } if (xziptic & EZT_SPRITE) g->mo->sprite = READUINT16(g->p); + if (xziptic & EZT_HEIGHT) + { + fixed_t temp = READINT16(g->p)<mo->height = FixedMul(temp, g->mo->scale); + } } // Tick ghost colors (Super and Mario Invincibility flashing) @@ -4806,13 +4840,16 @@ void G_GhostTicker(void) if (ziptic & GZT_FOLLOW) { // Even more... UINT8 followtic = READUINT8(g->p); + fixed_t temp; if (followtic & FZT_SPAWNED) { if (follow) P_RemoveMobj(follow); - P_SetTarget(&follow, P_SpawnMobj(g->mo->x, g->mo->y, g->mo->z, MT_GHOST)); + 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)<height = FixedMul(follow->scale, temp); if (followtic & FZT_LINKDRAW) follow->flags2 |= MF2_LINKDRAW; @@ -4822,34 +4859,41 @@ void G_GhostTicker(void) if (followtic & FZT_SKIN) follow->skin = &skins[READUINT8(g->p)]; - - follow->eflags = (follow->eflags & ~MFE_VERTICALFLIP)|(g->mo->eflags & MFE_VERTICALFLIP); } if (follow) { - if (!(followtic & FZT_SPAWNED)) - { - if (xziptic & EZT_FLIP) - follow->eflags ^= MFE_VERTICALFLIP; - } + 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); 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); + 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)) | tr_trans30<frame = (READUINT8(g->p)) | (g->mo->frame & FF_TRANSMASK); 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)); + + if (!(followtic & FZT_SPAWNED)) + { + if (xziptic & EZT_FLIP) + { + follow->flags2 ^= MF2_OBJECTFLIP; + follow->eflags ^= MFE_VERTICALFLIP; + } + } } } else if (follow) @@ -4857,12 +4901,20 @@ void G_GhostTicker(void) 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 @@ -4871,6 +4923,7 @@ void G_GhostTicker(void) continue; } p = g; +#undef follow } } @@ -4936,46 +4989,126 @@ void G_ReadMetalTic(mobj_t *metal) 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)); + + 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)<height = FixedMul(temp, metal->scale); + } } #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_SpawnMobj(metal->x, metal->y, metal->z, MT_GHOST)); + P_SetTarget(&follow, P_SpawnMobjFromMobj(metal, 0, 0, 0, MT_GHOST)); P_SetTarget(&follow->tracer, metal); follow->tics = -1; + temp = READINT16(metal_p)<height = FixedMul(follow->scale, temp); + + if (followtic & FZT_LINKDRAW) + follow->flags2 |= MF2_LINKDRAW; if (followtic & FZT_COLORIZED) follow->colorized = true; - follow->eflags = (follow->eflags & ~MFE_VERTICALFLIP)|(metal->eflags & MFE_VERTICALFLIP); + if (followtic & FZT_SKIN) + follow->skin = &skins[READUINT8(metal_p)]; } if (follow) { - if (!(followtic & FZT_SPAWNED)) - { - if (xziptic & EZT_FLIP) - follow->eflags ^= MFE_VERTICALFLIP; - } + 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); - follow->x = metal->x + (READINT16(metal_p)<<8); - follow->y = metal->y + (READINT16(metal_p)<<8); - follow->z = metal->z + (READINT16(metal_p)<<8); + 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_SCALE) - P_SetScale(follow, (follow->destscale = READFIXED(metal_p))); - else - P_SetScale(follow, (follow->destscale = metal->destscale)); + + if (!(followtic & FZT_SPAWNED)) + { + if (xziptic & EZT_FLIP) + { + follow->flags2 ^= MF2_OBJECTFLIP; + follow->eflags ^= MFE_VERTICALFLIP; + } + } } } else if (follow) @@ -4997,8 +5130,9 @@ 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; demo_p++; ziptic_p = demo_p++; // the ziptic, written at the end of this function @@ -5077,32 +5211,43 @@ void G_WriteMetalTic(mobj_t *metal) WRITEUINT8(demo_p,oldmetal.sprite2); } + // Check for sprite set changes + if (metal->sprite != oldmetal.sprite) { - 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.sprite = metal->sprite; + ghostext.flags |= EZT_SPRITE; } - if (metal->player && metal->player->followmobj) + 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++; @@ -5110,34 +5255,39 @@ void G_WriteMetalTic(mobj_t *metal) ziptic |= GZT_FOLLOW; + if (metal->player->followmobj->skin) + followtic |= FZT_SKIN; + if (!(oldmetal.flags2 & MF2_AMBUSH)) { followtic |= FZT_SPAWNED; - /*if (metal->player->followmobj->flags2 & MF2_LINKDRAW) - followtic |= FZT_LINKDRAW;*/ + 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));*/ + 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);*/ + 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; } @@ -5344,6 +5494,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; @@ -5992,7 +6145,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<mo->frame = tr_trans30<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); diff --git a/src/p_mobj.c b/src/p_mobj.c index b74e08e54..c87f0bd3c 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -10402,8 +10402,6 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) 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; @@ -10471,6 +10469,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)) { diff --git a/src/p_user.c b/src/p_user.c index a4e500c7c..7ac9b34ed 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -2048,8 +2048,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 { @@ -2059,8 +2058,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(); } // @@ -4574,8 +4572,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(); } } @@ -8325,8 +8322,7 @@ static void P_MovePlayer(player_t *player) if (player->pflags & PF_SPINNING && P_AproxDistance(player->speed, player->mo->momz) > FixedMul(15<mo->scale) && !(player->pflags & PF_JUMPED)) { P_SpawnSpinMobj(player, player->spinitem); - if (demorecording) - G_GhostAddSpin(); + G_GhostAddSpin(); } @@ -11052,6 +11048,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 @@ -11060,10 +11057,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); } From d7ea986d7bfc99facf0b5f14029019373878cc77 Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 29 Oct 2019 15:59:59 +0000 Subject: [PATCH 3/9] I was right on the money - SendWeaponPref seems to have been delayed compared to earlier in development. Doesn't matter a lick for regular gameplay (net or otherwise), but absolutely mandatory to get it sorted ASAP in Record Attack. --- src/g_game.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index b5e8a9509..52a2d7566 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -5434,22 +5434,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 From 2d1a574e097f81909b7f0b138efb025e31745e60 Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 29 Oct 2019 17:38:14 +0000 Subject: [PATCH 4/9] * Add a death animation for killing the Metal object, in case somebody wants to Lua up a Sonic CD style race finish for the Metal object, or in case of the following... * Add an alternate DEMOMARKER for ending the Metal Recording on death, which kills the Metal object as well. * Add some more relevant exceptions to the "most objects are removed when touching a deathpit" thing, primarily for the sake of ghosts and Metal playback. --- src/d_clisrv.c | 2 +- src/g_game.c | 50 ++++++++++++++++++++++++++++++--------------- src/g_game.h | 2 +- src/info.c | 4 ++-- src/p_inter.c | 8 +++++++- src/p_mobj.c | 49 +++++++++++++++++++++++++++++++++----------- src/sdl/i_system.c | 4 ++-- src/win32/win_sys.c | 4 ++-- 8 files changed, 85 insertions(+), 38 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 411d847b5..860bde624 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -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) diff --git a/src/g_game.c b/src/g_game.c index 52a2d7566..10e841f64 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -3053,7 +3053,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]) @@ -4060,6 +4060,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; @@ -4901,7 +4903,6 @@ void G_GhostTicker(void) P_RemoveMobj(follow); P_SetTarget(&follow, NULL); } - // Demo ends after ghost data. if (*g->p == DEMOMARKER) { @@ -4934,7 +4935,24 @@ void G_ReadMetalTic(mobj_t *metal) if (!metal_p) 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 @@ -5117,13 +5135,6 @@ void G_ReadMetalTic(mobj_t *metal) P_SetTarget(&follow, NULL); } #undef follow - - if (*metal_p == DEMOMARKER) - { - // end of demo data stream - G_StopMetalDemo(); - return; - } } void G_WriteMetalTic(mobj_t *metal) @@ -5134,7 +5145,8 @@ void G_WriteMetalTic(mobj_t *metal) if (!demo_p) // demo_p will be NULL until the race start linedef executor is activated! return; - demo_p++; + + WRITEUINT8(demo_p, METALSNICE); ziptic_p = demo_p++; // the ziptic, written at the end of this function #define MAXMOM (0xFFFF<<8) @@ -5300,7 +5312,7 @@ void G_WriteMetalTic(mobj_t *metal) // 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; } } @@ -6282,19 +6294,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. diff --git a/src/g_game.h b/src/g_game.h index 198cbc396..8c775c238 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -174,7 +174,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); diff --git a/src/info.c b/src/info.c index 99cd12862..a418b1c51 100644 --- a/src/info.c +++ b/src/info.c @@ -6674,7 +6674,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate - S_NULL, // deathstate + S_PLAY_DEAD, // deathstate S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed @@ -6684,7 +6684,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 }, diff --git a/src/p_inter.c b/src/p_inter.c index 44215d691..e25fb395a 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -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 @@ -2761,6 +2761,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; } diff --git a/src/p_mobj.c b/src/p_mobj.c index c87f0bd3c..64c8d61d3 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -2570,19 +2570,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; } } @@ -8271,6 +8282,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; } diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index d7926e5b2..e7f8f2e4f 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -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(); diff --git a/src/win32/win_sys.c b/src/win32/win_sys.c index 93b3ff523..c9fdb1c97 100644 --- a/src/win32/win_sys.c +++ b/src/win32/win_sys.c @@ -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 From 56d095e39fa6a576a03c9cf568d307db829a1dcc Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 29 Oct 2019 18:17:04 +0000 Subject: [PATCH 5/9] Finish the Metal demo if the Metal playback object is dead. --- src/g_game.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/g_game.c b/src/g_game.c index 10e841f64..569d777ab 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -4936,6 +4936,12 @@ void G_ReadMetalTic(mobj_t *metal) if (!metal_p) return; + if (!metal->health) + { + G_StopMetalDemo(); + return; + } + switch (*metal_p) { case METALSNICE: From 4d55a9f4a9f7db942aa8b04bfdc36a29e2db0594 Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 29 Oct 2019 23:11:12 +0000 Subject: [PATCH 6/9] Metal Sonic battle improvements. * Tweaked to use new sprites. * Jet fume behaves much closer to Lach's wonderful work. * Instead of clobbering tracer to spawn powerup overlay, use hprev. * Change timings. One second less to charge up, but two seconds less to spin them out. * Remove terrible pinch overlay in favour of new dashmode-mimic flashing. --- src/info.c | 32 +++++++++--------- src/p_enemy.c | 5 +-- src/p_mobj.c | 91 +++++++++++++++++++++++++++++++++------------------ src/r_draw.c | 4 +++ 4 files changed, 83 insertions(+), 49 deletions(-) diff --git a/src/info.c b/src/info.c index a418b1c51..1064eaf42 100644 --- a/src/info.c +++ b/src/info.c @@ -1803,21 +1803,21 @@ state_t states[NUMSTATES] = {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 @@ -6734,7 +6734,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 diff --git a/src/p_enemy.c b/src/p_enemy.c index cc2d64e8b..9d2a8e95a 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -8966,10 +8966,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 { diff --git a/src/p_mobj.c b/src/p_mobj.c index 64c8d61d3..b7103ce65 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -5595,16 +5595,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)); @@ -5618,7 +5624,7 @@ static void P_Boss9Thinker(mobj_t *mobj) return; } else if (!mobj->fuse) - mobj->fuse = 10*TICRATE; + mobj->fuse = 8*TICRATE; } // AI goes here. @@ -5645,16 +5651,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) @@ -5742,6 +5750,7 @@ static void P_Boss9Thinker(mobj_t *mobj) S_StartSound(mobj, sfx_s3kb3); } } + } // up... mobj->z += mobj->height/2; @@ -5768,12 +5777,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; @@ -5885,8 +5894,6 @@ static void P_Boss9Thinker(mobj_t *mobj) return; } - P_SpawnGhostMobj(mobj); - // Pinball attack! if (mobj->movecount == 3 && (mobj->movedir == 0 || mobj->movedir == 2)) { @@ -5901,20 +5908,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 @@ -5931,6 +5938,8 @@ static void P_Boss9Thinker(mobj_t *mobj) return; } + P_SpawnGhostMobj(mobj); + // Vector form dodge! mobj->angle += mobj->movedir; P_InstaThrust(mobj, mobj->angle, -speed); @@ -6027,7 +6036,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! @@ -6058,14 +6067,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); @@ -6076,14 +6086,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; @@ -6099,6 +6109,7 @@ static void P_Boss9Thinker(mobj_t *mobj) 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; @@ -6106,9 +6117,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 { @@ -6123,7 +6138,7 @@ static void P_Boss9Thinker(mobj_t *mobj) 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++; @@ -8738,11 +8753,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 @@ -8750,6 +8771,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++; } @@ -10426,9 +10455,9 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) break; case MT_METALSONIC_RACE: mobj->skin = &skins[5]; - mobj->color = skins[5].prefcolor; /* FALLTHRU */ case MT_METALSONIC_BATTLE: + mobj->color = skins[5].prefcolor; sc = 5; break; case MT_FANG: diff --git a/src/r_draw.c b/src/r_draw.c index bb70a319f..6fc8d6599 100644 --- a/src/r_draw.c +++ b/src/r_draw.c @@ -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 { From 04f400e048a3b5e7419deefb281f5524a02e9220 Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 29 Oct 2019 23:24:00 +0000 Subject: [PATCH 7/9] Give the clone fighter's vectorisation colour, rather than forcing greyscale. --- src/p_mobj.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index b7103ce65..2a8d22b03 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -5938,7 +5938,7 @@ static void P_Boss9Thinker(mobj_t *mobj) return; } - P_SpawnGhostMobj(mobj); + P_SpawnGhostMobj(mobj)->colorized = false; // Vector form dodge! mobj->angle += mobj->movedir; From fc22d1adc3f745f07fec02db14da7d4b7571a8f4 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 2 Nov 2019 11:56:51 +0000 Subject: [PATCH 8/9] * Restart Metal recording from beginning if retry is used. * Disable lives when Metal recording (and have a big flashing REC instead, because I'm a riot). * Correct some minor directionchar issues (some introduced in this branch, some not). --- src/g_game.c | 6 +++++- src/p_user.c | 16 ++++++++-------- src/st_stuff.c | 8 +++++++- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 569d777ab..44aaf7b12 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -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; @@ -5510,8 +5512,10 @@ void G_BeginMetal(void) { mobj_t *mo = players[consoleplayer].mo; +#if 0 if (demo_p) return; +#endif demo_p = demobuffer; diff --git a/src/p_user.c b/src/p_user.c index 7ac9b34ed..3849dd199 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -11463,6 +11463,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]) @@ -11500,13 +11507,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) @@ -11578,7 +11578,7 @@ void P_PlayerThink(player_t *player) 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) + else if (stat == S_PLAY_STND && player->mo->tics != -1) player->mo->tics++; } } diff --git a/src/st_stuff.c b/src/st_stuff.c index 392cb1c03..139303fbb 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -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, From bb182b9ec7c17c6f1f62c1a528ec2d50bc2e91eb Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 8 Nov 2019 13:58:53 +0000 Subject: [PATCH 9/9] Resolve #283. --- src/p_mobj.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index 2a8d22b03..395d434c1 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -5585,9 +5585,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; @@ -5801,8 +5801,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); @@ -6106,7 +6108,10 @@ 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 @@ -6135,7 +6140,10 @@ 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 = 8*TICRATE;