From 471ae8ecb4b146fffd1749cf7465a4ac3b7f86b6 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 13 Nov 2019 16:33:53 +0000 Subject: [PATCH 01/17] Paper sign tweaks. * Add "twinkling" and "landing sounds to paper signpost. * Fix whitespace on definitions. * It says locvar2 is unused in the comment for A_SignSpin, but it's used for the "doesn't have a spawnpoint" thing. Make it deathstate instead, so the object doesn't disappear. --- src/info.c | 28 ++++++++++++++-------------- src/p_enemy.c | 14 ++++++++++++-- src/sounds.c | 2 +- 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/src/info.c b/src/info.c index 698e6c25f..f00d32e0a 100644 --- a/src/info.c +++ b/src/info.c @@ -1911,18 +1911,18 @@ state_t states[NUMSTATES] = {SPR_BBLS, 3, 8, {A_BubbleCheck}, 0, 0, S_BUBBLES1}, // S_BUBBLES4 // Level End Sign - {SPR_SIGN, 0, -1, {A_SignPlayer}, -3, 0, S_NULL}, // S_SIGN - {SPR_SIGN, 0, 1, {A_SignSpin}, 30, 0, S_SIGNSPIN2}, // S_SIGNSPIN1 - {SPR_SIGN, 0, 0, {A_Repeat}, 4, S_SIGNSPIN1, S_SIGNSPIN3}, // S_SIGNSPIN2 - {SPR_SIGN, 0, 0, {A_SignPlayer}, -2, 0, S_SIGNSPIN4}, // S_SIGNSPIN3 - {SPR_SIGN, 0, 1, {A_SignSpin}, 30, 0, S_SIGNSPIN5}, // S_SIGNSPIN4 - {SPR_SIGN, 0, 0, {A_Repeat}, 4, S_SIGNSPIN4, S_SIGNSPIN6}, // S_SIGNSPIN5 - {SPR_SIGN, 0, 0, {A_SignPlayer}, -3, 0, S_SIGNSPIN1}, // S_SIGNSPIN6 - {SPR_SIGN, 0, 1, {A_SignPlayer}, -1, 0, S_SIGNSLOW}, // S_SIGNPLAYER - {SPR_SIGN, 0, 1, {A_SignSpin}, 30, 0, S_SIGNSLOW}, // S_SIGNSLOW - {SPR_SIGN, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SIGNSTOP - {SPR_SIGN, FF_PAPERSPRITE|2, -1, {NULL}, 0, 0, S_NULL}, // S_SIGNBOARD - {SPR_SIGN, FF_PAPERSPRITE|1, -1, {NULL}, 0, 29, S_NULL}, // S_EGGMANSIGN + {SPR_SIGN, 0, -1, {A_SignPlayer}, -3, 0, S_NULL}, // S_SIGN + {SPR_SIGN, 0, 1, {A_SignSpin}, 30, 0, S_SIGNSPIN2}, // S_SIGNSPIN1 + {SPR_SIGN, 0, 0, {A_Repeat}, 4, S_SIGNSPIN1, S_SIGNSPIN3}, // S_SIGNSPIN2 + {SPR_SIGN, 0, 0, {A_SignPlayer}, -2, 0, S_SIGNSPIN4}, // S_SIGNSPIN3 + {SPR_SIGN, 0, 1, {A_SignSpin}, 30, 0, S_SIGNSPIN5}, // S_SIGNSPIN4 + {SPR_SIGN, 0, 0, {A_Repeat}, 4, S_SIGNSPIN4, S_SIGNSPIN6}, // S_SIGNSPIN5 + {SPR_SIGN, 0, 0, {A_SignPlayer}, -3, 0, S_SIGNSPIN1}, // S_SIGNSPIN6 + {SPR_SIGN, 0, 1, {A_SignPlayer}, -1, 0, S_SIGNSLOW}, // S_SIGNPLAYER + {SPR_SIGN, 0, 1, {A_SignSpin}, 30, 0, S_SIGNSLOW}, // S_SIGNSLOW + {SPR_SIGN, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SIGNSTOP + {SPR_SIGN, FF_PAPERSPRITE|2, -1, {NULL}, 0, 0, S_NULL}, // S_SIGNBOARD + {SPR_SIGN, FF_PAPERSPRITE|1, -1, {NULL}, 0, 29, S_NULL}, // S_EGGMANSIGN // Spike Ball {SPR_SPIK, 0, 1, {NULL}, 0, 0, S_SPIKEBALL2}, // S_SPIKEBALL1 @@ -7794,12 +7794,12 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = sfx_None, // attacksound S_SIGNPLAYER, // painstate MT_SPARK, // painchance - sfx_None, // painsound + sfx_s3kb8, // painsound S_EGGMANSIGN, // meleestate S_NULL, // missilestate S_SIGNSTOP, // deathstate S_NULL, // xdeathstate - sfx_None, // deathsound + sfx_s3k64, // deathsound 8, // speed 36*FRACUNIT, // radius 32*FRACUNIT, // height diff --git a/src/p_enemy.c b/src/p_enemy.c index 27033acbc..3cb2b3354 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -5031,7 +5031,6 @@ void A_UnsetSolidSteam(mobj_t *actor) void A_SignSpin(mobj_t *actor) { INT32 locvar1 = var1; - INT32 locvar2 = var2; INT16 i; angle_t rotateangle = FixedAngle(locvar1 << FRACBITS); @@ -5042,6 +5041,11 @@ void A_SignSpin(mobj_t *actor) if (P_IsObjectOnGround(actor) && P_MobjFlip(actor) * actor->momz <= 0) { + if (actor->flags2 & MF2_BOSSFLEE) + { + S_StartSound(actor, actor->info->deathsound); + actor->flags2 &= ~MF2_BOSSFLEE; + } if (actor->spawnpoint) { angle_t mapangle = FixedAngle(actor->spawnpoint->angle << FRACBITS); @@ -5058,14 +5062,20 @@ void A_SignSpin(mobj_t *actor) } else // no mapthing? just finish in your current angle { - P_SetMobjState(actor, locvar2); + P_SetMobjState(actor, actor->info->deathstate); return; } } else { + if (!(actor->flags2 & MF2_BOSSFLEE)) + { + S_StartSound(actor, actor->info->painsound); + actor->flags2 |= MF2_BOSSFLEE; + } actor->movedir = rotateangle; } + actor->angle += actor->movedir; if (actor->tracer == NULL || P_MobjWasRemoved(actor->tracer)) return; for (i = -1; i < 2; i += 2) diff --git a/src/sounds.c b/src/sounds.c index 197bfc066..b067903b1 100644 --- a/src/sounds.c +++ b/src/sounds.c @@ -578,7 +578,7 @@ sfxinfo_t S_sfx[NUMSFX] = {"s3kb5", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Clink"}, {"s3kb6", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Spin launch"}, {"s3kb7", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Tumbler"}, - {"s3kb8", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Falling signpost"}, + {"s3kb8", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Spinning signpost"}, {"s3kb9", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Ring loss"}, {"s3kba", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Flight"}, {"s3kbb", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Tired flight"}, From b4da50b7fb5861e16a856746ab95ad1c6bbcc827 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 13 Nov 2019 16:51:50 +0000 Subject: [PATCH 02/17] When you have the skin to set to in A_SignPlayer be "next skin", it's possible the initial skin to pick is a not-available character. This commit prevents that. --- src/p_enemy.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/p_enemy.c b/src/p_enemy.c index 3cb2b3354..564b9bb31 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -5165,15 +5165,26 @@ void A_SignPlayer(mobj_t *actor) // I turned this function into a fucking mess. I'm so sorry. -Lach if (locvar1 == -2) // next skin { + player_t *player = actor->target ? actor->target->player : NULL; + UINT8 skinnum; +#define skincheck(num) (player ? !R_SkinUsable(player-players, num) : skins[num].availability > 0) if (ov->skin == NULL) // pick a random skin to start with! - skin = &skins[P_RandomKey(numskins)]; + { + UINT8 skincount = 0; + for (skincount = 0; skincount < numskins; skincount++) + if (!skincheck(skincount)) + skincount++; + skinnum = P_RandomKey(skincount); + for (skincount = skinnum; skincount < numskins; skincount++) + if (skincheck(skinnum)) + skinnum++; + } else // otherwise, advance 1 skin { - UINT8 skinnum = (skin_t*)ov->skin-skins; - player_t *player = actor->target ? actor->target->player : NULL; - while ((skinnum = (skinnum + 1) % numskins) && (player ? !R_SkinUsable(player-players, skinnum) : skins[skinnum].availability > 0)); - skin = &skins[skinnum]; + skinnum = (skin_t*)ov->skin-skins; + while ((skinnum = (skinnum + 1) % numskins) && skincheck(skinnum)); } + skin = &skins[skinnum]; } else // specific skin { From fc8392dac46c91590dc17146ac861aa2568f26fc Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 13 Nov 2019 16:54:44 +0000 Subject: [PATCH 03/17] Typo (my fault). --- src/p_enemy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_enemy.c b/src/p_enemy.c index 564b9bb31..6fdfd8c55 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -5176,7 +5176,7 @@ void A_SignPlayer(mobj_t *actor) skincount++; skinnum = P_RandomKey(skincount); for (skincount = skinnum; skincount < numskins; skincount++) - if (skincheck(skinnum)) + if (skincheck(skincount)) skinnum++; } else // otherwise, advance 1 skin From 527195c2145ef6b24c09c1e22e7b8f90d80f3dd8 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 13 Nov 2019 16:59:15 +0000 Subject: [PATCH 04/17] I should be more careful when commiting, lmao. (Just a little bit of tying off loose ends for peace of mind.) --- src/p_enemy.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/p_enemy.c b/src/p_enemy.c index 6fdfd8c55..1c6625bd9 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -5184,6 +5184,7 @@ void A_SignPlayer(mobj_t *actor) skinnum = (skin_t*)ov->skin-skins; while ((skinnum = (skinnum + 1) % numskins) && skincheck(skinnum)); } +#undef skincheck skin = &skins[skinnum]; } else // specific skin From ec44b24f72416c20bbee02bcaa400dfa8f0bb8c0 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 13 Nov 2019 17:06:54 +0000 Subject: [PATCH 05/17] Ok, I'm really paying for my hubris. Corrected a faulty optimisation in my fix. --- src/p_enemy.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/p_enemy.c b/src/p_enemy.c index 1c6625bd9..00f3c52c6 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -5175,9 +5175,13 @@ void A_SignPlayer(mobj_t *actor) if (!skincheck(skincount)) skincount++; skinnum = P_RandomKey(skincount); - for (skincount = skinnum; skincount < numskins; skincount++) + for (skincount = 0; skincount < numskins; skincount++) + { if (skincheck(skincount)) skinnum++; + if (skincount > skinnum) + break; + } } else // otherwise, advance 1 skin { From a3de6d21c4c933ef242fee0fa2adc609bce8f717 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 13 Nov 2019 20:14:08 +0000 Subject: [PATCH 06/17] * Add a "moveforward" param to P_InternalFlickySpawn, which controls whether to spawn the flicky a little in front or behind of the object making it. * Expose this via additional upper-16-bits flags of A_FlickySpawn. * Use this to make Snailers not spawn their flicky inside the wall they're attached to. * Make the behaviour of Egg Capsules more consistent mechanically regarding the flicky spawning, using the above as one relevant element. --- src/dehacked.c | 1 + src/info.c | 3 ++- src/info.h | 1 + src/p_enemy.c | 32 +++++++++++++++++++++++--------- src/p_local.h | 2 +- src/p_mobj.c | 11 +++++------ src/p_user.c | 2 +- 7 files changed, 34 insertions(+), 18 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index 1ac04dc56..cceff13fd 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -4667,6 +4667,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit // Snailer "S_SNAILER1", + "S_SNAILER_FLICKY", // Vulture "S_VULTURE_STND", diff --git a/src/info.c b/src/info.c index 698e6c25f..9f61b4b1d 100644 --- a/src/info.c +++ b/src/info.c @@ -1061,6 +1061,7 @@ state_t states[NUMSTATES] = // Snailer {SPR_SNLR, 0, 1, {A_SnailerThink}, 0, 0, S_SNAILER1}, // S_SNAILER1 + {SPR_BOM1, 0, 0, {A_FlickySpawn}, 1<<17, 0, S_XPLD1}, // S_SNAILER_FLICKY // Vulture {SPR_VLTR, 4, 35, {A_Look}, 1, 0, S_VULTURE_STND}, // S_VULTURE_STND @@ -4644,7 +4645,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate - S_XPLD_FLICKY, // deathstate + S_SNAILER_FLICKY, // deathstate S_NULL, // xdeathstate sfx_pop, // deathsound FRACUNIT, // speed diff --git a/src/info.h b/src/info.h index 812c31cba..853858960 100644 --- a/src/info.h +++ b/src/info.h @@ -1226,6 +1226,7 @@ typedef enum state // Snailer S_SNAILER1, + S_SNAILER_FLICKY, // Vulture S_VULTURE_STND, diff --git a/src/p_enemy.c b/src/p_enemy.c index 27033acbc..e7c7daf7f 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -11557,9 +11557,10 @@ void A_SpawnFreshCopy(mobj_t *actor) } // Internal Flicky spawning function. -mobj_t *P_InternalFlickySpawn(mobj_t *actor, mobjtype_t flickytype, fixed_t momz, boolean lookforplayers) +mobj_t *P_InternalFlickySpawn(mobj_t *actor, mobjtype_t flickytype, fixed_t momz, boolean lookforplayers, SINT8 moveforward) { mobj_t *flicky; + fixed_t offsx = 0, offsy = 0; if (!flickytype) { @@ -11572,7 +11573,14 @@ mobj_t *P_InternalFlickySpawn(mobj_t *actor, mobjtype_t flickytype, fixed_t momz } } - flicky = P_SpawnMobjFromMobj(actor, 0, 0, 0, flickytype); + if (moveforward) + { + fixed_t scal = mobjinfo[flickytype].radius*((fixed_t)moveforward); + offsx = P_ReturnThrustX(actor, actor->angle, scal); + offsy = P_ReturnThrustY(actor, actor->angle, scal); + } + + flicky = P_SpawnMobjFromMobj(actor, offsx, offsy, 0, flickytype); flicky->angle = actor->angle; if (flickytype == MT_SEED) @@ -11598,24 +11606,30 @@ mobj_t *P_InternalFlickySpawn(mobj_t *actor, mobjtype_t flickytype, fixed_t momz // // var1: // lower 16 bits: if 0, spawns random flicky based on level header. Else, spawns the designated thing type. -// upper 16 bits: if 0, no sound is played. Else, A_Scream is called. +// bit 17: if 0, no sound is played. Else, A_Scream is called. +// bit 18: if 1, spawn flicky slightly forward from spawn position, to avoid being stuck in wall. doesn't stack with 19. (snailers) +// bit 19: if 1, spawn flicky slightly backward from spawn position. doesn't stack with 18. // var2 = upwards thrust for spawned flicky. If zero, default value is provided. // void A_FlickySpawn(mobj_t *actor) { - INT32 locvar1 = var1; + INT32 locvar1 = var1 & 65535; INT32 locvar2 = var2; + INT32 test = (var1 >> 16); + SINT8 moveforward = 0; #ifdef HAVE_BLUA if (LUA_CallAction("A_FlickySpawn", actor)) return; #endif - if (locvar1 >> 16) { + if (test & 1) A_Scream(actor); // A shortcut for the truly lazy. - locvar1 &= 65535; - } + if (test & 2) + moveforward = 1; + else if (test & 4) + moveforward = -1; - P_InternalFlickySpawn(actor, locvar1, ((locvar2) ? locvar2 : 8*FRACUNIT), true); + P_InternalFlickySpawn(actor, locvar1, ((locvar2) ? locvar2 : 8*FRACUNIT), true, moveforward); } // Internal Flicky color setting @@ -11679,7 +11693,7 @@ void A_FlickyCenter(mobj_t *actor) if (!actor->tracer) { - mobj_t *flicky = P_InternalFlickySpawn(actor, locvar1, 1, false); + mobj_t *flicky = P_InternalFlickySpawn(actor, locvar1, 1, false, 0); P_SetTarget(&flicky->target, actor); P_SetTarget(&actor->tracer, flicky); diff --git a/src/p_local.h b/src/p_local.h index 02f497850..17a1c32d2 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -357,7 +357,7 @@ boolean P_CheckMissileRange(mobj_t *actor); void P_NewChaseDir(mobj_t *actor); boolean P_LookForPlayers(mobj_t *actor, boolean allaround, boolean tracer, fixed_t dist); -mobj_t *P_InternalFlickySpawn(mobj_t *actor, mobjtype_t flickytype, fixed_t momz, boolean lookforplayers); +mobj_t *P_InternalFlickySpawn(mobj_t *actor, mobjtype_t flickytype, fixed_t momz, boolean lookforplayers, SINT8 moveforward); void P_InternalFlickySetColor(mobj_t *actor, UINT8 extrainfo); #define P_IsFlickyCenter(type) (type > MT_FLICKY_01 && type < MT_SEED && (type - MT_FLICKY_01) % 2 ? 1 : 0) void P_InternalFlickyBubble(mobj_t *actor); diff --git a/src/p_mobj.c b/src/p_mobj.c index 498cf897e..d924c99ee 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -8227,8 +8227,7 @@ void P_MobjThinker(mobj_t *mobj) mobj->flags2 ^= MF2_DONTDRAW; break; case MT_EGGTRAP: // Egg Capsule animal release - if (mobj->fuse > 0 && mobj->fuse < 2*TICRATE-(TICRATE/7) - && (mobj->fuse & 3)) + if (mobj->fuse > 0 && mobj->fuse < 2*TICRATE-(TICRATE/7)) { INT32 i; fixed_t x,y,z; @@ -8236,7 +8235,7 @@ void P_MobjThinker(mobj_t *mobj) mobj_t *mo2; mobj_t *flicky; - z = mobj->subsector->sector->floorheight + ((P_RandomByte()&63)*FRACUNIT); + z = mobj->subsector->sector->floorheight + FRACUNIT + (P_RandomKey(64)<momx = FixedMul(FINESINE(fa),ns); mo2->momy = FixedMul(FINECOSINE(fa),ns); + mo2->angle = fa << ANGLETOFINESHIFT; - if (P_RandomChance(FRACUNIT/4)) // I filled a spreadsheet trying to get the equivalent chance to the original P_RandomByte hack! + if (!i && !(mobj->fuse & 2)) S_StartSound(mo2, mobj->info->deathsound); - flicky = P_InternalFlickySpawn(mo2, 0, 8*FRACUNIT, false); + flicky = P_InternalFlickySpawn(mo2, 0, 8*FRACUNIT, false, -1); if (!flicky) break; P_SetTarget(&flicky->target, mo2); flicky->momx = mo2->momx; flicky->momy = mo2->momy; - flicky->angle = fa << ANGLETOFINESHIFT; } mobj->fuse--; diff --git a/src/p_user.c b/src/p_user.c index 6a67e329c..91234337e 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -6895,7 +6895,7 @@ static void P_DoNiGHTSCapsule(player_t *player) { /*for (i = 0; i < 16; i++) { - mobj_t *flicky = P_InternalFlickySpawn(player->capsule, 0, ((i%4) + 1)*2*FRACUNIT, true); + mobj_t *flicky = P_InternalFlickySpawn(player->capsule, 0, ((i%4) + 1)*2*FRACUNIT, true, 0); flicky->z += player->capsule->height/2; flicky->angle = (i*(ANGLE_MAX/16)); P_InstaThrust(flicky, flicky->angle, 8*FRACUNIT); From 39d80e1cbab2934bee830feebdb2e5eba66062bf Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 13 Nov 2019 21:52:35 +0000 Subject: [PATCH 07/17] https://cdn.discordapp.com/attachments/244035368595357697/644293089049182218/srb20057.gif * Make the white flash a little easier on the eyes by putting the full-screen fade behind the title wing/ring, then drawing multiple overlaid colormapped versions of them on top of each other. * Darken the pre-flash screen. --- src/f_finale.c | 40 +++++++++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/src/f_finale.c b/src/f_finale.c index 764825e6f..b55014244 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -2573,6 +2573,8 @@ void F_TitleScreenDrawer(void) { boolean hidepics; fixed_t sc = FRACUNIT / max(1, curttscale); + INT32 whitefade = 0; + UINT8 *whitecol[2] = {NULL, NULL}; if (modeattacking) return; // We likely came here from retrying. Don't do a damn thing. @@ -2658,10 +2660,31 @@ void F_TitleScreenDrawer(void) // if (finalecount <= 29) V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); + // Flash at tic 30, timed to O__TITLE percussion. Hold the flash until tic 34. + // After tic 34, fade the flash until tic 44. + else + { + if (finalecount > 29 && finalecount < 35) + V_DrawFadeScreen(0, (whitefade = 9)); + else if (finalecount > 34 && 44-finalecount > 0 && 44-finalecount < 10) + V_DrawFadeScreen(0, 44-finalecount); + if (39-finalecount > 0) + { + whitefade = (9 - (39-finalecount))< 34) + if (finalecount > 29) V_DrawSciencePatch(39< 29 && finalecount < 35) - V_DrawFadeScreen(0, 9); - else if (finalecount > 34 && 44-finalecount > 0 && 44-finalecount < 10) - V_DrawFadeScreen(0, 44-finalecount); + if (finalecount <= 29) + V_DrawFadeScreen(0xFF00, 12); #undef CHARSTART #undef SONICSTART From 3afe152a864af96ae70f33866c0a4441a981c6dd Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 13 Nov 2019 22:02:06 +0000 Subject: [PATCH 08/17] Sev wanted no darkness during the initial animation... I think this is an acceptible compromise! https://cdn.discordapp.com/attachments/428262628893261828/644295409283497995/srb20058.gif --- src/f_finale.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/f_finale.c b/src/f_finale.c index b55014244..1e82359a1 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -2691,6 +2691,9 @@ void F_TitleScreenDrawer(void) // Ribbon unfurls, revealing SONIC text, from tic 0 to tic 24. SONIC text is pre-baked into this ribbon graphic. V_DrawSciencePatch(39<= 0) V_DrawSciencePatch(89< Date: Wed, 13 Nov 2019 14:26:28 -0800 Subject: [PATCH 09/17] Optimizations from LJ and generic functions --- src/command.c | 34 ++++++++++++ src/command.h | 2 + src/d_netcmd.c | 144 ++++++++++++------------------------------------- 3 files changed, 70 insertions(+), 110 deletions(-) diff --git a/src/command.c b/src/command.c index d39730f98..0a0b62956 100644 --- a/src/command.c +++ b/src/command.c @@ -345,6 +345,40 @@ size_t COM_CheckParm(const char *check) return 0; } +/** \brief COM_CheckParm, but checks only the start of each argument. + * E.g. checking for "-no" would match "-noerror" too. + */ +size_t COM_CheckPartialParm(const char *check) +{ + int len; + size_t i; + + len = strlen(check); + + for (i = 1; i < com_argc; i++) + { + if (strncasecmp(check, com_argv[i], len) == 0) + return i; + } + return 0; +} + +/** Find the first argument that starts with a hyphen (-). + * \return The index of the argument, or 0 + * if there are no such arguments. + */ +size_t COM_FirstOption(void) +{ + size_t i; + + for (i = 1; i < com_argc; i++) + { + if (com_argv[i][0] == '-')/* options start with a hyphen */ + return i; + } + return 0; +} + /** Parses a string into command-line tokens. * * \param ptext A null-terminated string. Does not need to be diff --git a/src/command.h b/src/command.h index 4682ba4a4..d324481f7 100644 --- a/src/command.h +++ b/src/command.h @@ -29,6 +29,8 @@ size_t COM_Argc(void); const char *COM_Argv(size_t arg); // if argv > argc, returns empty string char *COM_Args(void); size_t COM_CheckParm(const char *check); // like M_CheckParm :) +size_t COM_CheckPartialParm(const char *check); +size_t COM_FirstOption(void); // match existing command or NULL const char *COM_CompleteCommand(const char *partial, INT32 skips); diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 485b53719..3ccab0a8e 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1719,56 +1719,6 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pultmode, boolean rese } } -enum -{ - MAP_COMMAND_FORCE_OPTION, - MAP_COMMAND_GAMETYPE_OPTION, - MAP_COMMAND_NORESETPLAYERS_OPTION, - - NUM_MAP_COMMAND_OPTIONS -}; - -static size_t CheckOptions( - int num_options, - size_t *first_argumentp, - size_t *user_options, - const char ***option_names, - int *option_num_arguments -) -{ - int arguments_used; - size_t first_argument; - - int i; - const char **pp; - const char *name; - size_t n; - - arguments_used = 0; - first_argument = COM_Argc(); - - for (i = 0; i < num_options; ++i) - { - pp = option_names[i]; - name = *pp; - do - { - if (( n = COM_CheckParm(name) )) - { - user_options[i] = n; - arguments_used += 1 + option_num_arguments[i]; - if (n < first_argument) - first_argument = n; - } - } - while (( name = *++pp )) ; - } - - (*first_argumentp) = first_argument; - - return arguments_used; -} - static char * ConcatCommandArgv (int start, int end) { @@ -1812,43 +1762,10 @@ ConcatCommandArgv (int start, int end) // static void Command_Map_f(void) { - const char *force_option_names[] = - { - "-force", - "-f", - NULL - }; - const char *gametype_option_names[] = - { - "-gametype", - "-g", - "-gt", - NULL - }; - const char *noresetplayers_option_names[] = - { - "-noresetplayers", - NULL - }; - const char **option_names[] = - { - force_option_names, - gametype_option_names, - noresetplayers_option_names, - }; - int option_num_arguments[] = - { - 0,/* -force */ - 1,/* -gametype */ - 0,/* -noresetplayers */ - }; - - size_t acceptableargc;/* (this includes the command name itself!) */ - size_t first_argument; - - size_t user_options [NUM_MAP_COMMAND_OPTIONS] = {0}; - - const char *arg_gametype; + size_t first_option; + size_t option_force; + size_t option_gametype; + const char *gametypename; boolean newresetplayers; boolean mustmodifygame; @@ -1871,18 +1788,15 @@ static void Command_Map_f(void) return; } - /* map name + options */ - acceptableargc = 2 + CheckOptions(NUM_MAP_COMMAND_OPTIONS, - &first_argument, - user_options, option_names, option_num_arguments); - - newresetplayers = !user_options[MAP_COMMAND_NORESETPLAYERS_OPTION]; + option_force = COM_CheckPartialParm("-f"); + option_gametype = COM_CheckPartialParm("-g"); + newresetplayers = ! COM_CheckParm("-noresetplayers"); mustmodifygame = !( netgame || multiplayer ) && (!modifiedgame || savemoddata ); - if (mustmodifygame && !user_options[MAP_COMMAND_FORCE_OPTION]) + if (mustmodifygame && !option_force) { /* May want to be more descriptive? */ CONS_Printf(M_GetText("Sorry, level change disabled in single player.\n")); @@ -1895,26 +1809,37 @@ static void Command_Map_f(void) return; } - if (user_options[MAP_COMMAND_GAMETYPE_OPTION] && !multiplayer) + if (option_gametype) { - CONS_Printf(M_GetText("You can't switch gametypes in single player!\n")); - return; + if (!multiplayer) + { + CONS_Printf(M_GetText( + "You can't switch gametypes in single player!\n")); + return; + } + else if (COM_Argc() < option_gametype + 2)/* no argument after? */ + { + CONS_Alert(CONS_ERROR, + "No gametype name follows parameter '%s'.\n", + COM_Argv(option_gametype)); + return; + } } - /* If the first argument is an option, you fucked up. */ - if (COM_Argc() < acceptableargc || first_argument == 1) + if (!( first_option = COM_FirstOption() )) + first_option = COM_Argc(); + + if (first_option < 2) { /* I'm going over the fucking lines and I DON'T CAREEEEE */ CONS_Printf("map [-gametype ] [-force]:\n"); CONS_Printf(M_GetText( "Warp to a map, by its name, two character code, with optional \"MAP\" prefix, or by its number (though why would you).\n" - "All parameters are case-insensitive.\n" - "* \"-force\" may be shortened to \"-f\".\n" - "* \"-gametype\" may be shortened to \"-g\" or \"-gt\".\n")); + "All parameters are case-insensitive and may be abbreviated.\n")); return; } - mapname = ConcatCommandArgv(1, first_argument); + mapname = ConcatCommandArgv(1, first_option); mapnamelen = strlen(mapname); if (mapnamelen == 2)/* maybe two digit code */ @@ -1965,22 +1890,22 @@ static void Command_Map_f(void) realmapname = G_BuildMapTitle(newmapnum); } - if (mustmodifygame && user_options[MAP_COMMAND_FORCE_OPTION]) + if (mustmodifygame && option_force) { G_SetGameModified(false); } // new gametype value // use current one by default - if (user_options[MAP_COMMAND_GAMETYPE_OPTION]) + if (option_gametype) { - arg_gametype = COM_Argv(user_options[MAP_COMMAND_GAMETYPE_OPTION] + 1); + gametypename = COM_Argv(option_gametype + 1); - newgametype = G_GetGametypeByName(arg_gametype); + newgametype = G_GetGametypeByName(gametypename); if (newgametype == -1) // reached end of the list with no match { - d = atoi(arg_gametype); + d = atoi(gametypename); // assume they gave us a gametype number, which is okay too if (d >= 0 && d < NUMGAMETYPES) newgametype = d; @@ -1988,7 +1913,7 @@ static void Command_Map_f(void) } // don't use a gametype the map doesn't support - if (cv_debug || user_options[MAP_COMMAND_FORCE_OPTION] || cv_skipmapcheck.value) + if (cv_debug || option_force || cv_skipmapcheck.value) fromlevelselect = false; // The player wants us to trek on anyway. Do so. // G_TOLFlag handles both multiplayer gametype and ignores it for !multiplayer else @@ -2043,7 +1968,6 @@ static void Command_Map_f(void) Z_Free(realmapname); } -#undef CHECKPARM /** Receives a map command and changes the map. * From 7776c59cddaa4cc09dfdb2f15192a194fda36eb7 Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 13 Nov 2019 14:31:44 -0800 Subject: [PATCH 10/17] Warn if the gametype is not valid at all! --- src/d_netcmd.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 3ccab0a8e..67c8ac5ba 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1905,10 +1905,22 @@ static void Command_Map_f(void) if (newgametype == -1) // reached end of the list with no match { - d = atoi(gametypename); - // assume they gave us a gametype number, which is okay too - if (d >= 0 && d < NUMGAMETYPES) - newgametype = d; + /* Did they give us a gametype number? That's okay too! */ + if (isdigit(gametypename[0])) + { + d = atoi(gametypename); + if (d >= 0 && d < NUMGAMETYPES) + newgametype = d; + } + else + { + CONS_Alert(CONS_ERROR, + "'%s' is not a gametype.\n", + gametypename); + Z_Free(realmapname); + Z_Free(mapname); + return; + } } } From 761bd41eb5f7afefed9931fb4bd069dddfee8685 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 13 Nov 2019 23:26:37 +0000 Subject: [PATCH 11/17] I'm a dumbass who makes extremely consequential typoes (and since the typo is no longer in effect, the supergold can be brightened some more) https://cdn.discordapp.com/attachments/244035368595357697/644316971738857483/srb20060.gif --- src/f_finale.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/f_finale.c b/src/f_finale.c index 1e82359a1..26e1d3848 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -2671,7 +2671,7 @@ void F_TitleScreenDrawer(void) if (39-finalecount > 0) { whitefade = (9 - (39-finalecount))< Date: Thu, 14 Nov 2019 12:52:34 +0000 Subject: [PATCH 12/17] Disable menu input on the title screen for the first second (ie, before the flash), to guarantee people won't start the menu the very first tic after the fade and ruin the animation. (Console and gif/screenshot recording still works, for the impatient.) --- src/f_finale.c | 2 +- src/f_finale.h | 1 + src/g_game.c | 2 +- src/m_menu.c | 3 +++ 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/f_finale.c b/src/f_finale.c index 26e1d3848..fc300133d 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -45,7 +45,7 @@ // Stage of animation: // 0 = text, 1 = art screen -static INT32 finalecount; +INT32 finalecount; INT32 titlescrollxspeed = 20; INT32 titlescrollyspeed = 0; UINT8 titlemapinaction = TITLEMAP_OFF; diff --git a/src/f_finale.h b/src/f_finale.h index f75f93c77..3fa7106a9 100644 --- a/src/f_finale.h +++ b/src/f_finale.h @@ -74,6 +74,7 @@ void F_StartContinue(void); void F_ContinueTicker(void); void F_ContinueDrawer(void); +extern INT32 finalecount; extern INT32 titlescrollxspeed; extern INT32 titlescrollyspeed; diff --git a/src/g_game.c b/src/g_game.c index 9677e81c9..0c4ebcc92 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1717,7 +1717,7 @@ boolean G_Responder(event_t *ev) if (gameaction == ga_nothing && !singledemo && ((demoplayback && !modeattacking && !titledemo) || gamestate == GS_TITLESCREEN)) { - if (ev->type == ev_keydown && ev->data1 != 301) + if (ev->type == ev_keydown && ev->data1 != 301 && !(gamestate == GS_TITLESCREEN && finalecount < TICRATE)) { M_StartControlPanel(); return true; diff --git a/src/m_menu.c b/src/m_menu.c index 6a0a31bc1..e772c9421 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -2985,6 +2985,9 @@ boolean M_Responder(event_t *ev) || gamestate == GS_CREDITS || gamestate == GS_EVALUATION || gamestate == GS_GAMEEND) return false; + if (gamestate == GS_TITLESCREEN && finalecount < TICRATE) + return false; + if (noFurtherInput) { // Ignore input after enter/escape/other buttons From ffd9cfebe696a22f2dde9859e6f53023de043dc9 Mon Sep 17 00:00:00 2001 From: Louis-Antoine Date: Thu, 14 Nov 2019 16:37:09 +0100 Subject: [PATCH 13/17] Allow item respawn in co-op again Why was it even disabled anyway. --- src/d_netcmd.c | 8 ++++++-- src/p_inter.c | 2 +- src/p_mobj.c | 1 - 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 3fb204aea..6a78d29bb 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -3907,13 +3907,17 @@ void D_GameTypeChanged(INT32 lastgametype) // There will always be a server, and this only needs to be done once. if (server && (multiplayer || netgame)) { - if (gametype == GT_COMPETITION || gametype == GT_COOP) + if (gametype == GT_COMPETITION) CV_SetValue(&cv_itemrespawn, 0); - else if (!cv_itemrespawn.changed) + else if (!cv_itemrespawn.changed || lastgametype == GT_COMPETITION) CV_SetValue(&cv_itemrespawn, 1); switch (gametype) { + case GT_COOP: + if (!cv_itemrespawntime.changed) + CV_Set(&cv_itemrespawntime, cv_itemrespawntime.defaultvalue); // respawn normally + break; case GT_MATCH: case GT_TEAMMATCH: if (!cv_timelimit.changed && !cv_pointlimit.changed) // user hasn't changed limits diff --git a/src/p_inter.c b/src/p_inter.c index c65e6cc36..d6f7da48a 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -2392,7 +2392,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget { P_SetTarget(&target->target, source); source->player->numboxes++; - if ((cv_itemrespawn.value && gametype != GT_COOP && (modifiedgame || netgame || multiplayer))) + if (cv_itemrespawn.value && (modifiedgame || netgame || multiplayer)) target->fuse = cv_itemrespawntime.value*TICRATE + 2; // Random box generation } diff --git a/src/p_mobj.c b/src/p_mobj.c index 498cf897e..32b261fb8 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -11011,7 +11011,6 @@ void P_RespawnSpecials(void) // only respawn items when cv_itemrespawn is on if (!(netgame || multiplayer) // Never respawn in single player - || gametype == GT_COOP // Never respawn in co-op gametype || !cv_itemrespawn.value) // cvar is turned off return; From cccf1c8a7b92dae879062a13e37a0e3ff46a51f3 Mon Sep 17 00:00:00 2001 From: Louis-Antoine Date: Thu, 14 Nov 2019 19:25:19 +0100 Subject: [PATCH 14/17] Disable perfect bonus in multiplayer --- src/p_setup.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/p_setup.c b/src/p_setup.c index aff6c33d7..f414a87eb 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -2221,7 +2221,10 @@ static void P_LevelInitStuff(void) tokenbits = 0; runemeraldmanager = false; emeraldspawndelay = 60*TICRATE; - nummaprings = mapheaderinfo[gamemap-1]->startrings; + if ((netgame || multiplayer) && !G_IsSpecialStage(gamemap)) + nummaprings = -1; + else + nummaprings = mapheaderinfo[gamemap-1]->startrings; // emerald hunt hunt1 = hunt2 = hunt3 = NULL; From 18dc6013eda54b5eb86e497fbc53e71a66408cba Mon Sep 17 00:00:00 2001 From: Louis-Antoine Date: Thu, 14 Nov 2019 21:21:42 +0100 Subject: [PATCH 15/17] Disable item respawn in NiGHTs levels --- src/p_mobj.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/p_mobj.c b/src/p_mobj.c index 32b261fb8..6fe336f88 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -11011,6 +11011,7 @@ void P_RespawnSpecials(void) // only respawn items when cv_itemrespawn is on if (!(netgame || multiplayer) // Never respawn in single player + || (maptol & TOL_NIGHTS) // Never respawn in NiGHTs || !cv_itemrespawn.value) // cvar is turned off return; From f4beb4a56fd3aae435d84a73f6ec65607ccb209d Mon Sep 17 00:00:00 2001 From: Louis-Antoine Date: Thu, 14 Nov 2019 22:27:02 +0100 Subject: [PATCH 16/17] Disable monitor respawning in co-op --- src/p_inter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_inter.c b/src/p_inter.c index d6f7da48a..102341777 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -2392,7 +2392,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget { P_SetTarget(&target->target, source); source->player->numboxes++; - if (cv_itemrespawn.value && (modifiedgame || netgame || multiplayer)) + if (cv_itemrespawn.value && gametype != GT_COOP && (modifiedgame || netgame || multiplayer)) target->fuse = cv_itemrespawntime.value*TICRATE + 2; // Random box generation } From d36086ba3357238351100b1b8821f10ed8100f9d Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 14 Nov 2019 15:12:50 -0800 Subject: [PATCH 17/17] Clang warning --- src/f_finale.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/f_finale.c b/src/f_finale.c index 66957c92c..108385adb 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -2505,7 +2505,7 @@ static void F_LoadAlacroixGraphics(SINT8 newttscale) if (!ttloaded[newttscale]) { for (j = 0; j < 22; j++) - sprintf(&lumpnames[j][0], "T%.1hu%s", (UINT8)newttscale+1, names[j]); + sprintf(&lumpnames[j][0], "T%.1hu%s", (UINT16)( (UINT8)newttscale+1 ), names[j]); LOADTTGFX(ttembl[newttscale], lumpnames[0], TTMAX_ALACROIX) LOADTTGFX(ttribb[newttscale], lumpnames[1], TTMAX_ALACROIX)