From c9291b0f61189b89ba0d74721cd8cabeb6b063ab Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 18 Sep 2023 03:24:58 -0400 Subject: [PATCH] Mobj thinker freeze condition rework - There's a freeze cheat bool to freeze everything except for players. - There's a level freeze bool to freeze literally everything. - There's a frozen bool on mobj_t to explicitly control freeze status on an object. --- src/d_netcmd.c | 8 +++++++ src/k_dialogue.h | 2 ++ src/k_dialogue.hpp | 1 + src/k_follower.c | 2 +- src/m_cheat.c | 8 +++++++ src/m_cheat.h | 2 ++ src/p_local.h | 2 ++ src/p_map.c | 2 +- src/p_mobj.c | 6 ++++- src/p_mobj.h | 2 ++ src/p_saveg.c | 18 ++++++++------- src/p_tick.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++ src/p_tick.h | 6 +++++ src/p_user.c | 6 ++--- 14 files changed, 108 insertions(+), 14 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 45076986f..163c91a78 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -534,6 +534,7 @@ void D_RegisterClientCommands(void) // add cheats COM_AddDebugCommand("noclip", Command_CheatNoClip_f); COM_AddDebugCommand("god", Command_CheatGod_f); + COM_AddDebugCommand("freeze", Command_CheatFreeze_f); COM_AddDebugCommand("setrings", Command_Setrings_f); COM_AddDebugCommand("setspheres", Command_Setspheres_f); COM_AddDebugCommand("setlives", Command_Setlives_f); @@ -5737,6 +5738,13 @@ static void Got_Cheat(UINT8 **cp, INT32 playernum) break; } + case CHEAT_FREEZE: { + const char *status = P_FreezeCheat() ? "off" : "on"; + P_SetFreezeCheat( !P_FreezeCheat() ); + CV_CheaterWarning(targetPlayer, va("freeze %s", status)); + break; + } + case NUMBER_OF_CHEATS: break; } diff --git a/src/k_dialogue.h b/src/k_dialogue.h index 7b4f7437e..4d6c05c79 100644 --- a/src/k_dialogue.h +++ b/src/k_dialogue.h @@ -25,6 +25,8 @@ void K_UnsetDialogue(void); void K_DrawDialogue(void); void K_TickDialogue(void); +boolean K_DialogueFreeze(void); + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/k_dialogue.hpp b/src/k_dialogue.hpp index c3fb8dd5a..8639524fc 100644 --- a/src/k_dialogue.hpp +++ b/src/k_dialogue.hpp @@ -48,6 +48,7 @@ private: bool syllable; bool dismissable; + bool freeze; void Init(void); //void Unset(void); diff --git a/src/k_follower.c b/src/k_follower.c index b1e4261b0..05185212a 100644 --- a/src/k_follower.c +++ b/src/k_follower.c @@ -435,7 +435,7 @@ void K_HandleFollower(player_t *player) } else // follower exists, woo! { - if (player->follower->hitlag != 0) + if (P_MobjIsFrozen(player->follower)) { // Don't update frames in hitlag return; diff --git a/src/m_cheat.c b/src/m_cheat.c index b4cb60d83..e44735eb9 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -303,6 +303,14 @@ void Command_CheatGod_f(void) D_Cheat(consoleplayer, CHEAT_GOD); } +void Command_CheatFreeze_f(void) +{ + REQUIRE_CHEATS; + REQUIRE_INLEVEL; + + D_Cheat(consoleplayer, CHEAT_FREEZE); +} + void Command_Scale_f(void) { const double scaled = atof(COM_Argv(1)); diff --git a/src/m_cheat.h b/src/m_cheat.h index 1c1270e9b..a8d0505cc 100644 --- a/src/m_cheat.h +++ b/src/m_cheat.h @@ -41,6 +41,7 @@ typedef enum { CHEAT_RESPAWNAT, CHEAT_GIVEPOWERUP, CHEAT_SPHERES, + CHEAT_FREEZE, NUMBER_OF_CHEATS } cheat_t; @@ -74,6 +75,7 @@ void OP_ObjectplaceMovement(player_t *player); // void Command_CheatNoClip_f(void); void Command_CheatGod_f(void); +void Command_CheatFreeze_f(void); void Command_Savecheckpoint_f(void); void Command_Setrings_f(void); void Command_Setspheres_f(void); diff --git a/src/p_local.h b/src/p_local.h index 4c6b44449..c5ab6f37a 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -333,6 +333,8 @@ void P_FlashPal(player_t *pl, UINT16 type, UINT16 duration); #define PAL_RECYCLE 3 #define PAL_NUKE 4 +boolean P_MobjIsFrozen(mobj_t *mobj); + // // P_ENEMY // diff --git a/src/p_map.c b/src/p_map.c index 92a2a65d4..ff22d543a 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -573,7 +573,7 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing) return BMIT_CONTINUE; // Ignore the collision if BOTH things are in hitlag. - if (thing->hitlag > 0 && tm.thing->hitlag > 0) + if (P_MobjIsFrozen(thing) && P_MobjIsFrozen(tm.thing)) return BMIT_CONTINUE; if ((thing->flags & MF_NOCLIPTHING) || !(thing->flags & (MF_SOLID|MF_SPECIAL|MF_PAIN|MF_SHOOTABLE|MF_SPRING))) diff --git a/src/p_mobj.c b/src/p_mobj.c index 4f1b97657..c4c61b10f 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -9937,6 +9937,7 @@ void P_MobjThinker(mobj_t *mobj) mobj->flags2 &= ~(MF2_ALREADYHIT); // Don't run any thinker code while in hitlag + mobj->eflags &= ~(MFE_PAUSED); if ((mobj->player ? mobj->hitlag - mobj->player->nullHitlag : mobj->hitlag) > 0) { mobj->eflags |= MFE_PAUSED; @@ -9971,11 +9972,14 @@ void P_MobjThinker(mobj_t *mobj) if (mobj->type == MT_HITLAG && mobj->hitlag == 0) mobj->renderflags &= ~RF_DONTDRAW; */ + } + if (P_MobjIsFrozen(mobj)) + { return; } - mobj->eflags &= ~(MFE_PUSHED|MFE_SPRUNG|MFE_JUSTBOUNCEDWALL|MFE_DAMAGEHITLAG|MFE_SLOPELAUNCHED|MFE_PAUSED); + mobj->eflags &= ~(MFE_PUSHED|MFE_SPRUNG|MFE_JUSTBOUNCEDWALL|MFE_DAMAGEHITLAG|MFE_SLOPELAUNCHED); // sal: what the hell? is there any reason this isn't done, like, literally ANYWHERE else? P_SetTarget(&tm.floorthing, NULL); diff --git a/src/p_mobj.h b/src/p_mobj.h index 61bf62bf7..04344d505 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -433,6 +433,8 @@ struct mobj_t INT32 script_args[NUM_SCRIPT_ARGS]; char *script_stringargs[NUM_SCRIPT_STRINGARGS]; + boolean frozen; + // WARNING: New fields must be added separately to savegame and Lua. }; diff --git a/src/p_saveg.c b/src/p_saveg.c index 7f2410694..be6724ed4 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -2534,7 +2534,7 @@ typedef enum MD2_WAYPOINTCAP = 1<<25, MD2_KITEMCAP = 1<<26, MD2_ITNEXT = 1<<27, - MD2_LASTMOMZ = 1<<28, + MD2_FROZEN = 1<<28, MD2_TERRAIN = 1<<29, MD2_WATERSKIP = 1<<30, MD2_LIGHTLEVEL = (INT32)(1U<<31), @@ -2725,7 +2725,7 @@ static void SaveMobjThinker(savebuffer_t *save, const thinker_t *th, const UINT8 } // not the default but the most probable - if (mobj->momx != 0 || mobj->momy != 0 || mobj->momz != 0 || mobj->pmomz != 0) + if (mobj->momx != 0 || mobj->momy != 0 || mobj->momz != 0 || mobj->pmomz != 0 || mobj->lastmomz != 0) diff |= MD_MOM; if (mobj->radius != mobj->info->radius) diff |= MD_RADIUS; @@ -2847,8 +2847,8 @@ static void SaveMobjThinker(savebuffer_t *save, const thinker_t *th, const UINT8 diff2 |= MD2_KITEMCAP; if (mobj->itnext) diff2 |= MD2_ITNEXT; - if (mobj->lastmomz) - diff2 |= MD2_LASTMOMZ; + if (mobj->frozen) + diff2 |= MD2_FROZEN; if (mobj->terrain != NULL || mobj->terrainOverlay != NULL) diff2 |= MD2_TERRAIN; @@ -2911,6 +2911,7 @@ static void SaveMobjThinker(savebuffer_t *save, const thinker_t *th, const UINT8 WRITEFIXED(save->p, mobj->momy); WRITEFIXED(save->p, mobj->momz); WRITEFIXED(save->p, mobj->pmomz); + WRITEFIXED(save->p, mobj->lastmomz); } if (diff & MD_RADIUS) WRITEFIXED(save->p, mobj->radius); @@ -3114,9 +3115,9 @@ static void SaveMobjThinker(savebuffer_t *save, const thinker_t *th, const UINT8 { WRITEINT32(save->p, mobj->dispoffset); } - if (diff2 & MD2_LASTMOMZ) + if (diff2 & MD2_FROZEN) { - WRITEINT32(save->p, mobj->lastmomz); + WRITEUINT8(save->p, mobj->frozen); } if (diff2 & MD2_TERRAIN) { @@ -4093,6 +4094,7 @@ static thinker_t* LoadMobjThinker(savebuffer_t *save, actionf_p1 thinker) mobj->momy = READFIXED(save->p); mobj->momz = READFIXED(save->p); mobj->pmomz = READFIXED(save->p); + mobj->lastmomz = READFIXED(save->p); } // otherwise they're zero, and the memset took care of it if (diff & MD_RADIUS) @@ -4347,9 +4349,9 @@ static thinker_t* LoadMobjThinker(savebuffer_t *save, actionf_p1 thinker) { mobj->dispoffset = READINT32(save->p); } - if (diff2 & MD2_LASTMOMZ) + if (diff2 & MD2_FROZEN) { - mobj->lastmomz = READINT32(save->p); + mobj->frozen = (boolean)READUINT8(save->p); } if (diff2 & MD2_TERRAIN) { diff --git a/src/p_tick.c b/src/p_tick.c index 737ff9ba2..2ccf6243c 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -52,6 +52,63 @@ tic_t leveltime; boolean thinkersCompleted; +static boolean g_freezeCheat; +static boolean g_freezeLevel; + +boolean P_LevelIsFrozen(void) +{ + return (g_freezeLevel || g_freezeCheat); +} + +boolean P_FreezeCheat(void) +{ + return (g_freezeLevel || g_freezeCheat); +} + +void P_SetFreezeCheat(boolean value) +{ + g_freezeCheat = value; +} + +void P_SetFreezeLevel(boolean value) +{ + g_freezeLevel = value; +} + +boolean P_MobjIsFrozen(mobj_t *mobj) +{ + if (g_freezeCheat == true) + { + // freeze cheat + switch (mobj->type) + { + case MT_PLAYER: + { + break; + } + default: + { + return true; + } + } + } + + if (g_freezeLevel == true) + { + // level totally frozen + return true; + } + + if ((mobj->eflags & MFE_PAUSED) == MFE_PAUSED) + { + // hitlag + return true; + } + + // manual + return mobj->frozen; +} + INT32 P_AltFlip(INT32 n, tic_t tics) { return leveltime % (2 * tics) < tics ? n : -(n); diff --git a/src/p_tick.h b/src/p_tick.h index d05813c1c..58c154a1a 100644 --- a/src/p_tick.h +++ b/src/p_tick.h @@ -27,6 +27,12 @@ extern "C" { extern tic_t leveltime; extern boolean thinkersCompleted; +boolean P_LevelIsFrozen(void); +boolean P_FreezeCheat(void); +void P_SetFreezeCheat(boolean value); +void P_SetFreezeLevel(boolean value); +boolean P_MobjIsFrozen(mobj_t *mobj); + // Called by G_Ticker. Carries out all thinking of enemies and players. void Command_Numthinkers_f(void); void Command_CountMobjs_f(void); diff --git a/src/p_user.c b/src/p_user.c index 8f4dc5090..41708735b 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -3118,7 +3118,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall mo = player->mo; - if (mo->hitlag > 0 || player->playerstate == PST_DEAD) + if (P_MobjIsFrozen(mo) || player->playerstate == PST_DEAD) { // Do not move the camera while in hitlag! // The camera zooming out after you got hit makes it hard to focus on the vibration. @@ -4212,7 +4212,7 @@ void P_PlayerThink(player_t *player) ALL ABOVE THIS BLOCK OCCURS EVEN WITH HITLAG / ------------------------------------------ */ - if (player->mo->hitlag > 0) + if (P_MobjIsFrozen(player->mo)) { return; } @@ -4550,7 +4550,7 @@ void P_IncrementGriefValue(player_t *player, UINT32 *grief, const UINT32 griefMa INT32 progress = player->distancetofinishprev - player->distancetofinish; boolean exceptions = ( player->flashing != 0 - || player->mo->hitlag != 0 + || P_MobjIsFrozen(player->mo) || player->airtime > 3*TICRATE/2 || (player->justbumped > 0 && player->justbumped < bumptime-1) );