From 5568e445ed2211ea5cd931180661ee36c641aa3c Mon Sep 17 00:00:00 2001 From: Tatsuru <44866610+Ikkarin@users.noreply.github.com> Date: Fri, 3 Jan 2020 16:21:08 -0300 Subject: [PATCH 01/54] Colored title cards --- src/st_stuff.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/st_stuff.c b/src/st_stuff.c index 9c4f0abd5..659ed8c2f 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -1301,6 +1301,16 @@ void ST_drawTitleCard(void) INT32 ttlscroll = FixedInt(lt_scroll); INT32 zzticker; patch_t *actpat, *zigzag, *zztext; + UINT8 colornum; + const UINT8 *colormap; + stplyr = &players[consoleplayer]; + + if (stplyr) + colornum = stplyr->skincolor; + else + colornum = cv_playercolor.value; + + colormap = R_GetTranslationColormap(TC_DEFAULT, colornum, GTC_CACHE); if (!G_IsTitleCardAvailable()) return; @@ -1340,16 +1350,16 @@ void ST_drawTitleCard(void) if (!splitscreen || (splitscreen && stplyr == &players[displayplayer])) { zzticker = lt_ticker; - V_DrawScaledPatch(FixedInt(lt_zigzag), (-zzticker) % zigzag->height, V_SNAPTOTOP|V_SNAPTOLEFT, zigzag); - V_DrawScaledPatch(FixedInt(lt_zigzag), (zigzag->height-zzticker) % zigzag->height, V_SNAPTOTOP|V_SNAPTOLEFT, zigzag); - V_DrawScaledPatch(FixedInt(lt_zigzag), (-zigzag->height+zzticker) % zztext->height, V_SNAPTOTOP|V_SNAPTOLEFT, zztext); - V_DrawScaledPatch(FixedInt(lt_zigzag), (zzticker) % zztext->height, V_SNAPTOTOP|V_SNAPTOLEFT, zztext); + V_DrawMappedPatch(FixedInt(lt_zigzag), (-zzticker) % zigzag->height, V_SNAPTOTOP|V_SNAPTOLEFT, zigzag, colormap); + V_DrawMappedPatch(FixedInt(lt_zigzag), (zigzag->height-zzticker) % zigzag->height, V_SNAPTOTOP|V_SNAPTOLEFT, zigzag, colormap); + V_DrawMappedPatch(FixedInt(lt_zigzag), (-zigzag->height+zzticker) % zztext->height, V_SNAPTOTOP|V_SNAPTOLEFT, zztext, colormap); + V_DrawMappedPatch(FixedInt(lt_zigzag), (zzticker) % zztext->height, V_SNAPTOTOP|V_SNAPTOLEFT, zztext, colormap); } if (actnum) { if (!splitscreen) - V_DrawScaledPatch(ttlnumxpos + ttlscroll, 104 - ttlscroll, 0, actpat); + V_DrawMappedPatch(ttlnumxpos + ttlscroll, 104 - ttlscroll, 0, actpat, colormap); V_DrawLevelActNum(ttlnumxpos + ttlscroll, 104, V_PERPLAYER, actnum); } From 423d1af48d41e2595bd30ae6e219ee3b5205dc56 Mon Sep 17 00:00:00 2001 From: Tatsuru <44866610+Ikkarin@users.noreply.github.com> Date: Fri, 3 Jan 2020 16:39:39 -0300 Subject: [PATCH 02/54] I see trees of green --- src/st_stuff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/st_stuff.c b/src/st_stuff.c index 659ed8c2f..708034c03 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -1305,7 +1305,7 @@ void ST_drawTitleCard(void) const UINT8 *colormap; stplyr = &players[consoleplayer]; - if (stplyr) + if (stplyr->skincolor) colornum = stplyr->skincolor; else colornum = cv_playercolor.value; From 5d33ca42cfef4e57e38013815d9ae2d390244b2a Mon Sep 17 00:00:00 2001 From: Tatsuru <44866610+Ikkarin@users.noreply.github.com> Date: Sat, 11 Jan 2020 20:40:57 -0300 Subject: [PATCH 03/54] Expose colorization to mappers and SOC --- src/d_netcmd.c | 7 ++++--- src/d_player.h | 2 ++ src/dehacked.c | 6 +++++- src/info.h | 1 + src/p_enemy.c | 34 ++++++++++++++++++++++++++++++++++ src/p_setup.c | 4 ++++ src/p_spec.c | 39 +++++++++++++++++++++++++++++++++++++-- src/p_user.c | 13 ++++++++++++- 8 files changed, 99 insertions(+), 7 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 29e68143c..4bf592810 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1251,7 +1251,7 @@ static void SendNameAndColor(void) players[consoleplayer].skincolor = cv_playercolor.value; - if (players[consoleplayer].mo) + if (players[consoleplayer].mo && !players[consoleplayer].powers[pw_dye]) players[consoleplayer].mo->color = players[consoleplayer].skincolor; if (metalrecording) @@ -1363,8 +1363,9 @@ static void SendNameAndColor2(void) if (botingame) { players[secondplaya].skincolor = botcolor; - if (players[secondplaya].mo) + if (players[secondplaya].mo && !players[secondplaya].powers[pw_dye]) players[secondplaya].mo->color = players[secondplaya].skincolor; + SetPlayerSkinByNum(secondplaya, botskin-1); return; } @@ -1377,7 +1378,7 @@ static void SendNameAndColor2(void) // don't use secondarydisplayplayer: the second player must be 1 players[secondplaya].skincolor = cv_playercolor2.value; - if (players[secondplaya].mo) + if (players[secondplaya].mo && !players[secondplaya].powers[pw_dye]) players[secondplaya].mo->color = players[secondplaya].skincolor; if (cv_forceskin.value >= 0 && (netgame || multiplayer)) // Server wants everyone to use the same player diff --git a/src/d_player.h b/src/d_player.h index 62f38193f..2344c9c85 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -278,6 +278,8 @@ typedef enum pw_nights_linkfreeze, pw_nocontrol, //for linedef exec 427 + + pw_dye, // for dyes NUMPOWERS } powertype_t; diff --git a/src/dehacked.c b/src/dehacked.c index 091371122..85a020936 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -2918,6 +2918,7 @@ static actionpointer_t actionpointers[] = {{A_SetRandomTics}, "A_SETRANDOMTICS"}, {{A_ChangeColorRelative}, "A_CHANGECOLORRELATIVE"}, {{A_ChangeColorAbsolute}, "A_CHANGECOLORABSOLUTE"}, + {{A_Dye}, "A_DYE"}, {{A_MoveRelative}, "A_MOVERELATIVE"}, {{A_MoveAbsolute}, "A_MOVEABSOLUTE"}, {{A_Thrust}, "A_THRUST"}, @@ -9129,7 +9130,10 @@ static const char *const POWERS_LIST[] = { "NIGHTS_LINKFREEZE", //for linedef exec 427 - "NOCONTROL" + "NOCONTROL", + + //for dyes + "DYE" }; static const char *const HUDITEMS_LIST[] = { diff --git a/src/info.h b/src/info.h index 324795d45..33f597e61 100644 --- a/src/info.h +++ b/src/info.h @@ -165,6 +165,7 @@ void A_SetTics(); void A_SetRandomTics(); void A_ChangeColorRelative(); void A_ChangeColorAbsolute(); +void A_Dye(); void A_MoveRelative(); void A_MoveAbsolute(); void A_Thrust(); diff --git a/src/p_enemy.c b/src/p_enemy.c index bd7b81d40..d8e75f7f2 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -191,6 +191,7 @@ void A_SetTics(mobj_t *actor); void A_SetRandomTics(mobj_t *actor); void A_ChangeColorRelative(mobj_t *actor); void A_ChangeColorAbsolute(mobj_t *actor); +void A_Dye(mobj_t *actor); void A_MoveRelative(mobj_t *actor); void A_MoveAbsolute(mobj_t *actor); void A_Thrust(mobj_t *actor); @@ -8949,6 +8950,39 @@ void A_ChangeColorAbsolute(mobj_t *actor) actor->color = (UINT8)locvar2; } +// Function: A_Dye +// +// Description: Colorizes an object. +// +// var1 = if (var1 > 0), dye your target instead of yourself +// var2 = if (var1 = 0), color value to dye +// +void A_Dye(mobj_t *actor) +{ + INT32 locvar1 = var1; + INT32 locvar2 = var2; +#ifdef HAVE_BLUA + if (LUA_CallAction("A_Dye", actor)) + return; +#endif + mobj_t *target = ((locvar1 && actor->target) ? actor->target : actor); + UINT8 color = (UINT8)locvar2; + + // What if it's a player? + if (target->player) + { + target->player->powers[pw_dye] = color; + return; + } + + target->color = color; + + if (!color) + target->colorized = false; + else + target->colorized = true; +} + // Function: A_MoveRelative // // Description: Moves an object (wrapper for P_Thrust) diff --git a/src/p_setup.c b/src/p_setup.c index cfe141381..20dfc633b 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -1185,10 +1185,14 @@ static void P_LoadSidedefs(UINT8 *data) case 9: // Mace parameters case 14: // Bustable block parameters case 15: // Fan particle spawner parameters + case 334: // Trigger linedef executor: Object dye - Continuous + case 335: // Trigger linedef executor: Object dye - Each time + case 336: // Trigger linedef executor: Object dye - Once case 425: // Calls P_SetMobjState on calling mobj case 434: // Custom Power case 442: // Calls P_SetMobjState on mobjs of a given type in the tagged sectors case 461: // Spawns an object on the map based on texture offsets + case 463: // Colorizes an object { char process[8*3+1]; memset(process,0,8*3+1); diff --git a/src/p_spec.c b/src/p_spec.c index f2cb17e0e..8089896a7 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -2034,6 +2034,17 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller if (!(actor && actor->player && ((stricmp(triggerline->text, skins[actor->player->skin].name) == 0) ^ ((triggerline->flags & ML_NOCLIMB) == ML_NOCLIMB)))) return false; break; + case 334: // object dye - continuous + case 335: // object dye - each time + case 336: // object dye - once + { + INT32 triggercolor = (INT32)sides[triggerline->sidenum[0]].toptexture; + UINT8 color = (actor->player ? actor->player->powers[pw_dye] : actor->color); + boolean invert = (triggerline->flags & ML_NOCLIMB ? true : false); + + if (invert ^ (triggercolor != color)) + return false; + } default: break; } @@ -2167,6 +2178,7 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller || specialtype == 328 // Nights lap - Once || specialtype == 330 // Nights Bonus Time - Once || specialtype == 333 // Skin - Once + || specialtype == 336 // Dye - Once || specialtype == 399) // Level Load triggerline->special = 0; // Clear it out @@ -2208,7 +2220,8 @@ void P_LinedefExecute(INT16 tag, mobj_t *actor, sector_t *caller) || lines[masterline].special == 310 // CTF Red team - Each time || lines[masterline].special == 312 // CTF Blue team - Each time || lines[masterline].special == 322 // Trigger on X calls - Each Time - || lines[masterline].special == 332)// Skin - Each time + || lines[masterline].special == 332 // Skin - Each time + || lines[masterline].special == 335)// Dye - Each time continue; if (lines[masterline].special < 300 @@ -4041,7 +4054,23 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) } } break; - + + case 463: // Dye object + { + INT32 color = sides[line->sidenum[0]].toptexture; + + if (mo) + { + if (color < 0 || color > MAXSKINCOLORS) + return; + + var1 = 0; + var2 = color; + A_Dye(mo); + } + } + break; + #ifdef POLYOBJECTS case 480: // Polyobj_DoorSlide case 481: // Polyobj_DoorSwing @@ -7208,6 +7237,7 @@ void P_SpawnSpecials(boolean fromnetsave) case 310: case 312: case 332: + case 335: sec = sides[*lines[i].sidenum].sector - sectors; P_AddEachTimeThinker(§ors[sec], &lines[i]); break; @@ -7260,6 +7290,11 @@ void P_SpawnSpecials(boolean fromnetsave) case 331: case 333: break; + + // Object dye executors + case 334: + case 336: + break; case 399: // Linedef execute on map load // This is handled in P_RunLevelLoadExecutors. diff --git a/src/p_user.c b/src/p_user.c index 2bd856481..aa632d365 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -12270,7 +12270,18 @@ void P_PlayerThink(player_t *player) player->powers[pw_nocontrol]--; else player->powers[pw_nocontrol] = 0; - + + if (player->powers[pw_dye]) + { + player->mo->colorized = true; + player->mo->color = player->powers[pw_dye]; + } + else + { + player->mo->colorized = false; + player->mo->color = player->skincolor; + } + //pw_super acts as a timer now if (player->powers[pw_super] && (player->mo->state < &states[S_PLAY_SUPER_TRANS1] From 4221f08a7c82e6650ab48b6adad41ce22b39d260 Mon Sep 17 00:00:00 2001 From: Tatsuru <44866610+Ikkarin@users.noreply.github.com> Date: Sat, 11 Jan 2020 21:35:19 -0300 Subject: [PATCH 04/54] toaster guidance --- src/p_user.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/p_user.c b/src/p_user.c index aa632d365..0d96ced30 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -12271,17 +12271,6 @@ void P_PlayerThink(player_t *player) else player->powers[pw_nocontrol] = 0; - if (player->powers[pw_dye]) - { - player->mo->colorized = true; - player->mo->color = player->powers[pw_dye]; - } - else - { - player->mo->colorized = false; - player->mo->color = player->skincolor; - } - //pw_super acts as a timer now if (player->powers[pw_super] && (player->mo->state < &states[S_PLAY_SUPER_TRANS1] @@ -12927,6 +12916,17 @@ void P_PlayerAfterThink(player_t *player) player->mo->flags2 |= MF2_DONTDRAW; player->mo->flags |= MF_NOGRAVITY; } + + if (player->powers[pw_dye]) + { + player->mo->colorized = true; + player->mo->color = player->powers[pw_dye]; + } + else + { + player->mo->colorized = false; + player->mo->color = player->skincolor; + } if (player->followmobj && (player->spectator || player->mo->health <= 0 || player->followmobj->type != player->followitem)) { From 7b3cc02ddc3d78a3337defaeb5df375c197a1b64 Mon Sep 17 00:00:00 2001 From: Tatsuru <44866610+Ikkarin@users.noreply.github.com> Date: Sat, 11 Jan 2020 21:38:10 -0300 Subject: [PATCH 05/54] Range check --- src/p_enemy.c | 7 +++++-- src/p_spec.c | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/p_enemy.c b/src/p_enemy.c index d8e75f7f2..bfdd1b8de 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -8954,8 +8954,8 @@ void A_ChangeColorAbsolute(mobj_t *actor) // // Description: Colorizes an object. // -// var1 = if (var1 > 0), dye your target instead of yourself -// var2 = if (var1 = 0), color value to dye +// var1 = if (var1 != 0), dye your target instead of yourself +// var2 = color value to dye // void A_Dye(mobj_t *actor) { @@ -8968,6 +8968,9 @@ void A_Dye(mobj_t *actor) mobj_t *target = ((locvar1 && actor->target) ? actor->target : actor); UINT8 color = (UINT8)locvar2; + if (color >= MAXTRANSLATIONS) + return; + // What if it's a player? if (target->player) { diff --git a/src/p_spec.c b/src/p_spec.c index 8089896a7..c7558582a 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -4061,7 +4061,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) if (mo) { - if (color < 0 || color > MAXSKINCOLORS) + if (color < 0 || color >= MAXTRANSLATIONS) return; var1 = 0; From 0fa638eaf51b9f8b50b757d182e1c27a04dbf328 Mon Sep 17 00:00:00 2001 From: Tatsuru <44866610+Ikkarin@users.noreply.github.com> Date: Mon, 13 Jan 2020 19:12:02 -0300 Subject: [PATCH 06/54] Logistical problems --- src/p_enemy.c | 9 ++++----- src/p_user.c | 5 ----- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/p_enemy.c b/src/p_enemy.c index bfdd1b8de..21cf672a4 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -8971,6 +8971,9 @@ void A_Dye(mobj_t *actor) if (color >= MAXTRANSLATIONS) return; + if (!color) + target->colorized = false; + // What if it's a player? if (target->player) { @@ -8979,11 +8982,7 @@ void A_Dye(mobj_t *actor) } target->color = color; - - if (!color) - target->colorized = false; - else - target->colorized = true; + target->colorized = true; } // Function: A_MoveRelative diff --git a/src/p_user.c b/src/p_user.c index 0d96ced30..45f4a4562 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -12922,11 +12922,6 @@ void P_PlayerAfterThink(player_t *player) player->mo->colorized = true; player->mo->color = player->powers[pw_dye]; } - else - { - player->mo->colorized = false; - player->mo->color = player->skincolor; - } if (player->followmobj && (player->spectator || player->mo->health <= 0 || player->followmobj->type != player->followitem)) { From fb19d25de175264742c7561d3098823c0d763341 Mon Sep 17 00:00:00 2001 From: Tatsuru <44866610+Ikkarin@users.noreply.github.com> Date: Tue, 14 Jan 2020 00:52:54 -0300 Subject: [PATCH 07/54] What --- src/p_enemy.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/p_enemy.c b/src/p_enemy.c index 21cf672a4..6b20862ed 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -8973,7 +8973,9 @@ void A_Dye(mobj_t *actor) if (!color) target->colorized = false; - + else + target->colorized = true; + // What if it's a player? if (target->player) { @@ -8982,7 +8984,6 @@ void A_Dye(mobj_t *actor) } target->color = color; - target->colorized = true; } // Function: A_MoveRelative From 295f052f2decc703acd60d2b6f4c648bc0e40579 Mon Sep 17 00:00:00 2001 From: Tatsuru <44866610+Ikkarin@users.noreply.github.com> Date: Fri, 24 Jan 2020 01:03:38 -0300 Subject: [PATCH 08/54] I don't know how these even come up --- src/p_user.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_user.c b/src/p_user.c index 45f4a4562..288f0dbb5 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -12916,7 +12916,7 @@ void P_PlayerAfterThink(player_t *player) player->mo->flags2 |= MF2_DONTDRAW; player->mo->flags |= MF_NOGRAVITY; } - + if (player->powers[pw_dye]) { player->mo->colorized = true; From 0218f58f489e7ca00f1c8a6747f3837a1df95171 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 2 Feb 2020 15:08:22 -0800 Subject: [PATCH 09/54] userdataType can check light userdata too! --- src/lua_baselib.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 66bd30e32..7c53fa99a 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -220,10 +220,16 @@ static const char *GetUserdataUType(lua_State *L) // or players[0].powers -> "player_t.powers" static int lib_userdataType(lua_State *L) { + int type; lua_settop(L, 1); // pop everything except arg 1 (in case somebody decided to add more) - luaL_checktype(L, 1, LUA_TUSERDATA); - lua_pushstring(L, GetUserdataUType(L)); - return 1; + type = lua_type(L, 1); + if (type == LUA_TLIGHTUSERDATA || type == LUA_TUSERDATA) + { + lua_pushstring(L, GetUserdataUType(L)); + return 1; + } + else + return luaL_typerror(L, 1, "userdata"); } static int lib_isPlayerAdmin(lua_State *L) From deaf5cfa2895c21e50b10f7b0b3e4831729c4759 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 2 Feb 2020 15:19:19 -0800 Subject: [PATCH 10/54] LUA_PushLightUserdata takes the fun out of lib_cvFindVar --- src/lua_consolelib.c | 18 ++---------------- src/lua_script.c | 22 ++++++++++++++++++++++ src/lua_script.h | 1 + 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/src/lua_consolelib.c b/src/lua_consolelib.c index 48f2e20a8..62d59e582 100644 --- a/src/lua_consolelib.c +++ b/src/lua_consolelib.c @@ -431,22 +431,8 @@ static int lib_cvRegisterVar(lua_State *L) static int lib_cvFindVar(lua_State *L) { - consvar_t *cv; - if (( cv = CV_FindVar(luaL_checkstring(L,1)) )) - { - lua_settop(L,1);/* We only want one argument in the stack. */ - lua_pushlightuserdata(L, cv);/* Now the second value on stack. */ - luaL_getmetatable(L, META_CVAR); - /* - The metatable is the last value on the stack, so this - applies it to the second value, which is the cvar. - */ - lua_setmetatable(L,2); - lua_pushvalue(L,2); - return 1; - } - else - return 0; + LUA_PushLightUserdata(L, CV_FindVar(luaL_checkstring(L,1)), META_CVAR); + return 1; } // CONS_Printf for a single player diff --git a/src/lua_script.c b/src/lua_script.c index 2538fb711..9d0a80f90 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -568,6 +568,28 @@ fixed_t LUA_EvalMath(const char *word) return res; } +/* +LUA_PushUserdata but no userdata is created. +You can't invalidate it therefore. +*/ + +void LUA_PushLightUserdata (lua_State *L, void *data, const char *meta) +{ + if (data) + { + lua_pushlightuserdata(L, data); + luaL_getmetatable(L, meta); + /* + The metatable is the last value on the stack, so this + applies it to the second value, which is the userdata. + */ + lua_setmetatable(L, -2); + lua_pushvalue(L, -1); + } + else + lua_pushnil(L); +} + // Takes a pointer, any pointer, and a metatable name // Creates a userdata for that pointer with the given metatable // Pushes it to the stack and stores it in the registry. diff --git a/src/lua_script.h b/src/lua_script.h index 8f27dcb4c..d54387175 100644 --- a/src/lua_script.h +++ b/src/lua_script.h @@ -46,6 +46,7 @@ void LUA_LoadLump(UINT16 wad, UINT16 lump); void LUA_DumpFile(const char *filename); #endif fixed_t LUA_EvalMath(const char *word); +void LUA_PushLightUserdata(lua_State *L, void *data, const char *meta); void LUA_PushUserdata(lua_State *L, void *data, const char *meta); void LUA_InvalidateUserdata(void *data); void LUA_InvalidateLevel(void); From 35f1a4b76c600526a53f4a154bda6cfaca65340b Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 2 Feb 2020 17:22:28 -0800 Subject: [PATCH 11/54] lua_pushvalue is unneeded --- src/lua_script.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lua_script.c b/src/lua_script.c index 9d0a80f90..02f78cd35 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -584,7 +584,6 @@ void LUA_PushLightUserdata (lua_State *L, void *data, const char *meta) applies it to the second value, which is the userdata. */ lua_setmetatable(L, -2); - lua_pushvalue(L, -1); } else lua_pushnil(L); From 57a4545fdff6710782497601cd2223b4d3026afc Mon Sep 17 00:00:00 2001 From: fickleheart Date: Fri, 7 Feb 2020 00:01:20 -0600 Subject: [PATCH 12/54] PK3: Proper ignorance for file comments/extra data --- src/w_wad.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/w_wad.c b/src/w_wad.c index 1df2eacc7..00bdb7183 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -580,7 +580,7 @@ static lumpinfo_t* ResGetLumpsZip (FILE* handle, UINT16* nlmp) return NULL; } - lump_p->position = zentry->offset + zentry->namelen + zentry->xtralen + sizeof(zlentry_t); + lump_p->position = zentry->offset + zentry->namelen + sizeof(zlentry_t); lump_p->disksize = zentry->compsize; lump_p->size = zentry->size; @@ -629,6 +629,15 @@ static lumpinfo_t* ResGetLumpsZip (FILE* handle, UINT16* nlmp) lump_p->compression = CM_UNSUPPORTED; break; } + + // skip and ignore comments/extra fields + if (fseek(handle, zentry->xtralen + zentry->commlen, SEEK_CUR) != 0) + { + CONS_Alert(CONS_ERROR, "Central directory %d is corrupt (%02x%02x%02x%02x)\n", i, zentry->signature[0], zentry->signature[1], zentry->signature[2], zentry->signature[3]); + Z_Free(lumpinfo); + free(zentries); + return NULL; + } } free(zentries); From 8cb9d6f670c1982171f41d0a4ec715a6cd918c0c Mon Sep 17 00:00:00 2001 From: fickleheart Date: Fri, 7 Feb 2020 00:04:02 -0600 Subject: [PATCH 13/54] Uhhh do VerifyPk3 too --- src/w_wad.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/w_wad.c b/src/w_wad.c index 00bdb7183..9f36e5d2a 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -1819,6 +1819,10 @@ W_VerifyPK3 (FILE *fp, lumpchecklist_t *checklist, boolean status) } free(fullname); + + // skip and ignore comments/extra fields + if (fseek(fp, zentry->xtralen + zentry->commlen, SEEK_CUR) != 0) + return true; } return true; From c4ee113c710b6859cf837e9c0e8f1d7d694ed746 Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 7 Feb 2020 00:53:56 -0800 Subject: [PATCH 14/54] Let MUSICDEF set loop point --- src/s_sound.c | 15 +++++++++++++++ src/s_sound.h | 1 + 2 files changed, 16 insertions(+) diff --git a/src/s_sound.c b/src/s_sound.c index d84e20ab4..0854d9b67 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -1456,6 +1456,7 @@ musicdef_t soundtestsfx = { 0, // with no conditions 0, 0, + 0, false, NULL }; @@ -1651,6 +1652,8 @@ ReadMusicDefFields (UINT16 wadnum, int line, boolean fields, char *stoken, fixed_t bpmf = FLOAT_TO_FIXED(bpm); if (bpmf > 0) def->bpm = FixedDiv((60*TICRATE)<loop_ms = atoi(textline); } else { CONS_Alert(CONS_WARNING, "MUSICDEF: Invalid field '%s'. (file %s, line %d)\n", @@ -2262,6 +2265,8 @@ static void S_UnloadMusic(void) static boolean S_PlayMusic(boolean looping, UINT32 fadeinms) { + musicdef_t *def; + if (S_MusicDisabled()) return false; @@ -2273,6 +2278,16 @@ static boolean S_PlayMusic(boolean looping, UINT32 fadeinms) return false; } + /* set loop point from MUSICDEF */ + for (def = musicdefstart; def; def = def->next) + { + if (strcasecmp(def->name, music_name) == 0) + { + S_SetMusicLoopPoint(def->loop_ms); + break; + } + } + S_InitMusicVolume(); // switch between digi and sequence volume if (S_MusicNotInFocus()) diff --git a/src/s_sound.h b/src/s_sound.h index 9a4cbe48b..c7ec7e5d1 100644 --- a/src/s_sound.h +++ b/src/s_sound.h @@ -208,6 +208,7 @@ typedef struct musicdef_s INT16 soundtestcond; // +ve for map, -ve for conditionset, 0 for already here tic_t stoppingtics; fixed_t bpm; + UINT32 loop_ms;/* override LOOPPOINT/LOOPMS */ boolean allowed; // question marks or listenable on sound test? struct musicdef_s *next; } musicdef_t; From 3d00596c6a53c3e20cee7a8ebfd4a7057987003b Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 7 Feb 2020 12:51:21 -0800 Subject: [PATCH 15/54] Don't override loop point if unset in MUSICDEF --- src/s_sound.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/s_sound.c b/src/s_sound.c index 0854d9b67..4a335bbe6 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -2283,7 +2283,8 @@ static boolean S_PlayMusic(boolean looping, UINT32 fadeinms) { if (strcasecmp(def->name, music_name) == 0) { - S_SetMusicLoopPoint(def->loop_ms); + if (def->loop_ms) + S_SetMusicLoopPoint(def->loop_ms); break; } } From f7cf8e50f86824084307a71a6d3f146707e921ae Mon Sep 17 00:00:00 2001 From: fickleheart Date: Fri, 7 Feb 2020 18:43:20 -0600 Subject: [PATCH 16/54] Oops --- src/w_wad.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/w_wad.c b/src/w_wad.c index 9f36e5d2a..3e8502b5e 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -633,7 +633,7 @@ static lumpinfo_t* ResGetLumpsZip (FILE* handle, UINT16* nlmp) // skip and ignore comments/extra fields if (fseek(handle, zentry->xtralen + zentry->commlen, SEEK_CUR) != 0) { - CONS_Alert(CONS_ERROR, "Central directory %d is corrupt (%02x%02x%02x%02x)\n", i, zentry->signature[0], zentry->signature[1], zentry->signature[2], zentry->signature[3]); + CONS_Alert(CONS_ERROR, "Central directory is corrupt\n"); Z_Free(lumpinfo); free(zentries); return NULL; @@ -1821,7 +1821,7 @@ W_VerifyPK3 (FILE *fp, lumpchecklist_t *checklist, boolean status) free(fullname); // skip and ignore comments/extra fields - if (fseek(fp, zentry->xtralen + zentry->commlen, SEEK_CUR) != 0) + if (fseek(fp, zentry.xtralen + zentry.commlen, SEEK_CUR) != 0) return true; } From ee9aa86ecd8ffdbcd7913193a02365e5a536df84 Mon Sep 17 00:00:00 2001 From: fickleheart Date: Fri, 7 Feb 2020 18:46:46 -0600 Subject: [PATCH 17/54] Use the proper numbers to adjust lump's offset position --- src/w_wad.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/w_wad.c b/src/w_wad.c index 3e8502b5e..874b99cc7 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -529,6 +529,7 @@ static lumpinfo_t* ResGetLumpsZip (FILE* handle, UINT16* nlmp) zend_t zend; zentry_t* zentries; zentry_t* zentry; + zlentry_t zlentry; UINT16 numlumps = *nlmp; lumpinfo_t* lumpinfo; @@ -580,7 +581,7 @@ static lumpinfo_t* ResGetLumpsZip (FILE* handle, UINT16* nlmp) return NULL; } - lump_p->position = zentry->offset + zentry->namelen + sizeof(zlentry_t); + lump_p->position = zentry->offset; // NOT ACCURATE YET: we still need to read the local entry to find our true position lump_p->disksize = zentry->compsize; lump_p->size = zentry->size; @@ -642,6 +643,20 @@ static lumpinfo_t* ResGetLumpsZip (FILE* handle, UINT16* nlmp) free(zentries); + // Adjust lump position values properly + for (i = 0, lump_p = lumpinfo; i < numlumps; i++, lump_p++) + { + // skip and ignore comments/extra fields + if ((fseek(handle, lump_p->position, SEEK_SET) != 0) || (fread(&zlentry, 1, sizeof(zlentry_t), handle) < sizeof(zlentry_t))) + { + CONS_Alert(CONS_ERROR, "Local headers for lump %s are corrupt\n", lump_p->name2); + Z_Free(lumpinfo); + return NULL; + } + + lump_p->position += sizeof(zlentry_t) + zlentry.namelen + zlentry.xtralen; + } + *nlmp = numlumps; return lumpinfo; } From fb7c4ab812a26ec3b1007cd5adbd9c300061cf38 Mon Sep 17 00:00:00 2001 From: fickleheart Date: Fri, 7 Feb 2020 18:47:56 -0600 Subject: [PATCH 18/54] zentries is unnecessary --- src/w_wad.c | 34 +++++++++++++--------------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/src/w_wad.c b/src/w_wad.c index 874b99cc7..9137c477f 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -527,8 +527,7 @@ typedef struct zlentry_s static lumpinfo_t* ResGetLumpsZip (FILE* handle, UINT16* nlmp) { zend_t zend; - zentry_t* zentries; - zentry_t* zentry; + zentry_t zentry; zlentry_t zlentry; UINT16 numlumps = *nlmp; @@ -557,40 +556,36 @@ static lumpinfo_t* ResGetLumpsZip (FILE* handle, UINT16* nlmp) numlumps = zend.entries; lump_p = lumpinfo = Z_Malloc(numlumps * sizeof (*lumpinfo), PU_STATIC, NULL); - zentry = zentries = malloc(numlumps * sizeof (*zentries)); fseek(handle, zend.cdiroffset, SEEK_SET); - for (i = 0; i < numlumps; i++, zentry++, lump_p++) + for (i = 0; i < numlumps; i++, lump_p++) { char* fullname; char* trimname; char* dotpos; - if (fread(zentry, 1, sizeof(zentry_t), handle) < sizeof(zentry_t)) + if (fread(&zentry, 1, sizeof(zentry_t), handle) < sizeof(zentry_t)) { CONS_Alert(CONS_ERROR, "Failed to read central directory (%s)\n", M_FileError(handle)); Z_Free(lumpinfo); - free(zentries); return NULL; } - if (memcmp(zentry->signature, pat_central, 4)) + if (memcmp(zentry.signature, pat_central, 4)) { CONS_Alert(CONS_ERROR, "Central directory is corrupt\n"); Z_Free(lumpinfo); - free(zentries); return NULL; } - lump_p->position = zentry->offset; // NOT ACCURATE YET: we still need to read the local entry to find our true position - lump_p->disksize = zentry->compsize; - lump_p->size = zentry->size; + lump_p->position = zentry.offset; // NOT ACCURATE YET: we still need to read the local entry to find our true position + lump_p->disksize = zentry.compsize; + lump_p->size = zentry.size; - fullname = malloc(zentry->namelen + 1); - if (fgets(fullname, zentry->namelen + 1, handle) != fullname) + fullname = malloc(zentry.namelen + 1); + if (fgets(fullname, zentry.namelen + 1, handle) != fullname) { CONS_Alert(CONS_ERROR, "Unable to read lumpname (%s)\n", M_FileError(handle)); Z_Free(lumpinfo); - free(zentries); free(fullname); return NULL; } @@ -607,12 +602,12 @@ static lumpinfo_t* ResGetLumpsZip (FILE* handle, UINT16* nlmp) memset(lump_p->name, '\0', 9); // Making sure they're initialized to 0. Is it necessary? strncpy(lump_p->name, trimname, min(8, dotpos - trimname)); - lump_p->name2 = Z_Calloc(zentry->namelen + 1, PU_STATIC, NULL); - strncpy(lump_p->name2, fullname, zentry->namelen); + lump_p->name2 = Z_Calloc(zentry.namelen + 1, PU_STATIC, NULL); + strncpy(lump_p->name2, fullname, zentry.namelen); free(fullname); - switch(zentry->compression) + switch(zentry.compression) { case 0: lump_p->compression = CM_NOCOMPRESSION; @@ -632,17 +627,14 @@ static lumpinfo_t* ResGetLumpsZip (FILE* handle, UINT16* nlmp) } // skip and ignore comments/extra fields - if (fseek(handle, zentry->xtralen + zentry->commlen, SEEK_CUR) != 0) + if (fseek(handle, zentry.xtralen + zentry.commlen, SEEK_CUR) != 0) { CONS_Alert(CONS_ERROR, "Central directory is corrupt\n"); Z_Free(lumpinfo); - free(zentries); return NULL; } } - free(zentries); - // Adjust lump position values properly for (i = 0, lump_p = lumpinfo; i < numlumps; i++, lump_p++) { From 0a53f91820d116e23a2af9260074c5ab778308bc Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 7 Feb 2020 19:41:45 -0800 Subject: [PATCH 19/54] Don't skip the first directory entry of PK3 if it's a file Also puts the marker skipping in the WAD API. --- src/r_data.c | 20 ++++++-------------- src/r_things.c | 6 ++---- src/w_wad.c | 23 +++++++++++++++++++---- src/w_wad.h | 3 +++ 4 files changed, 30 insertions(+), 22 deletions(-) diff --git a/src/r_data.c b/src/r_data.c index 5608fdbde..e94368288 100644 --- a/src/r_data.c +++ b/src/r_data.c @@ -716,14 +716,12 @@ Rloadflats (INT32 i, INT32 w) } else { - texstart = W_CheckNumForNamePwad("F_START", (UINT16)w, 0); + texstart = W_CheckNumForMarkerStartPwad("F_START", (UINT16)w, 0); texend = W_CheckNumForNamePwad("F_END", (UINT16)w, texstart); } if (!( texstart == INT16_MAX || texend == INT16_MAX )) { - texstart++; // Do not count the first marker - // Work through each lump between the markers in the WAD. for (j = 0; j < (texend - texstart); j++) { @@ -836,7 +834,7 @@ Rloadtextures (INT32 i, INT32 w) } else { - texstart = W_CheckNumForNamePwad(TX_START, (UINT16)w, 0); + texstart = W_CheckNumForMarkerStartPwad(TX_START, (UINT16)w, 0); texend = W_CheckNumForNamePwad(TX_END, (UINT16)w, 0); texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, 0); if (texturesLumpPos != INT16_MAX) @@ -845,8 +843,6 @@ Rloadtextures (INT32 i, INT32 w) if (!( texstart == INT16_MAX || texend == INT16_MAX )) { - texstart++; // Do not count the first marker - // Work through each lump between the markers in the WAD. for (j = 0; j < (texend - texstart); j++) { @@ -953,14 +949,12 @@ void R_LoadTextures(void) } else { - texstart = W_CheckNumForNamePwad("F_START", (UINT16)w, 0); + texstart = W_CheckNumForMarkerStartPwad("F_START", (UINT16)w, 0); texend = W_CheckNumForNamePwad("F_END", (UINT16)w, texstart); } if (!( texstart == INT16_MAX || texend == INT16_MAX )) { - texstart++; // Do not count the first marker - // PK3s have subfolders, so we can't just make a simple sum if (wadfiles[w]->type == RET_PK3) { @@ -993,15 +987,13 @@ void R_LoadTextures(void) } else { - texstart = W_CheckNumForNamePwad(TX_START, (UINT16)w, 0); + texstart = W_CheckNumForMarkerStartPwad(TX_START, (UINT16)w, 0); texend = W_CheckNumForNamePwad(TX_END, (UINT16)w, 0); } if (texstart == INT16_MAX || texend == INT16_MAX) continue; - texstart++; // Do not count the first marker - // PK3s have subfolders, so we can't just make a simple sum if (wadfiles[w]->type == RET_PK3) { @@ -1587,9 +1579,9 @@ lumpnum_t R_GetFlatNumForName(const char *name) switch (wadfiles[i]->type) { case RET_WAD: - if ((start = W_CheckNumForNamePwad("F_START", (UINT16)i, 0)) == INT16_MAX) + if ((start = W_CheckNumForMarkerStartPwad("F_START", (UINT16)i, 0)) == INT16_MAX) { - if ((start = W_CheckNumForNamePwad("FF_START", (UINT16)i, 0)) == INT16_MAX) + if ((start = W_CheckNumForMarkerStartPwad("FF_START", (UINT16)i, 0)) == INT16_MAX) continue; else if ((end = W_CheckNumForNamePwad("FF_END", (UINT16)i, start)) == INT16_MAX) continue; diff --git a/src/r_things.c b/src/r_things.c index 7f0f43281..75210d4ab 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -437,9 +437,9 @@ void R_AddSpriteDefs(UINT16 wadnum) switch (wadfiles[wadnum]->type) { case RET_WAD: - start = W_CheckNumForNamePwad("S_START", wadnum, 0); + start = W_CheckNumForMarkerStartPwad("S_START", wadnum, 0); if (start == INT16_MAX) - start = W_CheckNumForNamePwad("SS_START", wadnum, 0); //deutex compatib. + start = W_CheckNumForMarkerStartPwad("SS_START", wadnum, 0); //deutex compatib. end = W_CheckNumForNamePwad("S_END",wadnum,start); if (end == INT16_MAX) @@ -461,8 +461,6 @@ void R_AddSpriteDefs(UINT16 wadnum) start = 0; //let say S_START is lump 0 } - else - start++; // just after S_START if (end == INT16_MAX || start >= end) { diff --git a/src/w_wad.c b/src/w_wad.c index 1df2eacc7..ea8a444b0 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -194,7 +194,6 @@ static inline void W_LoadDehackedLumpsPK3(UINT16 wadnum, boolean mainfile) if (posStart != INT16_MAX) { posEnd = W_CheckNumForFolderEndPK3("Lua/", wadnum, posStart); - posStart++; // first "lump" will be "Lua/" folder itself, so ignore it for (; posStart < posEnd; posStart++) LUA_LoadLump(wadnum, posStart); } @@ -204,7 +203,6 @@ static inline void W_LoadDehackedLumpsPK3(UINT16 wadnum, boolean mainfile) { posEnd = W_CheckNumForFolderEndPK3("SOC/", wadnum, posStart); - posStart++; // first "lump" will be "SOC/" folder itself, so ignore it for(; posStart < posEnd; posStart++) { lumpinfo_t *lump_p = &wadfiles[wadnum]->lumpinfo[posStart]; @@ -912,15 +910,32 @@ UINT16 W_CheckNumForNamePwad(const char *name, UINT16 wad, UINT16 startlump) return INT16_MAX; } +UINT16 +W_CheckNumForMarkerStartPwad (const char *name, UINT16 wad, UINT16 startlump) +{ + UINT16 marker; + marker = W_CheckNumForNamePwad(name, wad, startlump); + if (marker != INT16_MAX) + marker++; // Do not count the first marker + return marker; +} + // Look for the first lump from a folder. UINT16 W_CheckNumForFolderStartPK3(const char *name, UINT16 wad, UINT16 startlump) { + size_t name_length; INT32 i; lumpinfo_t *lump_p = wadfiles[wad]->lumpinfo + startlump; + name_length = strlen(name); for (i = startlump; i < wadfiles[wad]->numlumps; i++, lump_p++) { - if (strnicmp(name, lump_p->name2, strlen(name)) == 0) + if (strnicmp(name, lump_p->name2, name_length) == 0) + { + /* SLADE is special and puts a single directory entry. Skip that. */ + if (strlen(lump_p->name2) == name_length) + i++; break; + } } return i; } @@ -1023,7 +1038,7 @@ lumpnum_t W_CheckNumForMap(const char *name) else continue; // Now look for the specified map. - for (++lumpNum; lumpNum < end; lumpNum++) + for (; lumpNum < end; lumpNum++) if (!strnicmp(name, (wadfiles[i]->lumpinfo + lumpNum)->name, 8)) return (i<<16) + lumpNum; } diff --git a/src/w_wad.h b/src/w_wad.h index aca67c00f..e07ceaf2e 100644 --- a/src/w_wad.h +++ b/src/w_wad.h @@ -157,6 +157,9 @@ const char *W_CheckNameForNum(lumpnum_t lumpnum); UINT16 W_CheckNumForNamePwad(const char *name, UINT16 wad, UINT16 startlump); // checks only in one pwad +/* Find the first lump after F_START for instance. */ +UINT16 W_CheckNumForMarkerStartPwad(const char *name, UINT16 wad, UINT16 startlump); + UINT16 W_CheckNumForFullNamePK3(const char *name, UINT16 wad, UINT16 startlump); UINT16 W_CheckNumForFolderStartPK3(const char *name, UINT16 wad, UINT16 startlump); UINT16 W_CheckNumForFolderEndPK3(const char *name, UINT16 wad, UINT16 startlump); From 934808ef6202ffb4eec2f3753fc98cd22c630bec Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Wed, 12 Feb 2020 14:16:23 -0500 Subject: [PATCH 20/54] BotRespawn hook Lets you force when and when not to respawn the bot --- src/b_bot.c | 15 +++++++++++++++ src/lua_hook.h | 2 ++ src/lua_hooklib.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+) diff --git a/src/b_bot.c b/src/b_bot.c index f83aaa34c..3e8275bdd 100644 --- a/src/b_bot.c +++ b/src/b_bot.c @@ -463,6 +463,21 @@ boolean B_CheckRespawn(player_t *player) if (!sonic || sonic->health <= 0) return false; +#ifdef HAVE_BLUA + // B_RespawnBot doesn't do anything if the condition above this isn't met + { + UINT8 shouldForce = LUAh_BotRespawn(sonic, tails); + + if (P_MobjWasRemoved(sonic) || P_MobjWasRemoved(tails)) + return (shouldForce == 1); // mobj was removed + + if (shouldForce == 1) + return true; + else if (shouldForce == 2) + return false; + } +#endif + // Check if Sonic is busy first. // If he's doing any of these things, he probably doesn't want to see us. if (sonic->player->pflags & (PF_GLIDING|PF_SLIDING|PF_BOUNCING) diff --git a/src/lua_hook.h b/src/lua_hook.h index 94d2239f7..4bacf573a 100644 --- a/src/lua_hook.h +++ b/src/lua_hook.h @@ -42,6 +42,7 @@ enum hook { hook_JumpSpinSpecial, hook_BotTiccmd, hook_BotAI, + hook_BotRespawn, hook_LinedefExecute, hook_PlayerMsg, hook_HurtMsg, @@ -92,6 +93,7 @@ boolean LUAh_MobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 #define LUAh_JumpSpinSpecial(player) LUAh_PlayerHook(player, hook_JumpSpinSpecial) // Hook for P_DoJumpStuff (Spin button effect (mid-air)) boolean LUAh_BotTiccmd(player_t *bot, ticcmd_t *cmd); // Hook for B_BuildTiccmd boolean LUAh_BotAI(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd); // Hook for B_BuildTailsTiccmd by skin name +boolean LUAh_BotRespawn(mobj_t *sonic, mobj_t *tails); // Hook for B_CheckRespawn boolean LUAh_LinedefExecute(line_t *line, mobj_t *mo, sector_t *sector); // Hook for linedef executors boolean LUAh_PlayerMsg(int source, int target, int flags, char *msg); // Hook for chat messages boolean LUAh_HurtMsg(player_t *player, mobj_t *inflictor, mobj_t *source, UINT8 damagetype); // Hook for hurt messages diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c index 306bf6839..5c6b97ce0 100644 --- a/src/lua_hooklib.c +++ b/src/lua_hooklib.c @@ -53,6 +53,7 @@ const char *const hookNames[hook_MAX+1] = { "JumpSpinSpecial", "BotTiccmd", "BotAI", + "BotRespawn", "LinedefExecute", "PlayerMsg", "HurtMsg", @@ -1122,6 +1123,51 @@ boolean LUAh_BotAI(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd) return hooked; } +// Hook for B_CheckRespawn +boolean LUAh_BotRespawn(mobj_t *sonic, mobj_t *tails) +{ + hook_p hookp; + UINT8 shouldRespawn = 0; // 0 = default, 1 = force yes, 2 = force no. + if (!gL || !(hooksAvailable[hook_BotRespawn/8] & (1<<(hook_BotRespawn%8)))) + return false; + + lua_settop(gL, 0); + + for (hookp = roothook; hookp; hookp = hookp->next) + { + if (hookp->type != hook_BotRespawn) + continue; + + if (lua_gettop(gL) == 0) + { + LUA_PushUserdata(gL, sonic, META_MOBJ); + LUA_PushUserdata(gL, tails, META_MOBJ); + } + lua_pushfstring(gL, FMT_HOOKID, hookp->id); + lua_gettable(gL, LUA_REGISTRYINDEX); + lua_pushvalue(gL, -3); + lua_pushvalue(gL, -3); + if (lua_pcall(gL, 2, 1, 0)) { + if (!hookp->error || cv_debug & DBG_LUA) + CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); + lua_pop(gL, 1); + hookp->error = true; + continue; + } + if (!lua_isnil(gL, -1)) + { + if (lua_toboolean(gL, -1)) + shouldRespawn = 1; // Force yes + else + shouldRespawn = 2; // Force no + } + lua_pop(gL, 1); + } + + lua_settop(gL, 0); + return shouldRespawn; +} + // Hook for linedef executors boolean LUAh_LinedefExecute(line_t *line, mobj_t *mo, sector_t *sector) { From 64b7a5e4d6d71e53a10e6cefef4ce04993230882 Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 12 Feb 2020 18:03:39 -0800 Subject: [PATCH 21/54] SF_NONIGHTSSUPER for disabling super colors in NiGHTS --- src/d_player.h | 1 + src/dehacked.c | 1 + src/p_user.c | 2 +- src/r_things.c | 1 + 4 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/d_player.h b/src/d_player.h index db55a9913..250410c3b 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -48,6 +48,7 @@ typedef enum SF_FASTEDGE = 1<<12, // Faster edge teeter? SF_MULTIABILITY = 1<<13, // Revenge of Final Demo. SF_NONIGHTSROTATION = 1<<14, // Disable sprite rotation for NiGHTS + SF_NONIGHTSSUPER = 1<<15, // Disable super colors for NiGHTS (if you have SF_SUPER) // free up to and including 1<<31 } skinflags_t; diff --git a/src/dehacked.c b/src/dehacked.c index e7ff6e400..e48b1cb90 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -9477,6 +9477,7 @@ struct { {"SF_FASTEDGE",SF_FASTEDGE}, {"SF_MULTIABILITY",SF_MULTIABILITY}, {"SF_NONIGHTSROTATION",SF_NONIGHTSROTATION}, + {"SF_NONIGHTSSUPER",SF_NONIGHTSSUPER}, // Dashmode constants {"DASHMODE_THRESHOLD",DASHMODE_THRESHOLD}, diff --git a/src/p_user.c b/src/p_user.c index 176b07d0a..5b88148d5 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -8040,7 +8040,7 @@ static void P_MovePlayer(player_t *player) && player->mo->state < &states[S_PLAY_NIGHTS_TRANS6]))) // Note the < instead of <= { skin_t *skin = ((skin_t *)(player->mo->skin)); - if (skin->flags & SF_SUPER) + if (( skin->flags & (SF_SUPER|SF_NONIGHTSSUPER) ) == SF_SUPER) { player->mo->color = skin->supercolor + ((player->nightstime == player->startedtime) diff --git a/src/r_things.c b/src/r_things.c index 7f0f43281..ae4ccb5a8 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -3463,6 +3463,7 @@ static boolean R_ProcessPatchableFields(skin_t *skin, char *stoken, char *value) GETFLAG(DASHMODE) GETFLAG(FASTEDGE) GETFLAG(MULTIABILITY) + GETFLAG(NONIGHTSSUPER) #undef GETFLAG else // let's check if it's a sound, otherwise error out From 71319dce6b5cd1706c693942877149249e75b494 Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 12 Feb 2020 18:15:15 -0800 Subject: [PATCH 22/54] Revert "Update file hashes" This reverts commit 4281de3b89e4ace77bff9dca003feb26d9e4dbc5. --- src/config.h.in | 8 ++++---- src/d_main.c | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/config.h.in b/src/config.h.in index d4a613fdc..233cbdc53 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -26,12 +26,12 @@ #else /* Manually defined asset hashes for non-CMake builds - * Last updated 2020 / 02 / 09 - v2.2.1 - main assets + * Last updated 2019 / 12 / 06 - v2.2.0 - main assets * Last updated 20?? / ?? / ?? - v2.2.? - patch.pk3 */ -#define ASSET_HASH_SRB2_PK3 "0277c9416756627004e83cbb5b2e3e28" -#define ASSET_HASH_ZONES_PK3 "89627822f5a5c7fb022d836b138144b2" -#define ASSET_HASH_PLAYER_DTA "129fa7d4b273a4b3dcacaa44eccead4f" +#define ASSET_HASH_SRB2_PK3 "51419a33b4982d840c6772c159ba7c0a" +#define ASSET_HASH_ZONES_PK3 "df74843919fd51af26a0baa8e21e4c19" +#define ASSET_HASH_PLAYER_DTA "56a247e074dd0dc794b6617efef1e918" #ifdef USE_PATCH_DTA #define ASSET_HASH_PATCH_PK3 "there is no patch.pk3, only zuul" #endif diff --git a/src/d_main.c b/src/d_main.c index 27f250017..dc9bfbfea 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1213,7 +1213,7 @@ void D_SRB2Main(void) #endif D_CleanFile(); -#ifndef DEVELOP // md5s last updated 09/02/20 (ddmmyy) +#ifndef DEVELOP // md5s last updated 06/12/19 (ddmmyy) // Check MD5s of autoloaded files W_VerifyFileMD5(0, ASSET_HASH_SRB2_PK3); // srb2.pk3 From e53a17bb528800c6a3ec7ec2fcc36c5c6f96ba20 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Mon, 17 Feb 2020 21:11:56 -0300 Subject: [PATCH 23/54] Rename stuff around --- src/android/i_video.c | 10 ++++------ src/djgppdos/i_video.c | 5 +---- src/djgppdos/vid_vesa.c | 6 ++---- src/dummy/i_video.c | 10 ++++------ src/i_video.h | 18 +++++++++++------- src/m_menu.c | 2 +- src/screen.c | 4 ++-- src/sdl/i_video.c | 39 +++++++++++++++++---------------------- src/win32/win_vid.c | 11 +++-------- 9 files changed, 45 insertions(+), 60 deletions(-) diff --git a/src/android/i_video.c b/src/android/i_video.c index b8bb4fefb..1909cd71a 100644 --- a/src/android/i_video.c +++ b/src/android/i_video.c @@ -19,10 +19,10 @@ boolean allow_fullscreen = false; consvar_t cv_vidwait = {"vid_wait", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; void I_StartupGraphics(void){} -void I_StartupHardwareGraphics(void){} - void I_ShutdownGraphics(void){} +void VID_StartupOpenGL(void){} + void I_SetPalette(RGBA_t *palette) { (void)palette; @@ -52,10 +52,8 @@ INT32 VID_SetMode(INT32 modenum) return 0; } -void VID_CheckRenderer(void) -{ - // .............. -} +void VID_CheckRenderer(void) {} +void VID_CheckGLLoaded(rendermode_t oldrender) {} const char *VID_GetModeName(INT32 modenum) { diff --git a/src/djgppdos/i_video.c b/src/djgppdos/i_video.c index 02c7a842b..f525b96ca 100644 --- a/src/djgppdos/i_video.c +++ b/src/djgppdos/i_video.c @@ -339,7 +339,4 @@ void I_StartupGraphics(void) } -void I_StartupHardwareGraphics(void) -{ - // oh yeah woo yeah oh yeah woo yeah oh yeah woo yeah oh yeah woo yeah oh yeah woo yeah oh yeah woo yeah oh yeah woo y -} +void VID_StartupOpenGL(void) {} diff --git a/src/djgppdos/vid_vesa.c b/src/djgppdos/vid_vesa.c index c8ce7dae5..61ed18e4b 100644 --- a/src/djgppdos/vid_vesa.c +++ b/src/djgppdos/vid_vesa.c @@ -378,10 +378,8 @@ INT32 VID_SetMode (INT32 modenum) //, UINT8 *palette) return 1; } -void VID_CheckRenderer(void) -{ - // .............. -} +void VID_CheckRenderer(void) {} +void VID_CheckGLLoaded(rendermode_t oldrender) {} diff --git a/src/dummy/i_video.c b/src/dummy/i_video.c index fafeee000..56ead3672 100644 --- a/src/dummy/i_video.c +++ b/src/dummy/i_video.c @@ -11,10 +11,10 @@ boolean allow_fullscreen = false; consvar_t cv_vidwait = {"vid_wait", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; void I_StartupGraphics(void){} -void I_StartupHardwareGraphics(void){} - void I_ShutdownGraphics(void){} +void VID_StartupOpenGL(void){} + void I_SetPalette(RGBA_t *palette) { (void)palette; @@ -40,10 +40,8 @@ INT32 VID_SetMode(INT32 modenum) return 0; } -void VID_CheckRenderer(void) -{ - // .............. -} +void VID_CheckRenderer(void) {} +void VID_CheckGLLoaded(rendermode_t oldrender) {} const char *VID_GetModeName(INT32 modenum) { diff --git a/src/i_video.h b/src/i_video.h index 294b7ef84..2b40356ea 100644 --- a/src/i_video.h +++ b/src/i_video.h @@ -36,10 +36,10 @@ typedef enum */ extern rendermode_t rendermode; -/** \brief hardware renderer loaded +/** \brief OpenGL state 0 = never loaded, 1 = loaded successfully, -1 = failed loading */ -extern INT32 hwrenderloaded; +extern INT32 vid_opengl_state; /** \brief use highcolor modes if true */ @@ -49,11 +49,7 @@ extern boolean highcolor; */ void I_StartupGraphics(void); -/** \brief setup hardware mode -*/ -void I_StartupHardwareGraphics(void); - -/** \brief restore old video mode +/** \brief shutdown video mode */ void I_ShutdownGraphics(void); @@ -97,6 +93,14 @@ INT32 VID_SetMode(INT32 modenum); */ void VID_CheckRenderer(void); +/** \brief Load OpenGL mode +*/ +void VID_StartupOpenGL(void); + +/** \brief Checks if OpenGL loaded +*/ +void VID_CheckGLLoaded(rendermode_t oldrender); + /** \brief The VID_GetModeName function \param modenum video mode number diff --git a/src/m_menu.c b/src/m_menu.c index 0349ed3bc..82793ab9a 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -2093,7 +2093,7 @@ static void M_VideoOptions(INT32 choice) { (void)choice; #ifdef HWRENDER - if (hwrenderloaded == -1) + if (vid_opengl_state == -1) { OP_VideoOptionsMenu[op_video_renderer].status = (IT_TRANSTEXT | IT_PAIR); OP_VideoOptionsMenu[op_video_renderer].patch = "Renderer"; diff --git a/src/screen.c b/src/screen.c index 9c61f5689..828454a9b 100644 --- a/src/screen.c +++ b/src/screen.c @@ -453,7 +453,7 @@ void SCR_ActuallyChangeRenderer(void) #ifdef HWRENDER // Well, it didn't even load anyway. - if ((hwrenderloaded == -1) && (setrenderneeded == render_opengl)) + if ((vid_opengl_state == -1) && (setrenderneeded == render_opengl)) { if (M_CheckParm("-nogl")) CONS_Alert(CONS_ERROR, "OpenGL rendering was disabled!\n"); @@ -478,7 +478,7 @@ void SCR_ChangeRenderer(void) { target_renderer = cv_renderer.value; #ifdef HWRENDER - if (M_CheckParm("-opengl") && (hwrenderloaded == 1)) + if (M_CheckParm("-opengl") && (vid_opengl_state == 1)) target_renderer = rendermode = render_opengl; else #endif diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index 1dbaf06bd..5d1baee74 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -104,7 +104,7 @@ static consvar_t cv_stretch = {"stretch", "Off", CV_SAVE|CV_NOSHOWHELP, CV_OnOff static consvar_t cv_alwaysgrabmouse = {"alwaysgrabmouse", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; UINT8 graphics_started = 0; // Is used in console.c and screen.c -INT32 hwrenderloaded = 0; +INT32 vid_opengl_state = 0; // To disable fullscreen at startup; is set in VID_PrepareModeList boolean allow_fullscreen = false; @@ -1440,7 +1440,7 @@ static SDL_bool Impl_CreateContext(void) { // Renderer-specific stuff #ifdef HWRENDER - if ((rendermode == render_opengl) && (hwrenderloaded != -1)) + if ((rendermode == render_opengl) && (vid_opengl_state != -1)) { if (!sdlglcontext) sdlglcontext = SDL_GL_CreateContext(window); @@ -1473,10 +1473,10 @@ static SDL_bool Impl_CreateContext(void) return SDL_TRUE; } -#ifdef HWRENDER -static void VID_CheckGLLoaded(rendermode_t oldrender) +void VID_CheckGLLoaded(rendermode_t oldrender) { - if (hwrenderloaded == -1) // Well, it didn't work the first time anyway. +#ifdef HWRENDER + if (vid_opengl_state == -1) // Well, it didn't work the first time anyway. { rendermode = oldrender; if (chosenrendermode == render_opengl) // fallback to software @@ -1488,8 +1488,8 @@ static void VID_CheckGLLoaded(rendermode_t oldrender) setrenderneeded = 0; } } -} #endif +} void VID_CheckRenderer(void) { @@ -1499,11 +1499,6 @@ void VID_CheckRenderer(void) if (dedicated) return; -#ifdef HWRENDER - if (!graphics_started) - VID_CheckGLLoaded(oldrenderer); -#endif - if (setrenderneeded) { rendermode = setrenderneeded; @@ -1514,9 +1509,9 @@ void VID_CheckRenderer(void) { VID_CheckGLLoaded(oldrenderer); // Initialise OpenGL before calling SDLSetMode!!! - if (hwrenderloaded != 1) - I_StartupHardwareGraphics(); - else if (hwrenderloaded == -1) + if (vid_opengl_state != 1) + VID_StartupOpenGL(); + else if (vid_opengl_state == -1) rendererchanged = SDL_FALSE; } #endif @@ -1535,7 +1530,7 @@ void VID_CheckRenderer(void) bufSurface = NULL; } #ifdef HWRENDER - if (hwrenderloaded == 1) // Only if OpenGL ever loaded! + if (vid_opengl_state == 1) // Only if OpenGL ever loaded! HWR_FreeTextureCache(); #endif SCR_SetDrawFuncs(); @@ -1586,7 +1581,7 @@ static SDL_bool Impl_CreateWindow(SDL_bool fullscreen) flags |= SDL_WINDOW_BORDERLESS; #ifdef HWRENDER - if (hwrenderloaded != -1) + if (vid_opengl_state != -1) flags |= SDL_WINDOW_OPENGL; #endif @@ -1722,9 +1717,9 @@ void I_StartupGraphics(void) VID_Command_ModeList_f(); #ifdef HWRENDER if (M_CheckParm("-nogl")) - hwrenderloaded = -1; // Don't call SDL_GL_LoadLibrary + vid_opengl_state = -1; // Don't call SDL_GL_LoadLibrary else - I_StartupHardwareGraphics(); + VID_StartupOpenGL(); #endif // Fury: we do window initialization after GL setup to allow @@ -1779,7 +1774,7 @@ void I_StartupGraphics(void) graphics_started = true; } -void I_StartupHardwareGraphics(void) +void VID_StartupOpenGL(void) { #ifdef HWRENDER static boolean glstartup = false; @@ -1817,12 +1812,12 @@ void I_StartupHardwareGraphics(void) if (HWD.pfnGetRenderVersion() != VERSION) { CONS_Alert(CONS_ERROR, M_GetText("The version of the renderer doesn't match the version of the executable\nBe sure you have installed SRB2 properly.\n")); - hwrenderloaded = -1; + vid_opengl_state = -1; } else - hwrenderloaded = HWD.pfnInit(I_Error) ? 1 : -1; // let load the OpenGL library + vid_opengl_state = HWD.pfnInit(I_Error) ? 1 : -1; // let load the OpenGL library - if (hwrenderloaded == -1) + if (vid_opengl_state == -1) { rendermode = render_soft; setrenderneeded = 0; diff --git a/src/win32/win_vid.c b/src/win32/win_vid.c index d0aab92b3..4e7bab569 100644 --- a/src/win32/win_vid.c +++ b/src/win32/win_vid.c @@ -239,10 +239,7 @@ void I_StartupGraphics(void) if (!dedicated) graphics_started = true; } -void I_StartupHardwareGraphics(void) -{ - // oh yeah woo yeah oh yeah woo yeah oh yeah woo yeah oh yeah woo yeah oh yeah woo yeah oh yeah woo yeah oh yeah woo y -} +void VID_StartupOpenGL(void){} // ------------------ // I_ShutdownGraphics @@ -951,10 +948,8 @@ INT32 VID_SetMode(INT32 modenum) return 1; } -void VID_CheckRenderer(void) -{ - // .............. -} +void VID_CheckRenderer(void) {} +void VID_CheckGLLoaded(rendermode_t oldrender) {} // ======================================================================== // Free the video buffer of the last video mode, From bef6cb532b9d414eff831a40ae0321eb7a79b983 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Mon, 17 Feb 2020 21:58:32 -0300 Subject: [PATCH 24/54] Fix what !785 tried to fix, but correctly --- src/hardware/hw_main.c | 1 - src/sdl/i_video.c | 66 +++++++++++++++++++++++++++++++++--------- 2 files changed, 53 insertions(+), 14 deletions(-) diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index 7e913c4c7..eed1b4644 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -6611,7 +6611,6 @@ void HWR_Shutdown(void) CONS_Printf("HWR_Shutdown()\n"); HWR_FreeExtraSubsectors(); HWR_FreePolyPool(); - HWR_FreeMipmapCache(); HWR_FreeTextureCache(); HWD.pfnFlushScreenTextures(); } diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index 5d1baee74..3c1e01384 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -1493,7 +1493,8 @@ void VID_CheckGLLoaded(rendermode_t oldrender) void VID_CheckRenderer(void) { - SDL_bool rendererchanged = SDL_FALSE; + boolean rendererchanged = false; + boolean contextcreated = false; rendermode_t oldrenderer = rendermode; if (dedicated) @@ -1502,21 +1503,51 @@ void VID_CheckRenderer(void) if (setrenderneeded) { rendermode = setrenderneeded; - rendererchanged = SDL_TRUE; + rendererchanged = true; #ifdef HWRENDER if (rendermode == render_opengl) { VID_CheckGLLoaded(oldrenderer); + // Initialise OpenGL before calling SDLSetMode!!! - if (vid_opengl_state != 1) + // This is because SDLSetMode calls OglSdlSurface. + if (vid_opengl_state == 0) + { VID_StartupOpenGL(); + // Loaded successfully! + if (vid_opengl_state == 1) + { + // Destroy the current window, if it exists. + if (window) + { + SDL_DestroyWindow(window); + window = NULL; + } + + // Destroy the current window rendering context, if that also exists. + if (renderer) + { + SDL_DestroyRenderer(renderer); + renderer = NULL; + } + + // Create a new window. + Impl_CreateWindow(USE_FULLSCREEN); + + // From there, the OpenGL context was already created. + contextcreated = true; + } + } else if (vid_opengl_state == -1) - rendererchanged = SDL_FALSE; + rendererchanged = false; } #endif - Impl_CreateContext(); + if (!contextcreated) + Impl_CreateContext(); + + setrenderneeded = 0; } SDLSetMode(vid.width, vid.height, USE_FULLSCREEN, (rendererchanged ? SDL_FALSE : SDL_TRUE)); @@ -1529,15 +1560,22 @@ void VID_CheckRenderer(void) SDL_FreeSurface(bufSurface); bufSurface = NULL; } + + if (rendererchanged) + { #ifdef HWRENDER - if (vid_opengl_state == 1) // Only if OpenGL ever loaded! - HWR_FreeTextureCache(); + if (vid_opengl_state == 1) // Only if OpenGL ever loaded! + HWR_FreeTextureCache(); #endif - SCR_SetDrawFuncs(); + SCR_SetDrawFuncs(); + } } #ifdef HWRENDER else if (rendermode == render_opengl) - R_InitHardwareMode(); + { + if (rendererchanged) + R_InitHardwareMode(); + } #else (void)oldrenderer; #endif @@ -1581,7 +1619,7 @@ static SDL_bool Impl_CreateWindow(SDL_bool fullscreen) flags |= SDL_WINDOW_BORDERLESS; #ifdef HWRENDER - if (vid_opengl_state != -1) + if (vid_opengl_state == 1) flags |= SDL_WINDOW_OPENGL; #endif @@ -1715,10 +1753,11 @@ void I_StartupGraphics(void) //SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY>>1,SDL_DEFAULT_REPEAT_INTERVAL<<2); VID_Command_ModeList_f(); + #ifdef HWRENDER if (M_CheckParm("-nogl")) - vid_opengl_state = -1; // Don't call SDL_GL_LoadLibrary - else + vid_opengl_state = -1; // Don't startup OpenGL + else if (chosenrendermode == render_opengl) VID_StartupOpenGL(); #endif @@ -1780,6 +1819,7 @@ void VID_StartupOpenGL(void) static boolean glstartup = false; if (!glstartup) { + CONS_Printf("VID_StartupOpenGL()...\n"); HWD.pfnInit = hwSym("Init",NULL); HWD.pfnFinishUpdate = NULL; HWD.pfnDraw2DLine = hwSym("Draw2DLine",NULL); @@ -1811,7 +1851,7 @@ void VID_StartupOpenGL(void) // check gl renderer lib if (HWD.pfnGetRenderVersion() != VERSION) { - CONS_Alert(CONS_ERROR, M_GetText("The version of the renderer doesn't match the version of the executable\nBe sure you have installed SRB2 properly.\n")); + CONS_Alert(CONS_ERROR, M_GetText("The version of the renderer doesn't match the version of the executable!\nBe sure you have installed SRB2 properly.\n")); vid_opengl_state = -1; } else From d3abf5dcfa83b2935294bd49a28d6b1968b81050 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Mon, 17 Feb 2020 22:31:34 -0300 Subject: [PATCH 25/54] Set the palette :) --- src/sdl/i_video.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index 3c1e01384..dc263f457 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -1574,7 +1574,10 @@ void VID_CheckRenderer(void) else if (rendermode == render_opengl) { if (rendererchanged) + { R_InitHardwareMode(); + V_SetPalette(0); + } } #else (void)oldrenderer; From bd7b2fcb936e104bd3f63278cd743797efdbf70b Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Mon, 17 Feb 2020 22:33:36 -0300 Subject: [PATCH 26/54] setrenderneeded is already cleared earlier --- src/d_main.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index d9f67675c..c4b445089 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1300,11 +1300,10 @@ void D_SRB2Main(void) // Set cv_renderer to the new render mode VID_CheckRenderer(); - SCR_ChangeRendererCVars(setrenderneeded); + SCR_ChangeRendererCVars(rendermode); - // check the renderer's state, and then clear setrenderneeded + // check the renderer's state D_CheckRendererState(); - setrenderneeded = 0; } wipegamestate = gamestate; From b1ad7787f44bec6a0000728e68f97ea0cbb46e98 Mon Sep 17 00:00:00 2001 From: Tatsuru <44866610+Ikkarin@users.noreply.github.com> Date: Tue, 18 Feb 2020 22:54:11 -0300 Subject: [PATCH 27/54] Standards 3 --- src/p_enemy.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/p_enemy.c b/src/p_enemy.c index 6b20862ed..b7ccdb01b 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -8961,13 +8961,13 @@ void A_Dye(mobj_t *actor) { INT32 locvar1 = var1; INT32 locvar2 = var2; + + mobj_t *target = ((locvar1 && actor->target) ? actor->target : actor); + UINT8 color = (UINT8)locvar2; #ifdef HAVE_BLUA if (LUA_CallAction("A_Dye", actor)) return; #endif - mobj_t *target = ((locvar1 && actor->target) ? actor->target : actor); - UINT8 color = (UINT8)locvar2; - if (color >= MAXTRANSLATIONS) return; From 2a93ddc2dd01264234f66bd7bb37d89ff340dde9 Mon Sep 17 00:00:00 2001 From: Snu Date: Thu, 20 Feb 2020 21:53:21 +0000 Subject: [PATCH 28/54] Change SKINCOLOR_SUPERGOLD1-5 --- src/r_draw.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/r_draw.c b/src/r_draw.c index 743965cfb..60ff67680 100644 --- a/src/r_draw.c +++ b/src/r_draw.c @@ -229,11 +229,11 @@ const UINT8 Color_Index[MAXTRANSLATIONS-1][16] = { {0x00, 0xd0, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x44, 0x45, 0x46}, // SKINCOLOR_SUPERORANGE4 {0xd0, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x44, 0x45, 0x46, 0x47}, // SKINCOLOR_SUPERORANGE5 - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x50, 0x51, 0x52, 0x53, 0x48}, // SKINCOLOR_SUPERGOLD1 - {0x00, 0x50, 0x51, 0x52, 0x53, 0x53, 0x48, 0x48, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x40, 0x41}, // SKINCOLOR_SUPERGOLD2 - {0x51, 0x52, 0x53, 0x53, 0x48, 0x48, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x40, 0x41, 0x42, 0x43}, // SKINCOLOR_SUPERGOLD3 - {0x53, 0x48, 0x48, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46}, // SKINCOLOR_SUPERGOLD4 - {0x48, 0x48, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47}, // SKINCOLOR_SUPERGOLD5 + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x50, 0x51, 0x52, 0x53, 0x48, 0x48, 0x48}, // SKINCOLOR_SUPERGOLD1 + {0x00, 0x50, 0x51, 0x52, 0x53, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x40, 0x41, 0x41, 0x41}, // SKINCOLOR_SUPERGOLD2 + {0x51, 0x52, 0x53, 0x53, 0x48, 0x49, 0x49, 0x49, 0x49, 0x49, 0x40, 0x41, 0x42, 0x43, 0x43, 0x43}, // SKINCOLOR_SUPERGOLD3 + {0x53, 0x48, 0x48, 0x49, 0x49, 0x49, 0x49, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x46, 0x46}, // SKINCOLOR_SUPERGOLD4 + {0x48, 0x48, 0x49, 0x49, 0x49, 0x40, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x47, 0x47}, // SKINCOLOR_SUPERGOLD5 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x58, 0x58, 0xbc, 0xbc, 0xbc}, // SKINCOLOR_SUPERPERIDOT1 {0x00, 0x58, 0x58, 0x58, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe}, // SKINCOLOR_SUPERPERIDOT2 From 0df094021e35e4304e0e7b544cab1c2f5870e416 Mon Sep 17 00:00:00 2001 From: sphere Date: Mon, 24 Feb 2020 17:31:30 +0100 Subject: [PATCH 29/54] Allow emblem hints (and radar) in record attack. --- src/m_menu.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/m_menu.c b/src/m_menu.c index 403d3a036..622d740e2 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -521,6 +521,8 @@ static menuitem_t MISC_AddonsMenu[] = // --------------------------------- static menuitem_t MAPauseMenu[] = { + {IT_CALL | IT_STRING, NULL, "Emblem Hints...", M_EmblemHints, 32}, + {IT_CALL | IT_STRING, NULL, "Continue", M_SelectableClearMenus,48}, {IT_CALL | IT_STRING, NULL, "Retry", M_ModeAttackRetry, 56}, {IT_CALL | IT_STRING, NULL, "Abort", M_ModeAttackEndGame, 64}, @@ -528,6 +530,7 @@ static menuitem_t MAPauseMenu[] = typedef enum { + mapause_hints, mapause_continue, mapause_retry, mapause_abort @@ -729,9 +732,9 @@ static menuitem_t SR_SoundTestMenu[] = static menuitem_t SR_EmblemHintMenu[] = { - {IT_STRING | IT_ARROWS, NULL, "Page", M_HandleEmblemHints, 10}, - {IT_STRING|IT_CVAR, NULL, "Emblem Radar", &cv_itemfinder, 20}, - {IT_WHITESTRING|IT_SUBMENU, NULL, "Back", &SPauseDef, 30} + {IT_STRING | IT_ARROWS, NULL, "Page", M_HandleEmblemHints, 10}, + {IT_STRING|IT_CVAR, NULL, "Emblem Radar", &cv_itemfinder, 20}, + {IT_WHITESTRING|IT_CALL, NULL, "Back", M_GoBack, 30} }; // -------------------------------- @@ -7266,6 +7269,7 @@ static void M_EmblemHints(INT32 choice) SR_EmblemHintMenu[0].status = (local > NUMHINTS*2) ? (IT_STRING | IT_ARROWS) : (IT_DISABLED); SR_EmblemHintMenu[1].status = (M_SecretUnlocked(SECRET_ITEMFINDER)) ? (IT_CVAR|IT_STRING) : (IT_SECRET); hintpage = 1; + SR_EmblemHintDef.prevMenu = currentMenu; M_SetupNextMenu(&SR_EmblemHintDef); itemOn = 2; // always start on back. } From 776951c6a897ac0a55e952aeb2963bea79504df3 Mon Sep 17 00:00:00 2001 From: Tatsuru <44866610+Ikkarin@users.noreply.github.com> Date: Tue, 3 Mar 2020 11:40:35 -0300 Subject: [PATCH 30/54] Never forget who your enemy is --- src/p_mobj.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index faee245d3..31abeb780 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -9393,8 +9393,11 @@ static boolean P_MobjRegularThink(mobj_t *mobj) if (mobj->tracer && mobj->tracer->player && mobj->tracer->health > 0 && P_AproxDistance(P_AproxDistance(mobj->tracer->x - mobj->x, mobj->tracer->y - mobj->y), mobj->tracer->z - mobj->z) <= mobj->radius*16) { + var1 = mobj->info->speed; + var2 = 1; + // Home in on the target. - P_HomingAttack(mobj, mobj->tracer); + A_HomingChase(mobj); if (mobj->z < mobj->floorz) mobj->z = mobj->floorz; From 761ec3b6a4a2389d985ae8263fb65a3eb9a82fd7 Mon Sep 17 00:00:00 2001 From: Tatsuru <44866610+Ikkarin@users.noreply.github.com> Date: Sun, 1 Mar 2020 21:31:44 -0300 Subject: [PATCH 31/54] Separate PvP damage from non-friendly gametypes --- 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 a24f9bf03..8b9f74973 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -3156,7 +3156,7 @@ static boolean P_PlayerHitsPlayer(mobj_t *target, mobj_t *inflictor, mobj_t *sou return false; // In COOP/RACE, you can't hurt other players unless cv_friendlyfire is on - if (!(cv_friendlyfire.value || (gametyperules & GTR_FRIENDLYFIRE)) && (G_PlatformGametype())) + if (!(cv_friendlyfire.value || (gametyperules & GTR_FRIENDLYFIRE)) && (gametyperules & GTR_FRIENDLY)) { if (gametype == GT_COOP && inflictor->type == MT_LHRT && !(player->powers[pw_shield] & SH_NOSTACK)) // co-op only { From 0d92bf3dd09cd4712a63fc79cc7520ed18556bf6 Mon Sep 17 00:00:00 2001 From: GoldenTails Date: Sat, 7 Mar 2020 21:41:52 -0600 Subject: [PATCH 32/54] Florida man fixes sectors with light level 256 blacking out OpenGL objects; mappers riot. Disclaimer: I'm not actually from Florida. --- src/hardware/hw_main.c | 17 ++++++++++------- src/hardware/hw_md2.c | 4 ++-- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index efed9b602..5a5db90e8 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -4186,7 +4186,10 @@ static void HWR_DrawDropShadow(mobj_t *thing, gr_vissprite_t *spr, fixed_t scale { light = R_GetPlaneLight(thing->subsector->sector, floorz, false); // Always use the light at the top instead of whatever I was doing before - lightlevel = *thing->subsector->sector->lightlist[light].lightlevel; + if (*thing->subsector->sector->lightlist[light].lightlevel > 255) + lightlevel = 255; + else + lightlevel = *thing->subsector->sector->lightlist[light].lightlevel; if (*thing->subsector->sector->lightlist[light].extra_colormap) colormap = *thing->subsector->sector->lightlist[light].extra_colormap; @@ -4392,7 +4395,7 @@ static void HWR_SplitSprite(gr_vissprite_t *spr) if (h <= temp) { if (!(spr->mobj->frame & FF_FULLBRIGHT)) - lightlevel = *list[i-1].lightlevel; + lightlevel = *list[i-1].lightlevel > 255 ? 255 : *list[i-1].lightlevel; colormap = *list[i-1].extra_colormap; break; } @@ -4400,7 +4403,7 @@ static void HWR_SplitSprite(gr_vissprite_t *spr) #else i = R_GetPlaneLight(sector, temp, false); if (!(spr->mobj->frame & FF_FULLBRIGHT)) - lightlevel = *list[i].lightlevel; + lightlevel = *list[i].lightlevel > 255 ? 255 : *list[i].lightlevel; colormap = *list[i].extra_colormap; #endif @@ -4416,7 +4419,7 @@ static void HWR_SplitSprite(gr_vissprite_t *spr) if (!(list[i].flags & FF_NOSHADE) && (list[i].flags & FF_CUTSPRITES)) { if (!(spr->mobj->frame & FF_FULLBRIGHT)) - lightlevel = *list[i].lightlevel; + lightlevel = *list[i].lightlevel > 255 ? 255 : *list[i].lightlevel; colormap = *list[i].extra_colormap; } @@ -4693,7 +4696,7 @@ static void HWR_DrawSprite(gr_vissprite_t *spr) extracolormap_t *colormap = sector->extra_colormap; if (!(spr->mobj->frame & FF_FULLBRIGHT)) - lightlevel = sector->lightlevel; + lightlevel = sector->lightlevel > 255 ? 255 : sector->lightlevel; if (colormap) Surf.FlatColor.rgba = HWR_Lighting(lightlevel, colormap->rgba, colormap->fadergba, false, false); @@ -4790,7 +4793,7 @@ static inline void HWR_DrawPrecipitationSprite(gr_vissprite_t *spr) light = R_GetPlaneLight(sector, spr->mobj->z + spr->mobj->height, false); // Always use the light at the top instead of whatever I was doing before if (!(spr->mobj->frame & FF_FULLBRIGHT)) - lightlevel = *sector->lightlist[light].lightlevel; + lightlevel = *sector->lightlist[light].lightlevel > 255 ? 255 : *sector->lightlist[light].lightlevel; if (*sector->lightlist[light].extra_colormap) colormap = *sector->lightlist[light].extra_colormap; @@ -4798,7 +4801,7 @@ static inline void HWR_DrawPrecipitationSprite(gr_vissprite_t *spr) else { if (!(spr->mobj->frame & FF_FULLBRIGHT)) - lightlevel = sector->lightlevel; + lightlevel = sector->lightlevel > 255 ? 255 : sector->lightlevel; if (sector->extra_colormap) colormap = sector->extra_colormap; diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c index 5c3cd40a6..2e3af4a4c 100644 --- a/src/hardware/hw_md2.c +++ b/src/hardware/hw_md2.c @@ -1221,7 +1221,7 @@ boolean HWR_DrawModel(gr_vissprite_t *spr) light = R_GetPlaneLight(sector, spr->mobj->z + spr->mobj->height, false); // Always use the light at the top instead of whatever I was doing before if (!(spr->mobj->frame & FF_FULLBRIGHT)) - lightlevel = *sector->lightlist[light].lightlevel; + lightlevel = *sector->lightlist[light].lightlevel > 255 ? 255 : *sector->lightlist[light].lightlevel; if (*sector->lightlist[light].extra_colormap) colormap = *sector->lightlist[light].extra_colormap; @@ -1229,7 +1229,7 @@ boolean HWR_DrawModel(gr_vissprite_t *spr) else { if (!(spr->mobj->frame & FF_FULLBRIGHT)) - lightlevel = sector->lightlevel; + lightlevel = sector->lightlevel > 255 ? 255 : sector->lightlevel; if (sector->extra_colormap) colormap = sector->extra_colormap; From cdba9ba03371adf3609010aa8896200018570cc7 Mon Sep 17 00:00:00 2001 From: GoldenTails Date: Sat, 7 Mar 2020 21:48:40 -0600 Subject: [PATCH 33/54] Fix spaces before someone yells at me for it --- src/hardware/hw_main.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index 5a5db90e8..bb3559696 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -4186,10 +4186,10 @@ static void HWR_DrawDropShadow(mobj_t *thing, gr_vissprite_t *spr, fixed_t scale { light = R_GetPlaneLight(thing->subsector->sector, floorz, false); // Always use the light at the top instead of whatever I was doing before - if (*thing->subsector->sector->lightlist[light].lightlevel > 255) - lightlevel = 255; - else - lightlevel = *thing->subsector->sector->lightlist[light].lightlevel; + if (*thing->subsector->sector->lightlist[light].lightlevel > 255) + lightlevel = 255; + else + lightlevel = *thing->subsector->sector->lightlist[light].lightlevel; if (*thing->subsector->sector->lightlist[light].extra_colormap) colormap = *thing->subsector->sector->lightlist[light].extra_colormap; From be338da3f695f94cce07a9aa4ae2e0e85701d8af Mon Sep 17 00:00:00 2001 From: Tatsuru <44866610+Ikkarin@users.noreply.github.com> Date: Sun, 8 Mar 2020 14:06:18 -0300 Subject: [PATCH 34/54] I guess --- src/p_user.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_user.c b/src/p_user.c index 9167d5345..1715224ae 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -9422,7 +9422,7 @@ boolean P_HomingAttack(mobj_t *source, mobj_t *enemy) // Home in on your target if (enemy->health <= 0) // dead return false; - if (!((enemy->flags & (MF_ENEMY|MF_BOSS|MF_MONITOR) && (enemy->flags & MF_SHOOTABLE)) || (enemy->flags & MF_SPRING)) == !(enemy->flags2 & MF2_INVERTAIMABLE)) // allows if it has the flags desired XOR it has the invert aimable flag + if (source->player && (!((enemy->flags & (MF_ENEMY|MF_BOSS|MF_MONITOR) && (enemy->flags & MF_SHOOTABLE)) || (enemy->flags & MF_SPRING)) == !(enemy->flags2 & MF2_INVERTAIMABLE))) // allows if it has the flags desired XOR it has the invert aimable flag return false; if (enemy->flags2 & MF2_FRET) From bdab78152cb5288cdce2303cb6f65b99b6c22129 Mon Sep 17 00:00:00 2001 From: sphere Date: Fri, 13 Mar 2020 10:46:57 -0400 Subject: [PATCH 35/54] Check if emblem hints are actually unlocked. --- src/m_menu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/m_menu.c b/src/m_menu.c index 622d740e2..18f1f6c8e 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -3612,6 +3612,7 @@ void M_StartControlPanel(void) else if (modeattacking) { currentMenu = &MAPauseDef; + MAPauseMenu[mapause_hints].status = (M_SecretUnlocked(SECRET_EMBLEMHINTS)) ? (IT_STRING | IT_CALL) : (IT_DISABLED); itemOn = mapause_continue; } else if (!(netgame || multiplayer)) // Single Player From c2cd9a71fad89d9b57759e6567437be8958625c1 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Tue, 17 Mar 2020 13:05:54 -0300 Subject: [PATCH 36/54] Fix con_backpic --- src/console.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/console.c b/src/console.c index f8fa1314a..e39c2f531 100644 --- a/src/console.c +++ b/src/console.c @@ -1553,13 +1553,28 @@ static void CON_DrawConsole(void) if (cons_backpic.value || con_forcepic) { patch_t *con_backpic = W_CachePatchName("CONSBACK", PU_PATCH); - int h; + int x, w, h; + w = (con_backpic->width * vid.dupx); + x = (vid.width / 2) - (w / 2); h = con_curlines/vid.dupy; - // Jimita: CON_DrawBackpic just called V_DrawScaledPatch - //V_DrawScaledPatch(0, 0, 0, con_backpic); - V_DrawCroppedPatch(0, 0, FRACUNIT, 0, con_backpic, + // Lactozilla: If the patch doesn't fill the entire screen, + // then fill the sides with a solid color. + if (x > 0) + { + column_t *column = (column_t *)((UINT8 *)(con_backpic) + LONG(con_backpic->columnofs[0])); + if (!column->topdelta) + { + UINT8 *source = (UINT8 *)(column) + 3; + INT32 color = (source[0] | V_NOSCALESTART); + // left side + V_DrawFill(0, 0, x, con_curlines, color); + // right side + V_DrawFill((x + w), 0, (vid.width - w), con_curlines, color); + } + } + V_DrawCroppedPatch(x << FRACBITS, 0, FRACUNIT, V_NOSCALESTART, con_backpic, 0, ( BASEVIDHEIGHT - h ), BASEVIDWIDTH, h); W_UnlockCachedPatch(con_backpic); From 1904e3b5e9e1491d59cfac9df2aac9f9196c3a40 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Tue, 17 Mar 2020 15:23:13 -0300 Subject: [PATCH 37/54] Fix OpenGL --- src/console.c | 75 +++++++++++++++++++++++-------------- src/hardware/hw_draw.c | 4 +- src/w_wad.c | 85 ++++++++++++++++++++++++++---------------- src/w_wad.h | 11 +++++- 4 files changed, 110 insertions(+), 65 deletions(-) diff --git a/src/console.c b/src/console.c index e39c2f531..0f1ccbd33 100644 --- a/src/console.c +++ b/src/console.c @@ -97,6 +97,7 @@ static void CON_InputInit(void); static void CON_RecalcSize(void); static void CON_ChangeHeight(void); +static void CON_DrawBackpic(void); static void CONS_hudlines_Change(void); static void CONS_backcolor_Change(void); @@ -1530,6 +1531,51 @@ static void CON_DrawHudlines(void) con_clearlines = y; // this is handled by HU_Erase(); } +// Lactozilla: Draws the console's background picture. +static void CON_DrawBackpic(void) +{ + patch_t *con_backpic; + lumpnum_t piclump; + int x, w, h; + + // Get the lumpnum for CONSBACK, or fallback into MISSING. + piclump = W_CheckNumForName("CONSBACK"); + if (piclump == LUMPERROR) + piclump = W_GetNumForName("MISSING"); + + // Cache the Software patch. + con_backpic = W_CacheSoftwarePatchNum(piclump, PU_PATCH); + + // Center the backpic, and draw a vertically cropped patch. + w = (con_backpic->width * vid.dupx); + x = (vid.width / 2) - (w / 2); + h = con_curlines/vid.dupy; + + // If the patch doesn't fill the entire screen, + // then fill the sides with a solid color. + if (x > 0) + { + column_t *column = (column_t *)((UINT8 *)(con_backpic) + LONG(con_backpic->columnofs[0])); + if (!column->topdelta) + { + UINT8 *source = (UINT8 *)(column) + 3; + INT32 color = (source[0] | V_NOSCALESTART); + // left side + V_DrawFill(0, 0, x, con_curlines, color); + // right side + V_DrawFill((x + w), 0, (vid.width - w), con_curlines, color); + } + } + + // Cache the patch normally. + con_backpic = W_CachePatchNum(piclump, PU_PATCH); + V_DrawCroppedPatch(x << FRACBITS, 0, FRACUNIT, V_NOSCALESTART, con_backpic, + 0, ( BASEVIDHEIGHT - h ), BASEVIDWIDTH, h); + + // Unlock the cached patch. + W_UnlockCachedPatch(con_backpic); +} + // draw the console background, text, and prompt if enough place // static void CON_DrawConsole(void) @@ -1551,34 +1597,7 @@ static void CON_DrawConsole(void) // draw console background if (cons_backpic.value || con_forcepic) - { - patch_t *con_backpic = W_CachePatchName("CONSBACK", PU_PATCH); - int x, w, h; - - w = (con_backpic->width * vid.dupx); - x = (vid.width / 2) - (w / 2); - h = con_curlines/vid.dupy; - - // Lactozilla: If the patch doesn't fill the entire screen, - // then fill the sides with a solid color. - if (x > 0) - { - column_t *column = (column_t *)((UINT8 *)(con_backpic) + LONG(con_backpic->columnofs[0])); - if (!column->topdelta) - { - UINT8 *source = (UINT8 *)(column) + 3; - INT32 color = (source[0] | V_NOSCALESTART); - // left side - V_DrawFill(0, 0, x, con_curlines, color); - // right side - V_DrawFill((x + w), 0, (vid.width - w), con_curlines, color); - } - } - V_DrawCroppedPatch(x << FRACBITS, 0, FRACUNIT, V_NOSCALESTART, con_backpic, - 0, ( BASEVIDHEIGHT - h ), BASEVIDWIDTH, h); - - W_UnlockCachedPatch(con_backpic); - } + CON_DrawBackpic(); else { // inu: no more width (was always 0 and vid.width) diff --git a/src/hardware/hw_draw.c b/src/hardware/hw_draw.c index 598a635aa..d01331765 100644 --- a/src/hardware/hw_draw.c +++ b/src/hardware/hw_draw.c @@ -291,7 +291,7 @@ void HWR_DrawStretchyFixedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t if (cx >= -0.1f && cx <= 0.1f && SHORT(gpatch->width) == BASEVIDWIDTH && cy >= -0.1f && cy <= 0.1f && SHORT(gpatch->height) == BASEVIDHEIGHT) { // Need to temporarily cache the real patch to get the colour of the top left pixel - patch_t *realpatch = W_CacheLumpNumPwad(gpatch->wadnum, gpatch->lumpnum, PU_STATIC); + patch_t *realpatch = W_CacheSoftwarePatchNumPwad(gpatch->wadnum, gpatch->lumpnum, PU_STATIC); const column_t *column = (const column_t *)((const UINT8 *)(realpatch) + LONG((realpatch)->columnofs[0])); if (!column->topdelta) { @@ -450,7 +450,7 @@ void HWR_DrawCroppedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscal if (cx >= -0.1f && cx <= 0.1f && SHORT(gpatch->width) == BASEVIDWIDTH && cy >= -0.1f && cy <= 0.1f && SHORT(gpatch->height) == BASEVIDHEIGHT) { // Need to temporarily cache the real patch to get the colour of the top left pixel - patch_t *realpatch = W_CacheLumpNumPwad(gpatch->wadnum, gpatch->lumpnum, PU_STATIC); + patch_t *realpatch = W_CacheSoftwarePatchNumPwad(gpatch->wadnum, gpatch->lumpnum, PU_STATIC); const column_t *column = (const column_t *)((const UINT8 *)(realpatch) + LONG((realpatch)->columnofs[0])); if (!column->topdelta) { diff --git a/src/w_wad.c b/src/w_wad.c index e96afd050..797f286d5 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -1511,6 +1511,57 @@ void *W_CacheLumpName(const char *name, INT32 tag) // Cache a patch into heap memory, convert the patch format as necessary // +void *W_CacheSoftwarePatchNumPwad(UINT16 wad, UINT16 lump, INT32 tag) +{ + lumpcache_t *lumpcache = NULL; + + if (needpatchflush) + W_FlushCachedPatches(); + + if (!TestValidLump(wad, lump)) + return NULL; + + lumpcache = wadfiles[wad]->patchcache; + + if (!lumpcache[lump]) + { + size_t len = W_LumpLengthPwad(wad, lump); + void *ptr, *lumpdata; +#ifndef NO_PNG_LUMPS + void *srcdata = NULL; +#endif + + ptr = Z_Malloc(len, tag, &lumpcache[lump]); + lumpdata = Z_Malloc(len, tag, NULL); + + // read the lump in full + W_ReadLumpHeaderPwad(wad, lump, lumpdata, 0, 0); + +#ifndef NO_PNG_LUMPS + // lump is a png so convert it + if (R_IsLumpPNG((UINT8 *)lumpdata, len)) + { + size_t newlen; + srcdata = R_PNGToPatch((UINT8 *)lumpdata, len, &newlen); + ptr = Z_Realloc(ptr, newlen, tag, &lumpcache[lump]); + M_Memcpy(ptr, srcdata, newlen); + Z_Free(srcdata); + } + else // just copy it into the patch cache +#endif + M_Memcpy(ptr, lumpdata, len); + } + else + Z_ChangeTag(lumpcache[lump], tag); + + return lumpcache[lump]; +} + +void *W_CacheSoftwarePatchNum(lumpnum_t lumpnum, INT32 tag) +{ + return W_CacheSoftwarePatchNumPwad(WADFILENUM(lumpnum),LUMPNUM(lumpnum),tag); +} + void *W_CachePatchNumPwad(UINT16 wad, UINT16 lump, INT32 tag) { #ifdef HWRENDER @@ -1528,39 +1579,7 @@ void *W_CachePatchNumPwad(UINT16 wad, UINT16 lump, INT32 tag) if (rendermode == render_soft || rendermode == render_none) #endif { - lumpcache_t *lumpcache = wadfiles[wad]->patchcache; - if (!lumpcache[lump]) - { - size_t len = W_LumpLengthPwad(wad, lump); - void *ptr, *lumpdata; -#ifndef NO_PNG_LUMPS - void *srcdata = NULL; -#endif - - ptr = Z_Malloc(len, tag, &lumpcache[lump]); - lumpdata = Z_Malloc(len, tag, NULL); - - // read the lump in full - W_ReadLumpHeaderPwad(wad, lump, lumpdata, 0, 0); - -#ifndef NO_PNG_LUMPS - // lump is a png so convert it - if (R_IsLumpPNG((UINT8 *)lumpdata, len)) - { - size_t newlen; - srcdata = R_PNGToPatch((UINT8 *)lumpdata, len, &newlen); - ptr = Z_Realloc(ptr, newlen, tag, &lumpcache[lump]); - M_Memcpy(ptr, srcdata, newlen); - Z_Free(srcdata); - } - else // just copy it into the patch cache -#endif - M_Memcpy(ptr, lumpdata, len); - } - else - Z_ChangeTag(lumpcache[lump], tag); - - return lumpcache[lump]; + return W_CacheSoftwarePatchNumPwad(wad, lump, tag); } #ifdef HWRENDER diff --git a/src/w_wad.h b/src/w_wad.h index d598d9b39..3fb0cb77e 100644 --- a/src/w_wad.h +++ b/src/w_wad.h @@ -191,8 +191,15 @@ boolean W_IsPatchCached(lumpnum_t lump, void *ptr); void *W_CacheLumpName(const char *name, INT32 tag); void *W_CachePatchName(const char *name, INT32 tag); -void *W_CachePatchNumPwad(UINT16 wad, UINT16 lump, INT32 tag); // return a patch_t -void *W_CachePatchNum(lumpnum_t lumpnum, INT32 tag); // return a patch_t +// Returns either a Software patch, or an OpenGL patch. +// Performs any necessary conversions from PNG images. +void *W_CachePatchNumPwad(UINT16 wad, UINT16 lump, INT32 tag); +void *W_CachePatchNum(lumpnum_t lumpnum, INT32 tag); + +// Returns a Software patch. +// Performs any necessary conversions from PNG images. +void *W_CacheSoftwarePatchNumPwad(UINT16 wad, UINT16 lump, INT32 tag); +void *W_CacheSoftwarePatchNum(lumpnum_t lumpnum, INT32 tag); void W_UnlockCachedPatch(void *patch); void W_FlushCachedPatches(void); From 58c42eec292d6a38748d9a720ddb5e682344f628 Mon Sep 17 00:00:00 2001 From: Louis-Antoine Date: Thu, 19 Mar 2020 14:38:42 +0100 Subject: [PATCH 38/54] Simplify hook code --- src/lua_hooklib.c | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c index 20a7738e7..078732541 100644 --- a/src/lua_hooklib.c +++ b/src/lua_hooklib.c @@ -155,25 +155,13 @@ static int lib_addHook(lua_State *L) hook.s.skinname = NULL; if (lua_isstring(L, 2)) { // lowercase copy - const char *s = lua_tostring(L, 2); - char *p = hook.s.skinname = ZZ_Alloc(strlen(s)+1); - do { - *p = tolower(*s); - ++p; - } while(*(++s)); - *p = 0; + hook.s.skinname = Z_StrDup(lua_tostring(L, 2)); + strlwr(hook.s.skinname); } break; case hook_LinedefExecute: // Linedef executor functions - { // uppercase copy - const char *s = luaL_checkstring(L, 2); - char *p = hook.s.funcname = ZZ_Alloc(strlen(s)+1); - do { - *p = toupper(*s); - ++p; - } while(*(++s)); - *p = 0; - } + hook.s.skinname = Z_StrDup(luaL_checkstring(L, 2)); + strupr(hook.s.skinname); break; default: break; From 1d5d6ead651174a10f554e5a27ae0caa6511e2c6 Mon Sep 17 00:00:00 2001 From: Louis-Antoine Date: Thu, 19 Mar 2020 14:40:12 +0100 Subject: [PATCH 39/54] Fix misleading naming --- src/lua_hooklib.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c index 078732541..549d1a3ef 100644 --- a/src/lua_hooklib.c +++ b/src/lua_hooklib.c @@ -81,9 +81,7 @@ struct hook_s UINT16 id; union { mobjtype_t mt; - char *skinname; - char *musname; - char *funcname; + char *str; } s; boolean error; }; @@ -152,16 +150,16 @@ static int lib_addHook(lua_State *L) break; case hook_BotAI: case hook_ShouldJingleContinue: - hook.s.skinname = NULL; + hook.s.str = NULL; if (lua_isstring(L, 2)) { // lowercase copy - hook.s.skinname = Z_StrDup(lua_tostring(L, 2)); - strlwr(hook.s.skinname); + hook.s.str = Z_StrDup(lua_tostring(L, 2)); + strlwr(hook.s.str); } break; case hook_LinedefExecute: // Linedef executor functions - hook.s.skinname = Z_StrDup(luaL_checkstring(L, 2)); - strupr(hook.s.skinname); + hook.s.str = Z_StrDup(luaL_checkstring(L, 2)); + strupr(hook.s.str); break; default: break; @@ -1064,7 +1062,7 @@ boolean LUAh_BotAI(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd) for (hookp = roothook; hookp; hookp = hookp->next) { if (hookp->type != hook_BotAI - || (hookp->s.skinname && strcmp(hookp->s.skinname, ((skin_t*)tails->skin)->name))) + || (hookp->s.str && strcmp(hookp->s.str, ((skin_t*)tails->skin)->name))) continue; if (lua_gettop(gL) == 0) @@ -1126,7 +1124,7 @@ boolean LUAh_LinedefExecute(line_t *line, mobj_t *mo, sector_t *sector) for (hookp = linedefexecutorhooks; hookp; hookp = hookp->next) { - if (strcmp(hookp->s.funcname, line->text)) + if (strcmp(hookp->s.str, line->text)) continue; if (lua_gettop(gL) == 0) @@ -1664,7 +1662,7 @@ boolean LUAh_ShouldJingleContinue(player_t *player, const char *musname) for (hookp = roothook; hookp; hookp = hookp->next) { if (hookp->type != hook_ShouldJingleContinue - || (hookp->s.musname && strcmp(hookp->s.musname, musname))) + || (hookp->s.str && strcmp(hookp->s.str, musname))) continue; if (lua_gettop(gL) == 0) From c1b50397ce665bd082fb45e1f8b24e85963d2ead Mon Sep 17 00:00:00 2001 From: Louis-Antoine Date: Thu, 19 Mar 2020 14:40:35 +0100 Subject: [PATCH 40/54] Fix compiler warning --- src/p_user.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/p_user.c b/src/p_user.c index 345da85ef..15a75b3a5 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -1550,6 +1550,10 @@ boolean P_EvaluateMusicStatus(UINT16 status, const char *musname) int i; boolean result = false; +#ifndef HAVE_BLUA + (void)musname; +#endif + for (i = 0; i < MAXPLAYERS; i++) { if (!P_IsLocalPlayer(&players[i])) From dcb4ce8cb99380ff5c474681ceb2de8f8ba8083e Mon Sep 17 00:00:00 2001 From: Monster Iestyn Date: Thu, 19 Mar 2020 20:09:55 +0000 Subject: [PATCH 41/54] Created g_demo.c/.h, for the demo recording and playback code that formerly lived in g_game.c --- src/g_demo.c | 2501 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/g_demo.h | 85 ++ src/g_game.c | 2500 +------------------------------------------------ src/g_game.h | 65 +- 4 files changed, 2607 insertions(+), 2544 deletions(-) create mode 100644 src/g_demo.c create mode 100644 src/g_demo.h diff --git a/src/g_demo.c b/src/g_demo.c new file mode 100644 index 000000000..22cd34ad5 --- /dev/null +++ b/src/g_demo.c @@ -0,0 +1,2501 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1993-1996 by id Software, Inc. +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1999-2020 by Sonic Team Junior. +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file g_demo.c +/// \brief Demo recording and playback + +#include "doomdef.h" +#include "console.h" +#include "d_main.h" +#include "d_player.h" +#include "d_clisrv.h" +//#include "f_finale.h" +#include "p_setup.h" +#include "i_system.h" +#include "m_random.h" +#include "p_local.h" +#include "r_draw.h" +#include "r_main.h" +//#include "s_sound.h" +#include "g_game.h" +#include "g_demo.h" +#include "m_cheat.h" +#include "m_misc.h" +#include "m_menu.h" +#include "m_argv.h" +#include "hu_stuff.h" +#include "z_zone.h" +#include "i_video.h" +#include "byteptr.h" +#include "i_joy.h" +#include "r_local.h" +#include "r_skins.h" +#include "y_inter.h" +#include "v_video.h" +#include "lua_hook.h" +#include "md5.h" // demo checksums + +boolean timingdemo; // if true, exit with report on completion +boolean nodrawers; // for comparative timing purposes +boolean noblit; // for comparative timing purposes +tic_t demostarttime; // for comparative timing purposes + +static char demoname[64]; +boolean demorecording; +boolean demoplayback; +boolean titledemo; // Title Screen demo can be cancelled by any key +static UINT8 *demobuffer = NULL; +static UINT8 *demo_p, *demotime_p; +static UINT8 *demoend; +static UINT8 demoflags; +static UINT16 demoversion; +boolean singledemo; // quit after playing a demo from cmdline +boolean demo_start; // don't start playing demo right away +boolean demosynced = true; // console warning message + +boolean metalrecording; // recording as metal sonic +mobj_t *metalplayback; +static UINT8 *metalbuffer = NULL; +static UINT8 *metal_p; +static UINT16 metalversion; + +// extra data stuff (events registered this frame while recording) +static struct { + UINT8 flags; // EZT flags + + // EZT_COLOR + UINT8 color, lastcolor; + + // EZT_SCALE + fixed_t scale, lastscale; + + // EZT_HIT + UINT16 hits; + mobj_t **hitlist; +} ghostext; + +// Your naming conventions are stupid and useless. +// There is no conflict here. +typedef struct demoghost { + UINT8 checksum[16]; + UINT8 *buffer, *p, color, fadein; + UINT16 version; + mobj_t oldmo, *mo; + struct demoghost *next; +} demoghost; +demoghost *ghosts = NULL; + +// +// DEMO RECORDING +// + +#define DEMOVERSION 0x000c +#define DEMOHEADER "\xF0" "SRB2Replay" "\x0F" + +#define DF_GHOST 0x01 // This demo contains ghost data too! +#define DF_RECORDATTACK 0x02 // This demo is from record attack and contains its final completion time, score, and rings! +#define DF_NIGHTSATTACK 0x04 // This demo is from NiGHTS attack and contains its time left, score, and mares! +#define DF_ATTACKMASK 0x06 // This demo is from ??? attack and contains ??? +#define DF_ATTACKSHIFT 1 + +// For demos +#define ZT_FWD 0x01 +#define ZT_SIDE 0x02 +#define ZT_ANGLE 0x04 +#define ZT_BUTTONS 0x08 +#define ZT_AIMING 0x10 +#define DEMOMARKER 0x80 // demoend +#define METALDEATH 0x44 +#define METALSNICE 0x69 + +static ticcmd_t oldcmd; + +// For Metal Sonic and time attack ghosts +#define GZT_XYZ 0x01 +#define GZT_MOMXY 0x02 +#define GZT_MOMZ 0x04 +#define GZT_ANGLE 0x08 +#define GZT_FRAME 0x10 // Animation frame +#define GZT_SPR2 0x20 // Player animations +#define GZT_EXTRA 0x40 +#define GZT_FOLLOW 0x80 // Followmobj + +// GZT_EXTRA flags +#define EZT_THOK 0x01 // Spawned a thok object +#define EZT_SPIN 0x02 // Because one type of thok object apparently wasn't enough +#define EZT_REV 0x03 // And two types wasn't enough either yet +#define EZT_THOKMASK 0x03 +#define EZT_COLOR 0x04 // Changed color (Super transformation, Mario fireflowers/invulnerability, etc.) +#define EZT_FLIP 0x08 // Reversed gravity +#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) +#define EZT_HEIGHT 0x80 // Changed height + +// GZT_FOLLOW flags +#define FZT_SPAWNED 0x01 // just been spawned +#define FZT_SKIN 0x02 // has skin +#define FZT_LINKDRAW 0x04 // has linkdraw (combine with spawned only) +#define FZT_COLORIZED 0x08 // colorized (ditto) +#define FZT_SCALE 0x10 // different scale to object +// spare FZT slots 0x20 to 0x80 + +static mobj_t oldmetal, oldghost; + +void G_SaveMetal(UINT8 **buffer) +{ + I_Assert(buffer != NULL && *buffer != NULL); + + WRITEUINT32(*buffer, metal_p - metalbuffer); +} + +void G_LoadMetal(UINT8 **buffer) +{ + I_Assert(buffer != NULL && *buffer != NULL); + + G_DoPlayMetal(); + metal_p = metalbuffer + READUINT32(*buffer); +} + + +void G_ReadDemoTiccmd(ticcmd_t *cmd, INT32 playernum) +{ + UINT8 ziptic; + (void)playernum; + + if (!demo_p || !demo_start) + return; + ziptic = READUINT8(demo_p); + + if (ziptic & ZT_FWD) + oldcmd.forwardmove = READSINT8(demo_p); + if (ziptic & ZT_SIDE) + oldcmd.sidemove = READSINT8(demo_p); + if (ziptic & ZT_ANGLE) + oldcmd.angleturn = READINT16(demo_p); + if (ziptic & ZT_BUTTONS) + oldcmd.buttons = (oldcmd.buttons & (BT_CAMLEFT|BT_CAMRIGHT)) | (READUINT16(demo_p) & ~(BT_CAMLEFT|BT_CAMRIGHT)); + if (ziptic & ZT_AIMING) + oldcmd.aiming = READINT16(demo_p); + + G_CopyTiccmd(cmd, &oldcmd, 1); + + if (!(demoflags & DF_GHOST) && *demo_p == DEMOMARKER) + { + // end of demo data stream + G_CheckDemoStatus(); + return; + } +} + +void G_WriteDemoTiccmd(ticcmd_t *cmd, INT32 playernum) +{ + char ziptic = 0; + UINT8 *ziptic_p; + (void)playernum; + + if (!demo_p) + return; + ziptic_p = demo_p++; // the ziptic, written at the end of this function + + if (cmd->forwardmove != oldcmd.forwardmove) + { + WRITEUINT8(demo_p,cmd->forwardmove); + oldcmd.forwardmove = cmd->forwardmove; + ziptic |= ZT_FWD; + } + + if (cmd->sidemove != oldcmd.sidemove) + { + WRITEUINT8(demo_p,cmd->sidemove); + oldcmd.sidemove = cmd->sidemove; + ziptic |= ZT_SIDE; + } + + if (cmd->angleturn != oldcmd.angleturn) + { + WRITEINT16(demo_p,cmd->angleturn); + oldcmd.angleturn = cmd->angleturn; + ziptic |= ZT_ANGLE; + } + + if (cmd->buttons != oldcmd.buttons) + { + WRITEUINT16(demo_p,cmd->buttons); + oldcmd.buttons = cmd->buttons; + ziptic |= ZT_BUTTONS; + } + + if (cmd->aiming != oldcmd.aiming) + { + WRITEINT16(demo_p,cmd->aiming); + oldcmd.aiming = cmd->aiming; + ziptic |= ZT_AIMING; + } + + *ziptic_p = ziptic; + + // attention here for the ticcmd size! + // latest demos with mouse aiming byte in ticcmd + if (!(demoflags & DF_GHOST) && ziptic_p > demoend - 9) + { + G_CheckDemoStatus(); // no more space + return; + } +} + +void G_GhostAddThok(void) +{ + if (!metalrecording && (!demorecording || !(demoflags & DF_GHOST))) + return; + ghostext.flags = (ghostext.flags & ~EZT_THOKMASK) | EZT_THOK; +} + +void G_GhostAddSpin(void) +{ + if (!metalrecording && (!demorecording || !(demoflags & DF_GHOST))) + return; + ghostext.flags = (ghostext.flags & ~EZT_THOKMASK) | EZT_SPIN; +} + +void G_GhostAddRev(void) +{ + if (!metalrecording && (!demorecording || !(demoflags & DF_GHOST))) + return; + ghostext.flags = (ghostext.flags & ~EZT_THOKMASK) | EZT_REV; +} + +void G_GhostAddFlip(void) +{ + if (!metalrecording && (!demorecording || !(demoflags & DF_GHOST))) + return; + ghostext.flags |= EZT_FLIP; +} + +void G_GhostAddColor(ghostcolor_t color) +{ + if (!demorecording || !(demoflags & DF_GHOST)) + return; + if (ghostext.lastcolor == (UINT8)color) + { + ghostext.flags &= ~EZT_COLOR; + return; + } + ghostext.flags |= EZT_COLOR; + ghostext.color = (UINT8)color; +} + +void G_GhostAddScale(fixed_t scale) +{ + if (!metalrecording && (!demorecording || !(demoflags & DF_GHOST))) + return; + if (ghostext.lastscale == scale) + { + ghostext.flags &= ~EZT_SCALE; + return; + } + ghostext.flags |= EZT_SCALE; + ghostext.scale = scale; +} + +void G_GhostAddHit(mobj_t *victim) +{ + if (!demorecording || !(demoflags & DF_GHOST)) + return; + ghostext.flags |= EZT_HIT; + ghostext.hits++; + ghostext.hitlist = Z_Realloc(ghostext.hitlist, ghostext.hits * sizeof(mobj_t *), PU_LEVEL, NULL); + ghostext.hitlist[ghostext.hits-1] = victim; +} + +void G_WriteGhostTic(mobj_t *ghost) +{ + char ziptic = 0; + UINT8 *ziptic_p; + UINT32 i; + fixed_t height; + + if (!demo_p) + return; + if (!(demoflags & DF_GHOST)) + return; // No ghost data to write. + + ziptic_p = demo_p++; // the ziptic, written at the end of this function + + #define MAXMOM (0xFFFF<<8) + + // GZT_XYZ is only useful if you've moved 256 FRACUNITS or more in a single tic. + if (abs(ghost->x-oldghost.x) > MAXMOM + || abs(ghost->y-oldghost.y) > MAXMOM + || abs(ghost->z-oldghost.z) > MAXMOM) + { + oldghost.x = ghost->x; + oldghost.y = ghost->y; + oldghost.z = ghost->z; + ziptic |= GZT_XYZ; + WRITEFIXED(demo_p,oldghost.x); + WRITEFIXED(demo_p,oldghost.y); + WRITEFIXED(demo_p,oldghost.z); + } + else + { + // For moving normally: + // Store one full byte of movement, plus one byte of fractional movement. + INT16 momx = (INT16)((ghost->x-oldghost.x)>>8); + INT16 momy = (INT16)((ghost->y-oldghost.y)>>8); + if (momx != oldghost.momx + || momy != oldghost.momy) + { + oldghost.momx = momx; + oldghost.momy = momy; + ziptic |= GZT_MOMXY; + WRITEINT16(demo_p,momx); + WRITEINT16(demo_p,momy); + } + momx = (INT16)((ghost->z-oldghost.z)>>8); + if (momx != oldghost.momz) + { + oldghost.momz = momx; + ziptic |= GZT_MOMZ; + WRITEINT16(demo_p,momx); + } + + // This SHOULD set oldghost.x/y/z to match ghost->x/y/z + // but it keeps the fractional loss of one byte, + // so it will hopefully be made up for in future tics. + oldghost.x += oldghost.momx<<8; + oldghost.y += oldghost.momy<<8; + oldghost.z += oldghost.momz<<8; + } + + #undef MAXMOM + + // Only store the 8 most relevant bits of angle + // because exact values aren't too easy to discern to begin with when only 8 angles have different sprites + // and it does not affect this mode of movement at all anyway. + if (ghost->player && ghost->player->drawangle>>24 != oldghost.angle) + { + oldghost.angle = ghost->player->drawangle>>24; + ziptic |= GZT_ANGLE; + WRITEUINT8(demo_p,oldghost.angle); + } + + // Store the sprite frame. + if ((ghost->frame & FF_FRAMEMASK) != oldghost.frame) + { + oldghost.frame = (ghost->frame & FF_FRAMEMASK); + ziptic |= GZT_FRAME; + WRITEUINT8(demo_p,oldghost.frame); + } + + if (ghost->sprite == SPR_PLAY + && ghost->sprite2 != oldghost.sprite2) + { + oldghost.sprite2 = ghost->sprite2; + ziptic |= GZT_SPR2; + WRITEUINT8(demo_p,oldghost.sprite2); + } + + // Check for sprite set changes + if (ghost->sprite != oldghost.sprite) + { + oldghost.sprite = ghost->sprite; + 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; + + if (ghostext.color == ghostext.lastcolor) + ghostext.flags &= ~EZT_COLOR; + if (ghostext.scale == ghostext.lastscale) + ghostext.flags &= ~EZT_SCALE; + + WRITEUINT8(demo_p,ghostext.flags); + if (ghostext.flags & EZT_COLOR) + { + WRITEUINT8(demo_p,ghostext.color); + ghostext.lastcolor = ghostext.color; + } + if (ghostext.flags & EZT_SCALE) + { + WRITEFIXED(demo_p,ghostext.scale); + ghostext.lastscale = ghostext.scale; + } + if (ghostext.flags & EZT_HIT) + { + WRITEUINT16(demo_p,ghostext.hits); + for (i = 0; i < ghostext.hits; i++) + { + mobj_t *mo = ghostext.hitlist[i]; + //WRITEUINT32(demo_p,UINT32_MAX); // reserved for some method of determining exactly which mobj this is. (mobjnum doesn't work here.) + WRITEUINT32(demo_p,mo->type); + WRITEUINT16(demo_p,(UINT16)mo->health); + WRITEFIXED(demo_p,mo->x); + WRITEFIXED(demo_p,mo->y); + WRITEFIXED(demo_p,mo->z); + WRITEANGLE(demo_p,mo->angle); + } + Z_Free(ghostext.hitlist); + ghostext.hits = 0; + ghostext.hitlist = NULL; + } + 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 && !(ghost->player->followmobj->sprite == SPR_NULL || (ghost->player->followmobj->flags2 & MF2_DONTDRAW))) // bloats tails runs but what can ya do + { + INT16 temp; + UINT8 *followtic_p = demo_p++; + UINT8 followtic = 0; + + ziptic |= GZT_FOLLOW; + + if (ghost->player->followmobj->skin) + followtic |= FZT_SKIN; + + if (!(oldghost.flags2 & MF2_AMBUSH)) + { + followtic |= FZT_SPAWNED; + WRITEINT16(demo_p,ghost->player->followmobj->info->height>>FRACBITS); + if (ghost->player->followmobj->flags2 & MF2_LINKDRAW) + followtic |= FZT_LINKDRAW; + if (ghost->player->followmobj->colorized) + followtic |= FZT_COLORIZED; + if (followtic & FZT_SKIN) + WRITEUINT8(demo_p,(UINT8)(((skin_t *)(ghost->player->followmobj->skin))-skins)); + oldghost.flags2 |= MF2_AMBUSH; + } + + if (ghost->player->followmobj->scale != ghost->scale) + { + followtic |= FZT_SCALE; + WRITEFIXED(demo_p,ghost->player->followmobj->scale); + } + + 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); + 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); + + *followtic_p = followtic; + } + else + oldghost.flags2 &= ~MF2_AMBUSH; + + *ziptic_p = ziptic; + + // attention here for the ticcmd size! + // latest demos with mouse aiming byte in ticcmd + if (demo_p >= demoend - (13 + 9 + 9)) + { + G_CheckDemoStatus(); // no more space + return; + } +} + +// Uses ghost data to do consistency checks on your position. +// This fixes desynchronising demos when fighting eggman. +void G_ConsGhostTic(void) +{ + UINT8 ziptic; + UINT16 px,py,pz,gx,gy,gz; + mobj_t *testmo; + + if (!demo_p || !demo_start) + return; + if (!(demoflags & DF_GHOST)) + return; // No ghost data to use. + + testmo = players[0].mo; + + // Grab ghost data. + ziptic = READUINT8(demo_p); + if (ziptic & GZT_XYZ) + { + oldghost.x = READFIXED(demo_p); + oldghost.y = READFIXED(demo_p); + oldghost.z = READFIXED(demo_p); + } + else + { + if (ziptic & GZT_MOMXY) + { + oldghost.momx = READINT16(demo_p)<<8; + oldghost.momy = READINT16(demo_p)<<8; + } + if (ziptic & GZT_MOMZ) + oldghost.momz = READINT16(demo_p)<<8; + oldghost.x += oldghost.momx; + oldghost.y += oldghost.momy; + oldghost.z += oldghost.momz; + } + if (ziptic & GZT_ANGLE) + demo_p++; + if (ziptic & GZT_FRAME) + demo_p++; + if (ziptic & GZT_SPR2) + demo_p++; + + if (ziptic & GZT_EXTRA) + { // But wait, there's more! + UINT8 xziptic = READUINT8(demo_p); + if (xziptic & EZT_COLOR) + demo_p++; + if (xziptic & EZT_SCALE) + demo_p += sizeof(fixed_t); + if (xziptic & EZT_HIT) + { // Resync mob damage. + UINT16 i, count = READUINT16(demo_p); + thinker_t *th; + mobj_t *mobj; + + UINT32 type; + UINT16 health; + fixed_t x; + fixed_t y; + fixed_t z; + + for (i = 0; i < count; i++) + { + //demo_p += 4; // reserved. + type = READUINT32(demo_p); + health = READUINT16(demo_p); + x = READFIXED(demo_p); + y = READFIXED(demo_p); + z = READFIXED(demo_p); + demo_p += sizeof(angle_t); // angle, unnecessary for cons. + + mobj = NULL; + for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) + { + if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) + continue; + mobj = (mobj_t *)th; + if (mobj->type == (mobjtype_t)type && mobj->x == x && mobj->y == y && mobj->z == z) + break; + } + if (th != &thlist[THINK_MOBJ] && mobj->health != health) // Wasn't damaged?! This is desync! Fix it! + { + if (demosynced) + CONS_Alert(CONS_WARNING, M_GetText("Demo playback has desynced!\n")); + demosynced = false; + P_DamageMobj(mobj, players[0].mo, players[0].mo, 1, 0); + } + } + } + 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) + { + 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); + if (followtic & FZT_SKIN) + demo_p++; + demo_p += sizeof(UINT16); + demo_p++; + demo_p++; + } + + // Re-synchronise + px = testmo->x>>FRACBITS; + py = testmo->y>>FRACBITS; + pz = testmo->z>>FRACBITS; + gx = oldghost.x>>FRACBITS; + gy = oldghost.y>>FRACBITS; + gz = oldghost.z>>FRACBITS; + + if (px != gx || py != gy || pz != gz) + { + if (demosynced) + CONS_Alert(CONS_WARNING, M_GetText("Demo playback has desynced!\n")); + demosynced = false; + + P_UnsetThingPosition(testmo); + testmo->x = oldghost.x; + testmo->y = oldghost.y; + P_SetThingPosition(testmo); + testmo->z = oldghost.z; + } + + if (*demo_p == DEMOMARKER) + { + // end of demo data stream + G_CheckDemoStatus(); + return; + } +} + +void G_GhostTicker(void) +{ + demoghost *g,*p; + for(g = ghosts, p = NULL; g; g = g->next) + { + // Skip normal demo data. + UINT8 ziptic = READUINT8(g->p); + UINT8 xziptic = 0; + if (ziptic & ZT_FWD) + g->p++; + if (ziptic & ZT_SIDE) + g->p++; + if (ziptic & ZT_ANGLE) + g->p += 2; + if (ziptic & ZT_BUTTONS) + g->p += 2; + if (ziptic & ZT_AIMING) + g->p += 2; + + // Grab ghost data. + ziptic = READUINT8(g->p); + if (ziptic & GZT_XYZ) + { + g->oldmo.x = READFIXED(g->p); + g->oldmo.y = READFIXED(g->p); + g->oldmo.z = READFIXED(g->p); + } + else + { + if (ziptic & GZT_MOMXY) + { + g->oldmo.momx = READINT16(g->p)<<8; + g->oldmo.momy = READINT16(g->p)<<8; + } + if (ziptic & GZT_MOMZ) + g->oldmo.momz = READINT16(g->p)<<8; + g->oldmo.x += g->oldmo.momx; + g->oldmo.y += g->oldmo.momy; + g->oldmo.z += g->oldmo.momz; + } + if (ziptic & GZT_ANGLE) + g->mo->angle = READUINT8(g->p)<<24; + if (ziptic & GZT_FRAME) + g->oldmo.frame = READUINT8(g->p); + if (ziptic & GZT_SPR2) + g->oldmo.sprite2 = READUINT8(g->p); + + // Update ghost + P_UnsetThingPosition(g->mo); + g->mo->x = g->oldmo.x; + g->mo->y = g->oldmo.y; + 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) + { // But wait, there's more! + xziptic = READUINT8(g->p); + if (xziptic & EZT_COLOR) + { + g->color = READUINT8(g->p); + switch(g->color) + { + default: + case GHC_RETURNSKIN: + g->mo->skin = g->oldmo.skin; + /* FALLTHRU */ + case GHC_NORMAL: // Go back to skin color + g->mo->color = g->oldmo.color; + break; + // Handled below + case GHC_SUPER: + case GHC_INVINCIBLE: + break; + case GHC_FIREFLOWER: // Fireflower + g->mo->color = SKINCOLOR_WHITE; + break; + case GHC_NIGHTSSKIN: // not actually a colour + g->mo->skin = &skins[DEFAULTNIGHTSSKIN]; + break; + } + } + if (xziptic & EZT_FLIP) + g->mo->eflags ^= MFE_VERTICALFLIP; + if (xziptic & EZT_SCALE) + { + g->mo->destscale = READFIXED(g->p); + if (g->mo->destscale != g->mo->scale) + P_SetScale(g->mo, g->mo->destscale); + } + if (xziptic & EZT_THOKMASK) + { // Let's only spawn ONE of these per frame, thanks. + mobj_t *mobj; + INT32 type = -1; + if (g->mo->skin) + { + skin_t *skin = (skin_t *)g->mo->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(g->mo); // does a large portion of the work for us + mobj->frame = (mobj->frame & ~FF_FRAMEMASK)|tr_trans60<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); + } + } + if (xziptic & EZT_HIT) + { // Spawn hit poofs for killing things! + UINT16 i, count = READUINT16(g->p), health; + UINT32 type; + fixed_t x,y,z; + angle_t angle; + mobj_t *poof; + for (i = 0; i < count; i++) + { + //g->p += 4; // reserved + type = READUINT32(g->p); + health = READUINT16(g->p); + x = READFIXED(g->p); + y = READFIXED(g->p); + z = READFIXED(g->p); + angle = READANGLE(g->p); + if (!(mobjinfo[type].flags & MF_SHOOTABLE) + || !(mobjinfo[type].flags & (MF_ENEMY|MF_MONITOR)) + || health != 0 || i >= 4) // only spawn for the first 4 hits per frame, to prevent ghosts from splode-spamming too bad. + continue; + poof = P_SpawnMobj(x, y, z, MT_GHOST); + poof->angle = angle; + poof->flags = MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY; // make an ATTEMPT to curb crazy SOCs fucking stuff up... + poof->health = 0; + P_SetMobjStateNF(poof, S_XPLD1); + } + } + 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) + switch(g->color) + { + case GHC_SUPER: // Super (P_DoSuperStuff) + if (g->mo->skin) + { + skin_t *skin = (skin_t *)g->mo->skin; + g->mo->color = skin->supercolor; + } + else + g->mo->color = SKINCOLOR_SUPERGOLD1; + g->mo->color += abs( ( (signed)( (unsigned)leveltime >> 1 ) % 9) - 4); + break; + case GHC_INVINCIBLE: // Mario invincibility (P_CheckInvincibilityTimer) + g->mo->color = (UINT8)(SKINCOLOR_RUBY + (leveltime % (MAXSKINCOLORS - SKINCOLOR_RUBY))); // Passes through all saturated colours + break; + default: + break; + } + +#define follow g->mo->tracer + 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_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; + + if (followtic & FZT_COLORIZED) + follow->colorized = true; + + if (followtic & FZT_SKIN) + follow->skin = &skins[READUINT8(g->p)]; + } + if (follow) + { + if (followtic & FZT_SCALE) + follow->destscale = READFIXED(g->p); + else + follow->destscale = g->mo->destscale; + if (follow->destscale != follow->scale) + P_SetScale(follow, follow->destscale); + + P_UnsetThingPosition(follow); + temp = READINT16(g->p)<<8; + follow->x = g->mo->x + temp; + temp = READINT16(g->p)<<8; + follow->y = g->mo->y + temp; + temp = READINT16(g->p)<<8; + follow->z = g->mo->z + temp; + P_SetThingPosition(follow); + if (followtic & FZT_SKIN) + follow->sprite2 = READUINT8(g->p); + else + follow->sprite2 = 0; + follow->sprite = READUINT16(g->p); + follow->frame = (READUINT8(g->p)) | (g->mo->frame & FF_TRANSMASK); + follow->angle = g->mo->angle; + follow->color = READUINT8(g->p); + + if (!(followtic & FZT_SPAWNED)) + { + if (xziptic & EZT_FLIP) + { + follow->flags2 ^= MF2_OBJECTFLIP; + follow->eflags ^= MFE_VERTICALFLIP; + } + } + } + } + else if (follow) + { + P_RemoveMobj(follow); + P_SetTarget(&follow, NULL); + } + // 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 + ghosts = g->next; + Z_Free(g); + continue; + } + p = g; +#undef follow + } +} + +void G_ReadMetalTic(mobj_t *metal) +{ + UINT8 ziptic; + UINT8 xziptic = 0; + + if (!metal_p) + return; + + if (!metal->health) + { + G_StopMetalDemo(); + return; + } + + switch (*metal_p) + { + case METALSNICE: + break; + case METALDEATH: + if (metal->tracer) + P_RemoveMobj(metal->tracer); + P_KillMobj(metal, NULL, NULL, 0); + /* FALLTHRU */ + case DEMOMARKER: + default: + // end of demo data stream + G_StopMetalDemo(); + return; + } + metal_p++; + + ziptic = READUINT8(metal_p); + + // Read changes from the tic + if (ziptic & GZT_XYZ) + { + P_TeleportMove(metal, READFIXED(metal_p), READFIXED(metal_p), READFIXED(metal_p)); + oldmetal.x = metal->x; + oldmetal.y = metal->y; + oldmetal.z = metal->z; + } + else + { + if (ziptic & GZT_MOMXY) + { + oldmetal.momx = READINT16(metal_p)<<8; + oldmetal.momy = READINT16(metal_p)<<8; + } + if (ziptic & GZT_MOMZ) + oldmetal.momz = READINT16(metal_p)<<8; + oldmetal.x += oldmetal.momx; + oldmetal.y += oldmetal.momy; + oldmetal.z += oldmetal.momz; + } + if (ziptic & GZT_ANGLE) + metal->angle = READUINT8(metal_p)<<24; + if (ziptic & GZT_FRAME) + oldmetal.frame = READUINT32(metal_p); + if (ziptic & GZT_SPR2) + oldmetal.sprite2 = READUINT8(metal_p); + + // Set movement, position, and angle + // oldmetal contains where you're supposed to be. + metal->momx = oldmetal.momx; + metal->momy = oldmetal.momy; + metal->momz = oldmetal.momz; + P_UnsetThingPosition(metal); + metal->x = oldmetal.x; + 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! + xziptic = READUINT8(metal_p); + if (xziptic & EZT_FLIP) + { + metal->eflags ^= MFE_VERTICALFLIP; + metal->flags2 ^= MF2_OBJECTFLIP; + } + if (xziptic & EZT_SCALE) + { + metal->destscale = READFIXED(metal_p); + if (metal->destscale != metal->scale) + P_SetScale(metal, metal->destscale); + } + if (xziptic & EZT_THOKMASK) + { // Let's only spawn ONE of these per frame, thanks. + mobj_t *mobj; + INT32 type = -1; + if (metal->skin) + { + skin_t *skin = (skin_t *)metal->skin; + switch (xziptic & EZT_THOKMASK) + { + case EZT_THOK: + type = skin->thokitem < 0 ? (UINT32)mobjinfo[MT_PLAYER].painchance : (UINT32)skin->thokitem; + break; + case EZT_SPIN: + type = skin->spinitem < 0 ? (UINT32)mobjinfo[MT_PLAYER].damage : (UINT32)skin->spinitem; + break; + case EZT_REV: + type = skin->revitem < 0 ? (UINT32)mobjinfo[MT_PLAYER].raisestate : (UINT32)skin->revitem; + break; + } + } + if (type != MT_NULL) + { + if (type == MT_GHOST) + { + mobj = P_SpawnGhostMobj(metal); // does a large portion of the work for us + } + else + { + mobj = P_SpawnMobjFromMobj(metal, 0, 0, -FixedDiv(FixedMul(metal->info->height, metal->scale) - metal->height,3*FRACUNIT), MT_THOK); + mobj->sprite = states[mobjinfo[type].spawnstate].sprite; + mobj->frame = states[mobjinfo[type].spawnstate].frame; + mobj->angle = metal->angle; + mobj->color = metal->color; + mobj->skin = metal->skin; + P_SetScale(mobj, (mobj->destscale = metal->scale)); + + 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_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; + + if (followtic & FZT_SKIN) + follow->skin = &skins[READUINT8(metal_p)]; + } + if (follow) + { + if (followtic & FZT_SCALE) + follow->destscale = READFIXED(metal_p); + else + follow->destscale = metal->destscale; + if (follow->destscale != follow->scale) + P_SetScale(follow, follow->destscale); + + P_UnsetThingPosition(follow); + temp = READINT16(metal_p)<<8; + follow->x = metal->x + temp; + temp = READINT16(metal_p)<<8; + follow->y = metal->y + temp; + temp = READINT16(metal_p)<<8; + follow->z = metal->z + temp; + P_SetThingPosition(follow); + if (followtic & FZT_SKIN) + follow->sprite2 = READUINT8(metal_p); + else + follow->sprite2 = 0; + follow->sprite = READUINT16(metal_p); + follow->frame = READUINT32(metal_p); // NOT & FF_FRAMEMASK here, so 32 bits + follow->angle = metal->angle; + follow->color = READUINT8(metal_p); + + if (!(followtic & FZT_SPAWNED)) + { + if (xziptic & EZT_FLIP) + { + follow->flags2 ^= MF2_OBJECTFLIP; + follow->eflags ^= MFE_VERTICALFLIP; + } + } + } + } + else if (follow) + { + P_RemoveMobj(follow); + P_SetTarget(&follow, NULL); + } +#undef follow +} + +void G_WriteMetalTic(mobj_t *metal) +{ + UINT8 ziptic = 0; + UINT8 *ziptic_p; + fixed_t height; + + if (!demo_p) // demo_p will be NULL until the race start linedef executor is activated! + return; + + WRITEUINT8(demo_p, METALSNICE); + ziptic_p = demo_p++; // the ziptic, written at the end of this function + + #define MAXMOM (0xFFFF<<8) + + // GZT_XYZ is only useful if you've moved 256 FRACUNITS or more in a single tic. + if (abs(metal->x-oldmetal.x) > MAXMOM + || abs(metal->y-oldmetal.y) > MAXMOM + || abs(metal->z-oldmetal.z) > MAXMOM) + { + 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); + } + else + { + // For moving normally: + // Store one full byte of movement, plus one byte of fractional movement. + INT16 momx = (INT16)((metal->x-oldmetal.x)>>8); + INT16 momy = (INT16)((metal->y-oldmetal.y)>>8); + if (momx != oldmetal.momx + || momy != oldmetal.momy) + { + oldmetal.momx = momx; + oldmetal.momy = momy; + ziptic |= GZT_MOMXY; + WRITEINT16(demo_p,momx); + WRITEINT16(demo_p,momy); + } + momx = (INT16)((metal->z-oldmetal.z)>>8); + if (momx != oldmetal.momz) + { + oldmetal.momz = momx; + ziptic |= GZT_MOMZ; + WRITEINT16(demo_p,momx); + } + + // This SHOULD set oldmetal.x/y/z to match metal->x/y/z + // but it keeps the fractional loss of one byte, + // so it will hopefully be made up for in future tics. + oldmetal.x += oldmetal.momx<<8; + oldmetal.y += oldmetal.momy<<8; + oldmetal.z += oldmetal.momz<<8; + } + + #undef MAXMOM + + // Only store the 8 most relevant bits of angle + // because exact values aren't too easy to discern to begin with when only 8 angles have different sprites + // and it does not affect movement at all anyway. + if (metal->player && metal->player->drawangle>>24 != oldmetal.angle) + { + oldmetal.angle = metal->player->drawangle>>24; + ziptic |= GZT_ANGLE; + WRITEUINT8(demo_p,oldmetal.angle); + } + + // 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); + } + + // Check for sprite set changes + if (metal->sprite != oldmetal.sprite) + { + oldmetal.sprite = metal->sprite; + ghostext.flags |= EZT_SPRITE; + } + + if ((height = FixedDiv(metal->height, metal->scale)) != oldmetal.height) + { + oldmetal.height = height; + ghostext.flags |= EZT_HEIGHT; + } + + if (ghostext.flags & ~(EZT_COLOR|EZT_HIT)) // these two aren't handled by metal ever + { + ziptic |= GZT_EXTRA; + + if (ghostext.scale == ghostext.lastscale) + ghostext.flags &= ~EZT_SCALE; + + WRITEUINT8(demo_p,ghostext.flags); + if (ghostext.flags & EZT_SCALE) + { + WRITEFIXED(demo_p,ghostext.scale); + ghostext.lastscale = ghostext.scale; + } + if (ghostext.flags & EZT_SPRITE) + WRITEUINT16(demo_p,oldmetal.sprite); + if (ghostext.flags & EZT_HEIGHT) + { + height >>= FRACBITS; + WRITEINT16(demo_p, height); + } + ghostext.flags = 0; + } + + if (metal->player && metal->player->followmobj && !(metal->player->followmobj->sprite == SPR_NULL || (metal->player->followmobj->flags2 & MF2_DONTDRAW))) + { + INT16 temp; + UINT8 *followtic_p = demo_p++; + UINT8 followtic = 0; + + ziptic |= GZT_FOLLOW; + + if (metal->player->followmobj->skin) + followtic |= FZT_SKIN; + + if (!(oldmetal.flags2 & MF2_AMBUSH)) + { + followtic |= FZT_SPAWNED; + WRITEINT16(demo_p,metal->player->followmobj->info->height>>FRACBITS); + if (metal->player->followmobj->flags2 & MF2_LINKDRAW) + followtic |= FZT_LINKDRAW; + if (metal->player->followmobj->colorized) + followtic |= FZT_COLORIZED; + if (followtic & FZT_SKIN) + WRITEUINT8(demo_p,(UINT8)(((skin_t *)(metal->player->followmobj->skin))-skins)); + oldmetal.flags2 |= MF2_AMBUSH; + } + + if (metal->player->followmobj->scale != metal->scale) + { + followtic |= FZT_SCALE; + WRITEFIXED(demo_p,metal->player->followmobj->scale); + } + + temp = (INT16)((metal->player->followmobj->x-metal->x)>>8); + WRITEINT16(demo_p,temp); + temp = (INT16)((metal->player->followmobj->y-metal->y)>>8); + WRITEINT16(demo_p,temp); + temp = (INT16)((metal->player->followmobj->z-metal->z)>>8); + WRITEINT16(demo_p,temp); + if (followtic & FZT_SKIN) + WRITEUINT8(demo_p,metal->player->followmobj->sprite2); + WRITEUINT16(demo_p,metal->player->followmobj->sprite); + WRITEUINT32(demo_p,metal->player->followmobj->frame); // NOT & FF_FRAMEMASK here, so 32 bits + WRITEUINT8(demo_p,metal->player->followmobj->color); + + *followtic_p = followtic; + } + else + oldmetal.flags2 &= ~MF2_AMBUSH; + + *ziptic_p = ziptic; + + // attention here for the ticcmd size! + // latest demos with mouse aiming byte in ticcmd + if (demo_p >= demoend - 32) + { + G_StopMetalRecording(false); // no more space + return; + } +} + +// +// G_RecordDemo +// +void G_RecordDemo(const char *name) +{ + INT32 maxsize; + + strcpy(demoname, name); + strcat(demoname, ".lmp"); + maxsize = 1024*1024; + if (M_CheckParm("-maxdemo") && M_IsNextParm()) + maxsize = atoi(M_GetNextParm()) * 1024; +// if (demobuffer) +// free(demobuffer); + demo_p = NULL; + demobuffer = malloc(maxsize); + demoend = demobuffer + maxsize; + + demorecording = true; +} + +void G_RecordMetal(void) +{ + INT32 maxsize; + maxsize = 1024*1024; + if (M_CheckParm("-maxdemo") && M_IsNextParm()) + maxsize = atoi(M_GetNextParm()) * 1024; + demo_p = NULL; + demobuffer = malloc(maxsize); + demoend = demobuffer + maxsize; + metalrecording = true; +} + +void G_BeginRecording(void) +{ + UINT8 i; + char name[16]; + player_t *player = &players[consoleplayer]; + + if (demo_p) + return; + memset(name,0,sizeof(name)); + + demo_p = demobuffer; + demoflags = DF_GHOST|(modeattacking<>DF_ATTACKSHIFT) + { + case ATTACKING_NONE: // 0 + break; + case ATTACKING_RECORD: // 1 + demotime_p = demo_p; + WRITEUINT32(demo_p,UINT32_MAX); // time + WRITEUINT32(demo_p,0); // score + WRITEUINT16(demo_p,0); // rings + break; + case ATTACKING_NIGHTS: // 2 + demotime_p = demo_p; + WRITEUINT32(demo_p,UINT32_MAX); // time + WRITEUINT32(demo_p,0); // score + break; + default: // 3 + break; + } + + WRITEUINT32(demo_p,P_GetInitSeed()); + + // Name + for (i = 0; i < 16 && cv_playername.string[i]; i++) + name[i] = cv_playername.string[i]; + for (; i < 16; i++) + name[i] = '\0'; + M_Memcpy(demo_p,name,16); + demo_p += 16; + + // Skin + for (i = 0; i < 16 && cv_skin.string[i]; i++) + name[i] = cv_skin.string[i]; + for (; i < 16; i++) + name[i] = '\0'; + M_Memcpy(demo_p,name,16); + demo_p += 16; + + // Color + for (i = 0; i < 16 && cv_playercolor.string[i]; i++) + name[i] = cv_playercolor.string[i]; + for (; i < 16; i++) + name[i] = '\0'; + M_Memcpy(demo_p,name,16); + demo_p += 16; + + // Stats + WRITEUINT8(demo_p,player->charability); + WRITEUINT8(demo_p,player->charability2); + WRITEUINT8(demo_p,player->actionspd>>FRACBITS); + WRITEUINT8(demo_p,player->mindash>>FRACBITS); + WRITEUINT8(demo_p,player->maxdash>>FRACBITS); + WRITEUINT8(demo_p,player->normalspeed>>FRACBITS); + WRITEUINT8(demo_p,player->runspeed>>FRACBITS); + WRITEUINT8(demo_p,player->thrustfactor); + WRITEUINT8(demo_p,player->accelstart); + WRITEUINT8(demo_p,player->acceleration); + WRITEUINT8(demo_p,player->height>>FRACBITS); + WRITEUINT8(demo_p,player->spinheight>>FRACBITS); + WRITEUINT8(demo_p,player->camerascale>>FRACBITS); + WRITEUINT8(demo_p,player->shieldscale>>FRACBITS); + + // Trying to convert it back to % causes demo desync due to precision loss. + // Don't do it. + WRITEFIXED(demo_p, player->jumpfactor); + + // And mobjtype_t is best with UINT32 too... + WRITEUINT32(demo_p, player->followitem); + + // Save pflag data - see SendWeaponPref() + { + UINT8 buf = 0; + pflags_t pflags = 0; + if (cv_flipcam.value) + { + buf |= 0x01; + pflags |= PF_FLIPCAM; + } + if (cv_analog[0].value) + { + buf |= 0x02; + pflags |= PF_ANALOGMODE; + } + if (cv_directionchar[0].value) + { + buf |= 0x04; + 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 + CV_SaveNetVars(&demo_p); + + memset(&oldcmd,0,sizeof(oldcmd)); + memset(&oldghost,0,sizeof(oldghost)); + memset(&ghostext,0,sizeof(ghostext)); + ghostext.lastcolor = ghostext.color = GHC_NORMAL; + ghostext.lastscale = ghostext.scale = FRACUNIT; + + if (player->mo) + { + oldghost.x = player->mo->x; + oldghost.y = player->mo->y; + oldghost.z = player->mo->z; + oldghost.angle = player->mo->angle>>24; + + // preticker started us gravity flipped + if (player->mo->eflags & MFE_VERTICALFLIP) + ghostext.flags |= EZT_FLIP; + } +} + +void G_BeginMetal(void) +{ + mobj_t *mo = players[consoleplayer].mo; + +#if 0 + if (demo_p) + return; +#endif + + demo_p = demobuffer; + + // Write header. + M_Memcpy(demo_p, DEMOHEADER, 12); demo_p += 12; + WRITEUINT8(demo_p,VERSION); + WRITEUINT8(demo_p,SUBVERSION); + WRITEUINT16(demo_p,DEMOVERSION); + + // demo checksum + demo_p += 16; + + 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; + oldmetal.y = mo->y; + oldmetal.z = mo->z; + oldmetal.angle = mo->angle>>24; +} + +void G_SetDemoTime(UINT32 ptime, UINT32 pscore, UINT16 prings) +{ + if (!demorecording || !demotime_p) + return; + if (demoflags & DF_RECORDATTACK) + { + WRITEUINT32(demotime_p, ptime); + WRITEUINT32(demotime_p, pscore); + WRITEUINT16(demotime_p, prings); + demotime_p = NULL; + } + else if (demoflags & DF_NIGHTSATTACK) + { + WRITEUINT32(demotime_p, ptime); + WRITEUINT32(demotime_p, pscore); + demotime_p = NULL; + } +} + +// Returns bitfield: +// 1 == new demo has lower time +// 2 == new demo has higher score +// 4 == new demo has higher rings +UINT8 G_CmpDemoTime(char *oldname, char *newname) +{ + UINT8 *buffer,*p; + UINT8 flags; + UINT32 oldtime, newtime, oldscore, newscore; + UINT16 oldrings, newrings, oldversion; + size_t bufsize ATTRUNUSED; + UINT8 c; + UINT16 s ATTRUNUSED; + UINT8 aflags = 0; + + // load the new file + FIL_DefaultExtension(newname, ".lmp"); + bufsize = FIL_ReadFile(newname, &buffer); + I_Assert(bufsize != 0); + p = buffer; + + // read demo header + I_Assert(!memcmp(p, DEMOHEADER, 12)); + p += 12; // DEMOHEADER + c = READUINT8(p); // VERSION + I_Assert(c == VERSION); + c = READUINT8(p); // SUBVERSION + I_Assert(c == SUBVERSION); + s = READUINT16(p); + I_Assert(s == DEMOVERSION); + p += 16; // demo checksum + I_Assert(!memcmp(p, "PLAY", 4)); + p += 4; // PLAY + p += 2; // gamemap + p += 16; // map md5 + flags = READUINT8(p); // demoflags + + aflags = flags & (DF_RECORDATTACK|DF_NIGHTSATTACK); + I_Assert(aflags); + if (flags & DF_RECORDATTACK) + { + newtime = READUINT32(p); + newscore = READUINT32(p); + newrings = READUINT16(p); + } + else if (flags & DF_NIGHTSATTACK) + { + newtime = READUINT32(p); + newscore = READUINT32(p); + newrings = 0; + } + else // appease compiler + return 0; + + Z_Free(buffer); + + // load old file + FIL_DefaultExtension(oldname, ".lmp"); + if (!FIL_ReadFile(oldname, &buffer)) + { + CONS_Alert(CONS_ERROR, M_GetText("Failed to read file '%s'.\n"), oldname); + return UINT8_MAX; + } + p = buffer; + + // read demo header + if (memcmp(p, DEMOHEADER, 12)) + { + CONS_Alert(CONS_NOTICE, M_GetText("File '%s' invalid format. It will be overwritten.\n"), oldname); + Z_Free(buffer); + return UINT8_MAX; + } p += 12; // DEMOHEADER + p++; // VERSION + p++; // SUBVERSION + oldversion = READUINT16(p); + switch(oldversion) // demoversion + { + case DEMOVERSION: // latest always supported + break; + // too old, cannot support. + default: + CONS_Alert(CONS_NOTICE, M_GetText("File '%s' invalid format. It will be overwritten.\n"), oldname); + Z_Free(buffer); + return UINT8_MAX; + } + p += 16; // demo checksum + if (memcmp(p, "PLAY", 4)) + { + CONS_Alert(CONS_NOTICE, M_GetText("File '%s' invalid format. It will be overwritten.\n"), oldname); + Z_Free(buffer); + return UINT8_MAX; + } p += 4; // "PLAY" + if (oldversion <= 0x0008) + p++; // gamemap + else + p += 2; // gamemap + p += 16; // mapmd5 + flags = READUINT8(p); + if (!(flags & aflags)) + { + CONS_Alert(CONS_NOTICE, M_GetText("File '%s' not from same game mode. It will be overwritten.\n"), oldname); + Z_Free(buffer); + return UINT8_MAX; + } + if (flags & DF_RECORDATTACK) + { + oldtime = READUINT32(p); + oldscore = READUINT32(p); + oldrings = READUINT16(p); + } + else if (flags & DF_NIGHTSATTACK) + { + oldtime = READUINT32(p); + oldscore = READUINT32(p); + oldrings = 0; + } + else // appease compiler + return UINT8_MAX; + + Z_Free(buffer); + + c = 0; + if (newtime < oldtime + || (newtime == oldtime && (newscore > oldscore || newrings > oldrings))) + c |= 1; // Better time + if (newscore > oldscore + || (newscore == oldscore && newtime < oldtime)) + c |= 1<<1; // Better score + if (newrings > oldrings + || (newrings == oldrings && newtime < oldtime)) + c |= 1<<2; // Better rings + return c; +} + +// +// G_PlayDemo +// +void G_DeferedPlayDemo(const char *name) +{ + COM_BufAddText("playdemo \""); + COM_BufAddText(name); + COM_BufAddText("\"\n"); +} + +// +// Start a demo from a .LMP file or from a wad resource +// +void G_DoPlayDemo(char *defdemoname) +{ + UINT8 i; + lumpnum_t l; + char skin[17],color[17],*n,*pdemoname; + UINT8 version,subversion,charability,charability2,thrustfactor,accelstart,acceleration; + pflags_t pflags; + UINT32 randseed, followitem; + fixed_t camerascale,shieldscale,actionspd,mindash,maxdash,normalspeed,runspeed,jumpfactor,height,spinheight; + char msg[1024]; + + skin[16] = '\0'; + color[16] = '\0'; + + n = defdemoname+strlen(defdemoname); + while (*n != '/' && *n != '\\' && n != defdemoname) + n--; + if (n != defdemoname) + n++; + pdemoname = ZZ_Alloc(strlen(n)+1); + strcpy(pdemoname,n); + + // Internal if no extension, external if one exists + if (FIL_CheckExtension(defdemoname)) + { + //FIL_DefaultExtension(defdemoname, ".lmp"); + if (!FIL_ReadFile(defdemoname, &demobuffer)) + { + snprintf(msg, 1024, M_GetText("Failed to read file '%s'.\n"), defdemoname); + CONS_Alert(CONS_ERROR, "%s", msg); + gameaction = ga_nothing; + M_StartMessage(msg, NULL, MM_NOTHING); + return; + } + demo_p = demobuffer; + } + // load demo resource from WAD + else if ((l = W_CheckNumForName(defdemoname)) == LUMPERROR) + { + snprintf(msg, 1024, M_GetText("Failed to read lump '%s'.\n"), defdemoname); + CONS_Alert(CONS_ERROR, "%s", msg); + gameaction = ga_nothing; + M_StartMessage(msg, NULL, MM_NOTHING); + return; + } + else // it's an internal demo + demobuffer = demo_p = W_CacheLumpNum(l, PU_STATIC); + + // read demo header + gameaction = ga_nothing; + demoplayback = true; + if (memcmp(demo_p, DEMOHEADER, 12)) + { + snprintf(msg, 1024, M_GetText("%s is not a SRB2 replay file.\n"), pdemoname); + CONS_Alert(CONS_ERROR, "%s", msg); + M_StartMessage(msg, NULL, MM_NOTHING); + Z_Free(pdemoname); + Z_Free(demobuffer); + demoplayback = false; + titledemo = false; + return; + } + demo_p += 12; // DEMOHEADER + + version = READUINT8(demo_p); + subversion = READUINT8(demo_p); + demoversion = READUINT16(demo_p); + switch(demoversion) + { + case DEMOVERSION: // latest always supported + break; + // too old, cannot support. + default: + snprintf(msg, 1024, M_GetText("%s is an incompatible replay format and cannot be played.\n"), pdemoname); + CONS_Alert(CONS_ERROR, "%s", msg); + M_StartMessage(msg, NULL, MM_NOTHING); + Z_Free(pdemoname); + Z_Free(demobuffer); + demoplayback = false; + titledemo = false; + return; + } + demo_p += 16; // demo checksum + if (memcmp(demo_p, "PLAY", 4)) + { + snprintf(msg, 1024, M_GetText("%s is the wrong type of recording and cannot be played.\n"), pdemoname); + CONS_Alert(CONS_ERROR, "%s", msg); + M_StartMessage(msg, NULL, MM_NOTHING); + Z_Free(pdemoname); + Z_Free(demobuffer); + demoplayback = false; + titledemo = false; + return; + } + demo_p += 4; // "PLAY" + gamemap = READINT16(demo_p); + demo_p += 16; // mapmd5 + + demoflags = READUINT8(demo_p); + modeattacking = (demoflags & DF_ATTACKMASK)>>DF_ATTACKSHIFT; + CON_ToggleOff(); + + hu_demoscore = 0; + hu_demotime = UINT32_MAX; + hu_demorings = 0; + + switch (modeattacking) + { + case ATTACKING_NONE: // 0 + break; + case ATTACKING_RECORD: // 1 + hu_demotime = READUINT32(demo_p); + hu_demoscore = READUINT32(demo_p); + hu_demorings = READUINT16(demo_p); + break; + case ATTACKING_NIGHTS: // 2 + hu_demotime = READUINT32(demo_p); + hu_demoscore = READUINT32(demo_p); + break; + default: // 3 + modeattacking = ATTACKING_NONE; + break; + } + + // Random seed + randseed = READUINT32(demo_p); + + // Player name + M_Memcpy(player_names[0],demo_p,16); + demo_p += 16; + + // Skin + M_Memcpy(skin,demo_p,16); + demo_p += 16; + + // Color + M_Memcpy(color,demo_p,16); + demo_p += 16; + + charability = READUINT8(demo_p); + charability2 = READUINT8(demo_p); + actionspd = (fixed_t)READUINT8(demo_p)<color = players[0].skincolor; + oldghost.x = players[0].mo->x; + oldghost.y = players[0].mo->y; + oldghost.z = players[0].mo->z; + } + + // Set saved attribute values + // No cheat checking here, because even if they ARE wrong... + // it would only break the replay if we clipped them. + players[0].camerascale = camerascale; + players[0].shieldscale = shieldscale; + players[0].charability = charability; + players[0].charability2 = charability2; + players[0].actionspd = actionspd; + players[0].mindash = mindash; + players[0].maxdash = maxdash; + players[0].normalspeed = normalspeed; + players[0].runspeed = runspeed; + players[0].thrustfactor = thrustfactor; + players[0].accelstart = accelstart; + players[0].acceleration = acceleration; + players[0].height = height; + players[0].spinheight = spinheight; + players[0].jumpfactor = jumpfactor; + players[0].followitem = followitem; + players[0].pflags = pflags; + + demo_start = true; +} + +void G_AddGhost(char *defdemoname) +{ + INT32 i; + lumpnum_t l; + char name[17],skin[17],color[17],*n,*pdemoname,md5[16]; + demoghost *gh; + UINT8 flags; + UINT8 *buffer,*p; + mapthing_t *mthing; + UINT16 count, ghostversion; + + name[16] = '\0'; + skin[16] = '\0'; + color[16] = '\0'; + + n = defdemoname+strlen(defdemoname); + while (*n != '/' && *n != '\\' && n != defdemoname) + n--; + if (n != defdemoname) + n++; + pdemoname = ZZ_Alloc(strlen(n)+1); + strcpy(pdemoname,n); + + // Internal if no extension, external if one exists + if (FIL_CheckExtension(defdemoname)) + { + //FIL_DefaultExtension(defdemoname, ".lmp"); + if (!FIL_ReadFileTag(defdemoname, &buffer, PU_LEVEL)) + { + CONS_Alert(CONS_ERROR, M_GetText("Failed to read file '%s'.\n"), defdemoname); + Z_Free(pdemoname); + return; + } + p = buffer; + } + // load demo resource from WAD + else if ((l = W_CheckNumForName(defdemoname)) == LUMPERROR) + { + CONS_Alert(CONS_ERROR, M_GetText("Failed to read lump '%s'.\n"), defdemoname); + Z_Free(pdemoname); + return; + } + else // it's an internal demo + buffer = p = W_CacheLumpNum(l, PU_LEVEL); + + // read demo header + if (memcmp(p, DEMOHEADER, 12)) + { + CONS_Alert(CONS_NOTICE, M_GetText("Ghost %s: Not a SRB2 replay.\n"), pdemoname); + Z_Free(pdemoname); + Z_Free(buffer); + return; + } p += 12; // DEMOHEADER + p++; // VERSION + p++; // SUBVERSION + ghostversion = READUINT16(p); + switch(ghostversion) + { + case DEMOVERSION: // latest always supported + break; + // too old, cannot support. + default: + CONS_Alert(CONS_NOTICE, M_GetText("Ghost %s: Demo version incompatible.\n"), pdemoname); + Z_Free(pdemoname); + Z_Free(buffer); + return; + } + M_Memcpy(md5, p, 16); p += 16; // demo checksum + for (gh = ghosts; gh; gh = gh->next) + if (!memcmp(md5, gh->checksum, 16)) // another ghost in the game already has this checksum? + { // Don't add another one, then! + CONS_Debug(DBG_SETUP, "Rejecting duplicate ghost %s (MD5 was matched)\n", pdemoname); + Z_Free(pdemoname); + Z_Free(buffer); + return; + } + if (memcmp(p, "PLAY", 4)) + { + CONS_Alert(CONS_NOTICE, M_GetText("Ghost %s: Demo format unacceptable.\n"), pdemoname); + Z_Free(pdemoname); + Z_Free(buffer); + return; + } p += 4; // "PLAY" + if (ghostversion <= 0x0008) + p++; // gamemap + else + p += 2; // gamemap + p += 16; // mapmd5 (possibly check for consistency?) + flags = READUINT8(p); + if (!(flags & DF_GHOST)) + { + CONS_Alert(CONS_NOTICE, M_GetText("Ghost %s: No ghost data in this demo.\n"), pdemoname); + Z_Free(pdemoname); + Z_Free(buffer); + return; + } + switch ((flags & DF_ATTACKMASK)>>DF_ATTACKSHIFT) + { + case ATTACKING_NONE: // 0 + break; + case ATTACKING_RECORD: // 1 + p += 10; // demo time, score, and rings + break; + case ATTACKING_NIGHTS: // 2 + p += 8; // demo time left, score + break; + default: // 3 + break; + } + + p += 4; // random seed + + // Player name (TODO: Display this somehow if it doesn't match cv_playername!) + M_Memcpy(name, p,16); + p += 16; + + // Skin + M_Memcpy(skin, p,16); + p += 16; + + // Color + M_Memcpy(color, p,16); + p += 16; + + // Ghosts do not have a player structure to put this in. + p++; // charability + p++; // charability2 + p++; // actionspd + p++; // mindash + p++; // maxdash + p++; // normalspeed + p++; // runspeed + p++; // thrustfactor + p++; // accelstart + p++; // acceleration + p++; // height + p++; // spinheight + p++; // camerascale + p++; // shieldscale + p += 4; // jumpfactor + p += 4; // followitem + + p++; // pflag data + + // net var data + count = READUINT16(p); + while (count--) + { + p += 2; + SKIPSTRING(p); + p++; + } + + if (*p == DEMOMARKER) + { + CONS_Alert(CONS_NOTICE, M_GetText("Failed to add ghost %s: Replay is empty.\n"), pdemoname); + Z_Free(pdemoname); + Z_Free(buffer); + return; + } + + gh = Z_Calloc(sizeof(demoghost), PU_LEVEL, NULL); + gh->next = ghosts; + gh->buffer = buffer; + M_Memcpy(gh->checksum, md5, 16); + gh->p = p; + + ghosts = gh; + + gh->version = ghostversion; + mthing = playerstarts[0]; + I_Assert(mthing); + { // A bit more complex than P_SpawnPlayer because ghosts aren't solid and won't just push themselves out of the ceiling. + fixed_t z,f,c; + fixed_t offset = mthing->z << FRACBITS; + gh->mo = P_SpawnMobj(mthing->x << FRACBITS, mthing->y << FRACBITS, 0, MT_GHOST); + gh->mo->angle = FixedAngle(mthing->angle << FRACBITS); + f = gh->mo->floorz; + c = gh->mo->ceilingz - mobjinfo[MT_PLAYER].height; + if (!!(mthing->options & MTF_AMBUSH) ^ !!(mthing->options & MTF_OBJECTFLIP)) + { + z = c - offset; + if (z < f) + z = f; + } + else + { + z = f + offset; + if (z > c) + z = c; + } + gh->mo->z = z; + } + + gh->oldmo.x = gh->mo->x; + gh->oldmo.y = gh->mo->y; + gh->oldmo.z = gh->mo->z; + + // Set skin + gh->mo->skin = &skins[0]; + for (i = 0; i < numskins; i++) + if (!stricmp(skins[i].name,skin)) + { + gh->mo->skin = &skins[i]; + break; + } + gh->oldmo.skin = gh->mo->skin; + + // Set color + gh->mo->color = ((skin_t*)gh->mo->skin)->prefcolor; + for (i = 0; i < MAXSKINCOLORS; i++) + if (!stricmp(Color_Names[i],color)) + { + gh->mo->color = (UINT8)i; + break; + } + gh->oldmo.color = gh->mo->color; + + 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_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); + Z_Free(pdemoname); +} + +// +// G_TimeDemo +// NOTE: name is a full filename for external demos +// +static INT32 restorecv_vidwait; + +void G_TimeDemo(const char *name) +{ + nodrawers = M_CheckParm("-nodraw"); + noblit = M_CheckParm("-noblit"); + restorecv_vidwait = cv_vidwait.value; + if (cv_vidwait.value) + CV_Set(&cv_vidwait, "0"); + timingdemo = true; + singletics = true; + framecount = 0; + demostarttime = I_GetTime(); + G_DeferedPlayDemo(name); +} + +void G_DoPlayMetal(void) +{ + lumpnum_t l; + mobj_t *mo = NULL; + thinker_t *th; + + // it's an internal demo + if ((l = W_CheckNumForName(va("%sMS",G_BuildMapName(gamemap)))) == LUMPERROR) + { + CONS_Alert(CONS_WARNING, M_GetText("No bot recording for this map.\n")); + return; + } + else + metalbuffer = metal_p = W_CacheLumpNum(l, PU_STATIC); + + // find metal sonic + for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) + { + if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) + continue; + + mo = (mobj_t *)th; + if (mo->type != MT_METALSONIC_RACE) + continue; + + break; + } + if (th == &thlist[THINK_MOBJ]) + { + CONS_Alert(CONS_ERROR, M_GetText("Failed to find bot entity.\n")); + Z_Free(metalbuffer); + return; + } + + // read demo header + metal_p += 12; // DEMOHEADER + metal_p++; // VERSION + metal_p++; // SUBVERSION + metalversion = READUINT16(metal_p); + switch(metalversion) + { + case DEMOVERSION: // latest always supported + break; + // too old, cannot support. + default: + CONS_Alert(CONS_WARNING, M_GetText("Failed to load bot recording for this map, format version incompatible.\n")); + Z_Free(metalbuffer); + return; + } + metal_p += 16; // demo checksum + if (memcmp(metal_p, "METL", 4)) + { + CONS_Alert(CONS_WARNING, M_GetText("Failed to load bot recording for this map, wasn't recorded in Metal format.\n")); + Z_Free(metalbuffer); + return; + } metal_p += 4; // "METL" + + // read initial tic + memset(&oldmetal,0,sizeof(oldmetal)); + oldmetal.x = mo->x; + oldmetal.y = mo->y; + oldmetal.z = mo->z; + metalplayback = mo; +} + +void G_DoneLevelLoad(void) +{ + CONS_Printf(M_GetText("Loaded level in %f sec\n"), (double)(I_GetTime() - demostarttime) / TICRATE); + framecount = 0; + demostarttime = I_GetTime(); +} + +/* +=================== += += G_CheckDemoStatus += += Called after a death or level completion to allow demos to be cleaned up += Returns true if a new demo loop action will take place +=================== +*/ + +// Stops metal sonic's demo. Separate from other functions because metal + replays can coexist +void G_StopMetalDemo(void) +{ + + // Metal Sonic finishing doesn't end the game, dammit. + Z_Free(metalbuffer); + metalbuffer = NULL; + metalplayback = NULL; + metal_p = NULL; +} + +// Stops metal sonic recording. +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; + for (i = 0; i < 16; i++, p++) + *p = P_RandomByte(); // This MD5 was chosen by fair dice roll and most likely < 50% correct. + } +#else + 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. + } + free(demobuffer); + metalrecording = false; + if (saved) + I_Error("Saved to %sMS.LMP", G_BuildMapName(gamemap)); + I_Error("Failed to save demo!"); +} + +// reset engine variable set for the demos +// called from stopdemo command, map command, and g_checkdemoStatus. +void G_StopDemo(void) +{ + Z_Free(demobuffer); + demobuffer = NULL; + demoplayback = false; + titledemo = false; + timingdemo = false; + singletics = false; + + if (gamestate == GS_INTERMISSION) + Y_EndIntermission(); // cleanup + + G_SetGamestate(GS_NULL); + wipegamestate = GS_NULL; + SV_StopServer(); + SV_ResetServer(); +} + +boolean G_CheckDemoStatus(void) +{ + boolean saved; + + while (ghosts) + { + demoghost *next = ghosts->next; + Z_Free(ghosts); + ghosts = next; + } + ghosts = NULL; + + + // DO NOT end metal sonic demos here + + if (timingdemo) + { + INT32 demotime; + double f1, f2; + demotime = I_GetTime() - demostarttime; + if (!demotime) + return true; + G_StopDemo(); + timingdemo = false; + f1 = (double)demotime; + f2 = (double)framecount*TICRATE; + + CONS_Printf(M_GetText("timed %u gametics in %d realtics - %u frames\n%f seconds, %f avg fps\n"), + leveltime,demotime,(UINT32)framecount,f1/TICRATE,f2/f1); + + // CSV-readable timedemo results, for external parsing + if (timedemo_csv) + { + FILE *f; + const char *csvpath = va("%s"PATHSEP"%s", srb2home, "timedemo.csv"); + const char *header = "id,demoname,seconds,avgfps,leveltime,demotime,framecount,ticrate,rendermode,vidmode,vidwidth,vidheight,procbits\n"; + const char *rowformat = "\"%s\",\"%s\",%f,%f,%u,%d,%u,%u,%u,%u,%u,%u,%u\n"; + boolean headerrow = !FIL_FileExists(csvpath); + UINT8 procbits = 0; + + // Bitness + if (sizeof(void*) == 4) + procbits = 32; + else if (sizeof(void*) == 8) + procbits = 64; + + f = fopen(csvpath, "a+"); + + if (f) + { + if (headerrow) + fputs(header, f); + fprintf(f, rowformat, + timedemo_csv_id,timedemo_name,f1/TICRATE,f2/f1,leveltime,demotime,(UINT32)framecount,TICRATE,rendermode,vid.modenum,vid.width,vid.height,procbits); + fclose(f); + CONS_Printf("Timedemo results saved to '%s'\n", csvpath); + } + else + { + // Just print the CSV output to console + CON_LogMessage(header); + CONS_Printf(rowformat, + timedemo_csv_id,timedemo_name,f1/TICRATE,f2/f1,leveltime,demotime,(UINT32)framecount,TICRATE,rendermode,vid.modenum,vid.width,vid.height,procbits); + } + } + + if (restorecv_vidwait != cv_vidwait.value) + CV_SetValue(&cv_vidwait, restorecv_vidwait); + D_AdvanceDemo(); + return true; + } + + if (demoplayback) + { + if (singledemo) + I_Quit(); + G_StopDemo(); + + if (modeattacking) + M_EndModeAttackRun(); + else + D_AdvanceDemo(); + + return true; + } + + if (demorecording) + { + UINT8 *p = demobuffer+16; // checksum position +#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. +#else + WRITEUINT8(demo_p, DEMOMARKER); // add the demo end marker + md5_buffer((char *)p+16, demo_p - (p+16), p); // make a checksum of everything after the checksum in the file. +#endif + saved = FIL_WriteFile(va(pandf, srb2home, demoname), demobuffer, demo_p - demobuffer); // finally output the file. + free(demobuffer); + demorecording = false; + + if (modeattacking != ATTACKING_RECORD) + { + if (saved) + CONS_Printf(M_GetText("Demo %s recorded\n"), demoname); + else + CONS_Alert(CONS_WARNING, M_GetText("Demo %s not saved\n"), demoname); + } + return true; + } + + return false; +} diff --git a/src/g_demo.h b/src/g_demo.h new file mode 100644 index 000000000..89be6e343 --- /dev/null +++ b/src/g_demo.h @@ -0,0 +1,85 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1993-1996 by id Software, Inc. +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1999-2020 by Sonic Team Junior. +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file g_demo.h +/// \brief Demo recording and playback + +#ifndef __G_DEMO__ +#define __G_DEMO__ + +#include "doomdef.h" +#include "doomstat.h" +#include "d_event.h" + +// ====================================== +// DEMO playback/recording related stuff. +// ====================================== + +// demoplaying back and demo recording +extern boolean demoplayback, titledemo, demorecording, timingdemo; +extern tic_t demostarttime; + +// Quit after playing a demo from cmdline. +extern boolean singledemo; +extern boolean demo_start; +extern boolean demosynced; + +extern mobj_t *metalplayback; + +// Only called by startup code. +void G_RecordDemo(const char *name); +void G_RecordMetal(void); +void G_BeginRecording(void); +void G_BeginMetal(void); + +// Only called by shutdown code. +void G_SetDemoTime(UINT32 ptime, UINT32 pscore, UINT16 prings); +UINT8 G_CmpDemoTime(char *oldname, char *newname); + +typedef enum +{ + GHC_NORMAL = 0, + GHC_SUPER, + GHC_FIREFLOWER, + GHC_INVINCIBLE, + GHC_NIGHTSSKIN, // not actually a colour + GHC_RETURNSKIN // ditto +} ghostcolor_t; + +// Record/playback tics +void G_ReadDemoTiccmd(ticcmd_t *cmd, INT32 playernum); +void G_WriteDemoTiccmd(ticcmd_t *cmd, INT32 playernum); +void G_GhostAddThok(void); +void G_GhostAddSpin(void); +void G_GhostAddRev(void); +void G_GhostAddColor(ghostcolor_t color); +void G_GhostAddFlip(void); +void G_GhostAddScale(fixed_t scale); +void G_GhostAddHit(mobj_t *victim); +void G_WriteGhostTic(mobj_t *ghost); +void G_ConsGhostTic(void); +void G_GhostTicker(void); +void G_ReadMetalTic(mobj_t *metal); +void G_WriteMetalTic(mobj_t *metal); +void G_SaveMetal(UINT8 **buffer); +void G_LoadMetal(UINT8 **buffer); + +void G_DeferedPlayDemo(const char *demo); +void G_DoPlayDemo(char *defdemoname); +void G_TimeDemo(const char *name); +void G_AddGhost(char *defdemoname); +void G_DoPlayMetal(void); +void G_DoneLevelLoad(void); +void G_StopMetalDemo(void); +ATTRNORETURN void FUNCNORETURN G_StopMetalRecording(boolean kill); +void G_StopDemo(void); +boolean G_CheckDemoStatus(void); + +#endif // __G_DEMO__ diff --git a/src/g_game.c b/src/g_game.c index 58f7795b6..fb70e2995 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -27,6 +27,7 @@ #include "r_main.h" #include "s_sound.h" #include "g_game.h" +#include "g_demo.h" #include "m_cheat.h" #include "m_misc.h" #include "m_menu.h" @@ -41,11 +42,9 @@ #include "r_skins.h" #include "y_inter.h" #include "v_video.h" -#include "dehacked.h" // get_number (for ghost thok) #include "lua_hook.h" #include "b_bot.h" #include "m_cond.h" // condition sets -#include "md5.h" // demo checksums #ifdef HAVE_BLUA #include "lua_hud.h" @@ -105,11 +104,6 @@ UINT8 numDemos = 0; UINT32 demoDelayTime = 15*TICRATE; UINT32 demoIdleTime = 3*TICRATE; -boolean timingdemo; // if true, exit with report on completion -boolean nodrawers; // for comparative timing purposes -boolean noblit; // for comparative timing purposes -tic_t demostarttime; // for comparative timing purposes - boolean netgame; // only true if packets are broadcast boolean multiplayer; boolean playeringame[MAXPLAYERS]; @@ -256,57 +250,12 @@ UINT32 timesBeaten; UINT32 timesBeatenWithEmeralds; UINT32 timesBeatenUltimate; -static char demoname[64]; -boolean demorecording; -boolean demoplayback; -boolean titledemo; // Title Screen demo can be cancelled by any key -static UINT8 *demobuffer = NULL; -static UINT8 *demo_p, *demotime_p; -static UINT8 *demoend; -static UINT8 demoflags; -static UINT16 demoversion; -boolean singledemo; // quit after playing a demo from cmdline -boolean demo_start; // don't start playing demo right away -boolean demosynced = true; // console warning message - -boolean metalrecording; // recording as metal sonic -mobj_t *metalplayback; -static UINT8 *metalbuffer = NULL; -static UINT8 *metal_p; -static UINT16 metalversion; - typedef struct joystickvector2_s { INT32 xaxis; INT32 yaxis; } joystickvector2_t; -// extra data stuff (events registered this frame while recording) -static struct { - UINT8 flags; // EZT flags - - // EZT_COLOR - UINT8 color, lastcolor; - - // EZT_SCALE - fixed_t scale, lastscale; - - // EZT_HIT - UINT16 hits; - mobj_t **hitlist; -} ghostext; - -// Your naming conventions are stupid and useless. -// There is no conflict here. -typedef struct demoghost { - UINT8 checksum[16]; - UINT8 *buffer, *p, color, fadein; - UINT16 version; - mobj_t oldmo, *mo; - struct demoghost *next; -} demoghost; -demoghost *ghosts = NULL; - boolean precache = true; // if true, load all graphics at start INT16 prevmap, nextmap; @@ -1102,8 +1051,6 @@ static void G_HandleAxisDeadZone(UINT8 splitnum, joystickvector2_t *joystickvect } } - - // // G_BuildTiccmd // Builds a ticcmd from all of the available inputs @@ -1723,6 +1670,25 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) } } +ticcmd_t *G_CopyTiccmd(ticcmd_t* dest, const ticcmd_t* src, const size_t n) +{ + return M_Memcpy(dest, src, n*sizeof(*src)); +} + +ticcmd_t *G_MoveTiccmd(ticcmd_t* dest, const ticcmd_t* src, const size_t n) +{ + size_t i; + for (i = 0; i < n; i++) + { + dest[i].forwardmove = src[i].forwardmove; + dest[i].sidemove = src[i].sidemove; + dest[i].angleturn = SHORT(src[i].angleturn); + dest[i].aiming = (INT16)SHORT(src[i].aiming); + dest[i].buttons = (UINT16)SHORT(src[i].buttons); + } + return dest; +} + // User has designated that they want // analog ON, so tell the game to stop // fudging with it. @@ -4933,2432 +4899,6 @@ INT32 G_FindMapByNameOrCode(const char *mapname, char **realmapnamep) return newmapnum; } -// -// DEMO RECORDING -// - -#define DEMOVERSION 0x000c -#define DEMOHEADER "\xF0" "SRB2Replay" "\x0F" - -#define DF_GHOST 0x01 // This demo contains ghost data too! -#define DF_RECORDATTACK 0x02 // This demo is from record attack and contains its final completion time, score, and rings! -#define DF_NIGHTSATTACK 0x04 // This demo is from NiGHTS attack and contains its time left, score, and mares! -#define DF_ATTACKMASK 0x06 // This demo is from ??? attack and contains ??? -#define DF_ATTACKSHIFT 1 - -// For demos -#define ZT_FWD 0x01 -#define ZT_SIDE 0x02 -#define ZT_ANGLE 0x04 -#define ZT_BUTTONS 0x08 -#define ZT_AIMING 0x10 -#define DEMOMARKER 0x80 // demoend -#define METALDEATH 0x44 -#define METALSNICE 0x69 - -static ticcmd_t oldcmd; - -// For Metal Sonic and time attack ghosts -#define GZT_XYZ 0x01 -#define GZT_MOMXY 0x02 -#define GZT_MOMZ 0x04 -#define GZT_ANGLE 0x08 -#define GZT_FRAME 0x10 // Animation frame -#define GZT_SPR2 0x20 // Player animations -#define GZT_EXTRA 0x40 -#define GZT_FOLLOW 0x80 // Followmobj - -// GZT_EXTRA flags -#define EZT_THOK 0x01 // Spawned a thok object -#define EZT_SPIN 0x02 // Because one type of thok object apparently wasn't enough -#define EZT_REV 0x03 // And two types wasn't enough either yet -#define EZT_THOKMASK 0x03 -#define EZT_COLOR 0x04 // Changed color (Super transformation, Mario fireflowers/invulnerability, etc.) -#define EZT_FLIP 0x08 // Reversed gravity -#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) -#define EZT_HEIGHT 0x80 // Changed height - -// GZT_FOLLOW flags -#define FZT_SPAWNED 0x01 // just been spawned -#define FZT_SKIN 0x02 // has skin -#define FZT_LINKDRAW 0x04 // has linkdraw (combine with spawned only) -#define FZT_COLORIZED 0x08 // colorized (ditto) -#define FZT_SCALE 0x10 // different scale to object -// spare FZT slots 0x20 to 0x80 - -static mobj_t oldmetal, oldghost; - -void G_SaveMetal(UINT8 **buffer) -{ - I_Assert(buffer != NULL && *buffer != NULL); - - WRITEUINT32(*buffer, metal_p - metalbuffer); -} - -void G_LoadMetal(UINT8 **buffer) -{ - I_Assert(buffer != NULL && *buffer != NULL); - - G_DoPlayMetal(); - metal_p = metalbuffer + READUINT32(*buffer); -} - -ticcmd_t *G_CopyTiccmd(ticcmd_t* dest, const ticcmd_t* src, const size_t n) -{ - return M_Memcpy(dest, src, n*sizeof(*src)); -} - -ticcmd_t *G_MoveTiccmd(ticcmd_t* dest, const ticcmd_t* src, const size_t n) -{ - size_t i; - for (i = 0; i < n; i++) - { - dest[i].forwardmove = src[i].forwardmove; - dest[i].sidemove = src[i].sidemove; - dest[i].angleturn = SHORT(src[i].angleturn); - dest[i].aiming = (INT16)SHORT(src[i].aiming); - dest[i].buttons = (UINT16)SHORT(src[i].buttons); - } - return dest; -} - -void G_ReadDemoTiccmd(ticcmd_t *cmd, INT32 playernum) -{ - UINT8 ziptic; - (void)playernum; - - if (!demo_p || !demo_start) - return; - ziptic = READUINT8(demo_p); - - if (ziptic & ZT_FWD) - oldcmd.forwardmove = READSINT8(demo_p); - if (ziptic & ZT_SIDE) - oldcmd.sidemove = READSINT8(demo_p); - if (ziptic & ZT_ANGLE) - oldcmd.angleturn = READINT16(demo_p); - if (ziptic & ZT_BUTTONS) - oldcmd.buttons = (oldcmd.buttons & (BT_CAMLEFT|BT_CAMRIGHT)) | (READUINT16(demo_p) & ~(BT_CAMLEFT|BT_CAMRIGHT)); - if (ziptic & ZT_AIMING) - oldcmd.aiming = READINT16(demo_p); - - G_CopyTiccmd(cmd, &oldcmd, 1); - - if (!(demoflags & DF_GHOST) && *demo_p == DEMOMARKER) - { - // end of demo data stream - G_CheckDemoStatus(); - return; - } -} - -void G_WriteDemoTiccmd(ticcmd_t *cmd, INT32 playernum) -{ - char ziptic = 0; - UINT8 *ziptic_p; - (void)playernum; - - if (!demo_p) - return; - ziptic_p = demo_p++; // the ziptic, written at the end of this function - - if (cmd->forwardmove != oldcmd.forwardmove) - { - WRITEUINT8(demo_p,cmd->forwardmove); - oldcmd.forwardmove = cmd->forwardmove; - ziptic |= ZT_FWD; - } - - if (cmd->sidemove != oldcmd.sidemove) - { - WRITEUINT8(demo_p,cmd->sidemove); - oldcmd.sidemove = cmd->sidemove; - ziptic |= ZT_SIDE; - } - - if (cmd->angleturn != oldcmd.angleturn) - { - WRITEINT16(demo_p,cmd->angleturn); - oldcmd.angleturn = cmd->angleturn; - ziptic |= ZT_ANGLE; - } - - if (cmd->buttons != oldcmd.buttons) - { - WRITEUINT16(demo_p,cmd->buttons); - oldcmd.buttons = cmd->buttons; - ziptic |= ZT_BUTTONS; - } - - if (cmd->aiming != oldcmd.aiming) - { - WRITEINT16(demo_p,cmd->aiming); - oldcmd.aiming = cmd->aiming; - ziptic |= ZT_AIMING; - } - - *ziptic_p = ziptic; - - // attention here for the ticcmd size! - // latest demos with mouse aiming byte in ticcmd - if (!(demoflags & DF_GHOST) && ziptic_p > demoend - 9) - { - G_CheckDemoStatus(); // no more space - return; - } -} - -void G_GhostAddThok(void) -{ - if (!metalrecording && (!demorecording || !(demoflags & DF_GHOST))) - return; - ghostext.flags = (ghostext.flags & ~EZT_THOKMASK) | EZT_THOK; -} - -void G_GhostAddSpin(void) -{ - if (!metalrecording && (!demorecording || !(demoflags & DF_GHOST))) - return; - ghostext.flags = (ghostext.flags & ~EZT_THOKMASK) | EZT_SPIN; -} - -void G_GhostAddRev(void) -{ - if (!metalrecording && (!demorecording || !(demoflags & DF_GHOST))) - return; - ghostext.flags = (ghostext.flags & ~EZT_THOKMASK) | EZT_REV; -} - -void G_GhostAddFlip(void) -{ - if (!metalrecording && (!demorecording || !(demoflags & DF_GHOST))) - return; - ghostext.flags |= EZT_FLIP; -} - -void G_GhostAddColor(ghostcolor_t color) -{ - if (!demorecording || !(demoflags & DF_GHOST)) - return; - if (ghostext.lastcolor == (UINT8)color) - { - ghostext.flags &= ~EZT_COLOR; - return; - } - ghostext.flags |= EZT_COLOR; - ghostext.color = (UINT8)color; -} - -void G_GhostAddScale(fixed_t scale) -{ - if (!metalrecording && (!demorecording || !(demoflags & DF_GHOST))) - return; - if (ghostext.lastscale == scale) - { - ghostext.flags &= ~EZT_SCALE; - return; - } - ghostext.flags |= EZT_SCALE; - ghostext.scale = scale; -} - -void G_GhostAddHit(mobj_t *victim) -{ - if (!demorecording || !(demoflags & DF_GHOST)) - return; - ghostext.flags |= EZT_HIT; - ghostext.hits++; - ghostext.hitlist = Z_Realloc(ghostext.hitlist, ghostext.hits * sizeof(mobj_t *), PU_LEVEL, NULL); - ghostext.hitlist[ghostext.hits-1] = victim; -} - -void G_WriteGhostTic(mobj_t *ghost) -{ - char ziptic = 0; - UINT8 *ziptic_p; - UINT32 i; - fixed_t height; - - if (!demo_p) - return; - if (!(demoflags & DF_GHOST)) - return; // No ghost data to write. - - ziptic_p = demo_p++; // the ziptic, written at the end of this function - - #define MAXMOM (0xFFFF<<8) - - // GZT_XYZ is only useful if you've moved 256 FRACUNITS or more in a single tic. - if (abs(ghost->x-oldghost.x) > MAXMOM - || abs(ghost->y-oldghost.y) > MAXMOM - || abs(ghost->z-oldghost.z) > MAXMOM) - { - oldghost.x = ghost->x; - oldghost.y = ghost->y; - oldghost.z = ghost->z; - ziptic |= GZT_XYZ; - WRITEFIXED(demo_p,oldghost.x); - WRITEFIXED(demo_p,oldghost.y); - WRITEFIXED(demo_p,oldghost.z); - } - else - { - // For moving normally: - // Store one full byte of movement, plus one byte of fractional movement. - INT16 momx = (INT16)((ghost->x-oldghost.x)>>8); - INT16 momy = (INT16)((ghost->y-oldghost.y)>>8); - if (momx != oldghost.momx - || momy != oldghost.momy) - { - oldghost.momx = momx; - oldghost.momy = momy; - ziptic |= GZT_MOMXY; - WRITEINT16(demo_p,momx); - WRITEINT16(demo_p,momy); - } - momx = (INT16)((ghost->z-oldghost.z)>>8); - if (momx != oldghost.momz) - { - oldghost.momz = momx; - ziptic |= GZT_MOMZ; - WRITEINT16(demo_p,momx); - } - - // This SHOULD set oldghost.x/y/z to match ghost->x/y/z - // but it keeps the fractional loss of one byte, - // so it will hopefully be made up for in future tics. - oldghost.x += oldghost.momx<<8; - oldghost.y += oldghost.momy<<8; - oldghost.z += oldghost.momz<<8; - } - - #undef MAXMOM - - // Only store the 8 most relevant bits of angle - // because exact values aren't too easy to discern to begin with when only 8 angles have different sprites - // and it does not affect this mode of movement at all anyway. - if (ghost->player && ghost->player->drawangle>>24 != oldghost.angle) - { - oldghost.angle = ghost->player->drawangle>>24; - ziptic |= GZT_ANGLE; - WRITEUINT8(demo_p,oldghost.angle); - } - - // Store the sprite frame. - if ((ghost->frame & FF_FRAMEMASK) != oldghost.frame) - { - oldghost.frame = (ghost->frame & FF_FRAMEMASK); - ziptic |= GZT_FRAME; - WRITEUINT8(demo_p,oldghost.frame); - } - - if (ghost->sprite == SPR_PLAY - && ghost->sprite2 != oldghost.sprite2) - { - oldghost.sprite2 = ghost->sprite2; - ziptic |= GZT_SPR2; - WRITEUINT8(demo_p,oldghost.sprite2); - } - - // Check for sprite set changes - if (ghost->sprite != oldghost.sprite) - { - oldghost.sprite = ghost->sprite; - 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; - - if (ghostext.color == ghostext.lastcolor) - ghostext.flags &= ~EZT_COLOR; - if (ghostext.scale == ghostext.lastscale) - ghostext.flags &= ~EZT_SCALE; - - WRITEUINT8(demo_p,ghostext.flags); - if (ghostext.flags & EZT_COLOR) - { - WRITEUINT8(demo_p,ghostext.color); - ghostext.lastcolor = ghostext.color; - } - if (ghostext.flags & EZT_SCALE) - { - WRITEFIXED(demo_p,ghostext.scale); - ghostext.lastscale = ghostext.scale; - } - if (ghostext.flags & EZT_HIT) - { - WRITEUINT16(demo_p,ghostext.hits); - for (i = 0; i < ghostext.hits; i++) - { - mobj_t *mo = ghostext.hitlist[i]; - //WRITEUINT32(demo_p,UINT32_MAX); // reserved for some method of determining exactly which mobj this is. (mobjnum doesn't work here.) - WRITEUINT32(demo_p,mo->type); - WRITEUINT16(demo_p,(UINT16)mo->health); - WRITEFIXED(demo_p,mo->x); - WRITEFIXED(demo_p,mo->y); - WRITEFIXED(demo_p,mo->z); - WRITEANGLE(demo_p,mo->angle); - } - Z_Free(ghostext.hitlist); - ghostext.hits = 0; - ghostext.hitlist = NULL; - } - 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 && !(ghost->player->followmobj->sprite == SPR_NULL || (ghost->player->followmobj->flags2 & MF2_DONTDRAW))) // bloats tails runs but what can ya do - { - INT16 temp; - UINT8 *followtic_p = demo_p++; - UINT8 followtic = 0; - - ziptic |= GZT_FOLLOW; - - if (ghost->player->followmobj->skin) - followtic |= FZT_SKIN; - - if (!(oldghost.flags2 & MF2_AMBUSH)) - { - followtic |= FZT_SPAWNED; - WRITEINT16(demo_p,ghost->player->followmobj->info->height>>FRACBITS); - if (ghost->player->followmobj->flags2 & MF2_LINKDRAW) - followtic |= FZT_LINKDRAW; - if (ghost->player->followmobj->colorized) - followtic |= FZT_COLORIZED; - if (followtic & FZT_SKIN) - WRITEUINT8(demo_p,(UINT8)(((skin_t *)(ghost->player->followmobj->skin))-skins)); - oldghost.flags2 |= MF2_AMBUSH; - } - - if (ghost->player->followmobj->scale != ghost->scale) - { - followtic |= FZT_SCALE; - WRITEFIXED(demo_p,ghost->player->followmobj->scale); - } - - 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); - 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); - - *followtic_p = followtic; - } - else - oldghost.flags2 &= ~MF2_AMBUSH; - - *ziptic_p = ziptic; - - // attention here for the ticcmd size! - // latest demos with mouse aiming byte in ticcmd - if (demo_p >= demoend - (13 + 9 + 9)) - { - G_CheckDemoStatus(); // no more space - return; - } -} - -// Uses ghost data to do consistency checks on your position. -// This fixes desynchronising demos when fighting eggman. -void G_ConsGhostTic(void) -{ - UINT8 ziptic; - UINT16 px,py,pz,gx,gy,gz; - mobj_t *testmo; - - if (!demo_p || !demo_start) - return; - if (!(demoflags & DF_GHOST)) - return; // No ghost data to use. - - testmo = players[0].mo; - - // Grab ghost data. - ziptic = READUINT8(demo_p); - if (ziptic & GZT_XYZ) - { - oldghost.x = READFIXED(demo_p); - oldghost.y = READFIXED(demo_p); - oldghost.z = READFIXED(demo_p); - } - else - { - if (ziptic & GZT_MOMXY) - { - oldghost.momx = READINT16(demo_p)<<8; - oldghost.momy = READINT16(demo_p)<<8; - } - if (ziptic & GZT_MOMZ) - oldghost.momz = READINT16(demo_p)<<8; - oldghost.x += oldghost.momx; - oldghost.y += oldghost.momy; - oldghost.z += oldghost.momz; - } - if (ziptic & GZT_ANGLE) - demo_p++; - if (ziptic & GZT_FRAME) - demo_p++; - if (ziptic & GZT_SPR2) - demo_p++; - - if (ziptic & GZT_EXTRA) - { // But wait, there's more! - UINT8 xziptic = READUINT8(demo_p); - if (xziptic & EZT_COLOR) - demo_p++; - if (xziptic & EZT_SCALE) - demo_p += sizeof(fixed_t); - if (xziptic & EZT_HIT) - { // Resync mob damage. - UINT16 i, count = READUINT16(demo_p); - thinker_t *th; - mobj_t *mobj; - - UINT32 type; - UINT16 health; - fixed_t x; - fixed_t y; - fixed_t z; - - for (i = 0; i < count; i++) - { - //demo_p += 4; // reserved. - type = READUINT32(demo_p); - health = READUINT16(demo_p); - x = READFIXED(demo_p); - y = READFIXED(demo_p); - z = READFIXED(demo_p); - demo_p += sizeof(angle_t); // angle, unnecessary for cons. - - mobj = NULL; - for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) - { - if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) - continue; - mobj = (mobj_t *)th; - if (mobj->type == (mobjtype_t)type && mobj->x == x && mobj->y == y && mobj->z == z) - break; - } - if (th != &thlist[THINK_MOBJ] && mobj->health != health) // Wasn't damaged?! This is desync! Fix it! - { - if (demosynced) - CONS_Alert(CONS_WARNING, M_GetText("Demo playback has desynced!\n")); - demosynced = false; - P_DamageMobj(mobj, players[0].mo, players[0].mo, 1, 0); - } - } - } - 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) - { - 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); - if (followtic & FZT_SKIN) - demo_p++; - demo_p += sizeof(UINT16); - demo_p++; - demo_p++; - } - - // Re-synchronise - px = testmo->x>>FRACBITS; - py = testmo->y>>FRACBITS; - pz = testmo->z>>FRACBITS; - gx = oldghost.x>>FRACBITS; - gy = oldghost.y>>FRACBITS; - gz = oldghost.z>>FRACBITS; - - if (px != gx || py != gy || pz != gz) - { - if (demosynced) - CONS_Alert(CONS_WARNING, M_GetText("Demo playback has desynced!\n")); - demosynced = false; - - P_UnsetThingPosition(testmo); - testmo->x = oldghost.x; - testmo->y = oldghost.y; - P_SetThingPosition(testmo); - testmo->z = oldghost.z; - } - - if (*demo_p == DEMOMARKER) - { - // end of demo data stream - G_CheckDemoStatus(); - return; - } -} - -void G_GhostTicker(void) -{ - demoghost *g,*p; - for(g = ghosts, p = NULL; g; g = g->next) - { - // Skip normal demo data. - UINT8 ziptic = READUINT8(g->p); - UINT8 xziptic = 0; - if (ziptic & ZT_FWD) - g->p++; - if (ziptic & ZT_SIDE) - g->p++; - if (ziptic & ZT_ANGLE) - g->p += 2; - if (ziptic & ZT_BUTTONS) - g->p += 2; - if (ziptic & ZT_AIMING) - g->p += 2; - - // Grab ghost data. - ziptic = READUINT8(g->p); - if (ziptic & GZT_XYZ) - { - g->oldmo.x = READFIXED(g->p); - g->oldmo.y = READFIXED(g->p); - g->oldmo.z = READFIXED(g->p); - } - else - { - if (ziptic & GZT_MOMXY) - { - g->oldmo.momx = READINT16(g->p)<<8; - g->oldmo.momy = READINT16(g->p)<<8; - } - if (ziptic & GZT_MOMZ) - g->oldmo.momz = READINT16(g->p)<<8; - g->oldmo.x += g->oldmo.momx; - g->oldmo.y += g->oldmo.momy; - g->oldmo.z += g->oldmo.momz; - } - if (ziptic & GZT_ANGLE) - g->mo->angle = READUINT8(g->p)<<24; - if (ziptic & GZT_FRAME) - g->oldmo.frame = READUINT8(g->p); - if (ziptic & GZT_SPR2) - g->oldmo.sprite2 = READUINT8(g->p); - - // Update ghost - P_UnsetThingPosition(g->mo); - g->mo->x = g->oldmo.x; - g->mo->y = g->oldmo.y; - 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) - { // But wait, there's more! - xziptic = READUINT8(g->p); - if (xziptic & EZT_COLOR) - { - g->color = READUINT8(g->p); - switch(g->color) - { - default: - case GHC_RETURNSKIN: - g->mo->skin = g->oldmo.skin; - /* FALLTHRU */ - case GHC_NORMAL: // Go back to skin color - g->mo->color = g->oldmo.color; - break; - // Handled below - case GHC_SUPER: - case GHC_INVINCIBLE: - break; - case GHC_FIREFLOWER: // Fireflower - g->mo->color = SKINCOLOR_WHITE; - break; - case GHC_NIGHTSSKIN: // not actually a colour - g->mo->skin = &skins[DEFAULTNIGHTSSKIN]; - break; - } - } - if (xziptic & EZT_FLIP) - g->mo->eflags ^= MFE_VERTICALFLIP; - if (xziptic & EZT_SCALE) - { - g->mo->destscale = READFIXED(g->p); - if (g->mo->destscale != g->mo->scale) - P_SetScale(g->mo, g->mo->destscale); - } - if (xziptic & EZT_THOKMASK) - { // Let's only spawn ONE of these per frame, thanks. - mobj_t *mobj; - INT32 type = -1; - if (g->mo->skin) - { - skin_t *skin = (skin_t *)g->mo->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(g->mo); // does a large portion of the work for us - mobj->frame = (mobj->frame & ~FF_FRAMEMASK)|tr_trans60<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); - } - } - if (xziptic & EZT_HIT) - { // Spawn hit poofs for killing things! - UINT16 i, count = READUINT16(g->p), health; - UINT32 type; - fixed_t x,y,z; - angle_t angle; - mobj_t *poof; - for (i = 0; i < count; i++) - { - //g->p += 4; // reserved - type = READUINT32(g->p); - health = READUINT16(g->p); - x = READFIXED(g->p); - y = READFIXED(g->p); - z = READFIXED(g->p); - angle = READANGLE(g->p); - if (!(mobjinfo[type].flags & MF_SHOOTABLE) - || !(mobjinfo[type].flags & (MF_ENEMY|MF_MONITOR)) - || health != 0 || i >= 4) // only spawn for the first 4 hits per frame, to prevent ghosts from splode-spamming too bad. - continue; - poof = P_SpawnMobj(x, y, z, MT_GHOST); - poof->angle = angle; - poof->flags = MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY; // make an ATTEMPT to curb crazy SOCs fucking stuff up... - poof->health = 0; - P_SetMobjStateNF(poof, S_XPLD1); - } - } - 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) - switch(g->color) - { - case GHC_SUPER: // Super (P_DoSuperStuff) - if (g->mo->skin) - { - skin_t *skin = (skin_t *)g->mo->skin; - g->mo->color = skin->supercolor; - } - else - g->mo->color = SKINCOLOR_SUPERGOLD1; - g->mo->color += abs( ( (signed)( (unsigned)leveltime >> 1 ) % 9) - 4); - break; - case GHC_INVINCIBLE: // Mario invincibility (P_CheckInvincibilityTimer) - g->mo->color = (UINT8)(SKINCOLOR_RUBY + (leveltime % (MAXSKINCOLORS - SKINCOLOR_RUBY))); // Passes through all saturated colours - break; - default: - break; - } - -#define follow g->mo->tracer - 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_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; - - if (followtic & FZT_COLORIZED) - follow->colorized = true; - - if (followtic & FZT_SKIN) - follow->skin = &skins[READUINT8(g->p)]; - } - if (follow) - { - if (followtic & FZT_SCALE) - follow->destscale = READFIXED(g->p); - else - follow->destscale = g->mo->destscale; - if (follow->destscale != follow->scale) - P_SetScale(follow, follow->destscale); - - P_UnsetThingPosition(follow); - temp = READINT16(g->p)<<8; - follow->x = g->mo->x + temp; - temp = READINT16(g->p)<<8; - follow->y = g->mo->y + temp; - temp = READINT16(g->p)<<8; - follow->z = g->mo->z + temp; - P_SetThingPosition(follow); - if (followtic & FZT_SKIN) - follow->sprite2 = READUINT8(g->p); - else - follow->sprite2 = 0; - follow->sprite = READUINT16(g->p); - follow->frame = (READUINT8(g->p)) | (g->mo->frame & FF_TRANSMASK); - follow->angle = g->mo->angle; - follow->color = READUINT8(g->p); - - if (!(followtic & FZT_SPAWNED)) - { - if (xziptic & EZT_FLIP) - { - follow->flags2 ^= MF2_OBJECTFLIP; - follow->eflags ^= MFE_VERTICALFLIP; - } - } - } - } - else if (follow) - { - P_RemoveMobj(follow); - P_SetTarget(&follow, NULL); - } - // 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 - ghosts = g->next; - Z_Free(g); - continue; - } - p = g; -#undef follow - } -} - -void G_ReadMetalTic(mobj_t *metal) -{ - UINT8 ziptic; - UINT8 xziptic = 0; - - if (!metal_p) - return; - - if (!metal->health) - { - G_StopMetalDemo(); - return; - } - - switch (*metal_p) - { - case METALSNICE: - break; - case METALDEATH: - if (metal->tracer) - P_RemoveMobj(metal->tracer); - P_KillMobj(metal, NULL, NULL, 0); - /* FALLTHRU */ - case DEMOMARKER: - default: - // end of demo data stream - G_StopMetalDemo(); - return; - } - metal_p++; - - ziptic = READUINT8(metal_p); - - // Read changes from the tic - if (ziptic & GZT_XYZ) - { - P_TeleportMove(metal, READFIXED(metal_p), READFIXED(metal_p), READFIXED(metal_p)); - oldmetal.x = metal->x; - oldmetal.y = metal->y; - oldmetal.z = metal->z; - } - else - { - if (ziptic & GZT_MOMXY) - { - oldmetal.momx = READINT16(metal_p)<<8; - oldmetal.momy = READINT16(metal_p)<<8; - } - if (ziptic & GZT_MOMZ) - oldmetal.momz = READINT16(metal_p)<<8; - oldmetal.x += oldmetal.momx; - oldmetal.y += oldmetal.momy; - oldmetal.z += oldmetal.momz; - } - if (ziptic & GZT_ANGLE) - metal->angle = READUINT8(metal_p)<<24; - if (ziptic & GZT_FRAME) - oldmetal.frame = READUINT32(metal_p); - if (ziptic & GZT_SPR2) - oldmetal.sprite2 = READUINT8(metal_p); - - // Set movement, position, and angle - // oldmetal contains where you're supposed to be. - metal->momx = oldmetal.momx; - metal->momy = oldmetal.momy; - metal->momz = oldmetal.momz; - P_UnsetThingPosition(metal); - metal->x = oldmetal.x; - 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! - xziptic = READUINT8(metal_p); - if (xziptic & EZT_FLIP) - { - metal->eflags ^= MFE_VERTICALFLIP; - metal->flags2 ^= MF2_OBJECTFLIP; - } - if (xziptic & EZT_SCALE) - { - metal->destscale = READFIXED(metal_p); - if (metal->destscale != metal->scale) - P_SetScale(metal, metal->destscale); - } - if (xziptic & EZT_THOKMASK) - { // Let's only spawn ONE of these per frame, thanks. - mobj_t *mobj; - INT32 type = -1; - if (metal->skin) - { - skin_t *skin = (skin_t *)metal->skin; - switch (xziptic & EZT_THOKMASK) - { - case EZT_THOK: - type = skin->thokitem < 0 ? (UINT32)mobjinfo[MT_PLAYER].painchance : (UINT32)skin->thokitem; - break; - case EZT_SPIN: - type = skin->spinitem < 0 ? (UINT32)mobjinfo[MT_PLAYER].damage : (UINT32)skin->spinitem; - break; - case EZT_REV: - type = skin->revitem < 0 ? (UINT32)mobjinfo[MT_PLAYER].raisestate : (UINT32)skin->revitem; - break; - } - } - if (type != MT_NULL) - { - if (type == MT_GHOST) - { - mobj = P_SpawnGhostMobj(metal); // does a large portion of the work for us - } - else - { - mobj = P_SpawnMobjFromMobj(metal, 0, 0, -FixedDiv(FixedMul(metal->info->height, metal->scale) - metal->height,3*FRACUNIT), MT_THOK); - mobj->sprite = states[mobjinfo[type].spawnstate].sprite; - mobj->frame = states[mobjinfo[type].spawnstate].frame; - mobj->angle = metal->angle; - mobj->color = metal->color; - mobj->skin = metal->skin; - P_SetScale(mobj, (mobj->destscale = metal->scale)); - - 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_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; - - if (followtic & FZT_SKIN) - follow->skin = &skins[READUINT8(metal_p)]; - } - if (follow) - { - if (followtic & FZT_SCALE) - follow->destscale = READFIXED(metal_p); - else - follow->destscale = metal->destscale; - if (follow->destscale != follow->scale) - P_SetScale(follow, follow->destscale); - - P_UnsetThingPosition(follow); - temp = READINT16(metal_p)<<8; - follow->x = metal->x + temp; - temp = READINT16(metal_p)<<8; - follow->y = metal->y + temp; - temp = READINT16(metal_p)<<8; - follow->z = metal->z + temp; - P_SetThingPosition(follow); - if (followtic & FZT_SKIN) - follow->sprite2 = READUINT8(metal_p); - else - follow->sprite2 = 0; - follow->sprite = READUINT16(metal_p); - follow->frame = READUINT32(metal_p); // NOT & FF_FRAMEMASK here, so 32 bits - follow->angle = metal->angle; - follow->color = READUINT8(metal_p); - - if (!(followtic & FZT_SPAWNED)) - { - if (xziptic & EZT_FLIP) - { - follow->flags2 ^= MF2_OBJECTFLIP; - follow->eflags ^= MFE_VERTICALFLIP; - } - } - } - } - else if (follow) - { - P_RemoveMobj(follow); - P_SetTarget(&follow, NULL); - } -#undef follow -} - -void G_WriteMetalTic(mobj_t *metal) -{ - UINT8 ziptic = 0; - UINT8 *ziptic_p; - fixed_t height; - - if (!demo_p) // demo_p will be NULL until the race start linedef executor is activated! - return; - - WRITEUINT8(demo_p, METALSNICE); - ziptic_p = demo_p++; // the ziptic, written at the end of this function - - #define MAXMOM (0xFFFF<<8) - - // GZT_XYZ is only useful if you've moved 256 FRACUNITS or more in a single tic. - if (abs(metal->x-oldmetal.x) > MAXMOM - || abs(metal->y-oldmetal.y) > MAXMOM - || abs(metal->z-oldmetal.z) > MAXMOM) - { - 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); - } - else - { - // For moving normally: - // Store one full byte of movement, plus one byte of fractional movement. - INT16 momx = (INT16)((metal->x-oldmetal.x)>>8); - INT16 momy = (INT16)((metal->y-oldmetal.y)>>8); - if (momx != oldmetal.momx - || momy != oldmetal.momy) - { - oldmetal.momx = momx; - oldmetal.momy = momy; - ziptic |= GZT_MOMXY; - WRITEINT16(demo_p,momx); - WRITEINT16(demo_p,momy); - } - momx = (INT16)((metal->z-oldmetal.z)>>8); - if (momx != oldmetal.momz) - { - oldmetal.momz = momx; - ziptic |= GZT_MOMZ; - WRITEINT16(demo_p,momx); - } - - // This SHOULD set oldmetal.x/y/z to match metal->x/y/z - // but it keeps the fractional loss of one byte, - // so it will hopefully be made up for in future tics. - oldmetal.x += oldmetal.momx<<8; - oldmetal.y += oldmetal.momy<<8; - oldmetal.z += oldmetal.momz<<8; - } - - #undef MAXMOM - - // Only store the 8 most relevant bits of angle - // because exact values aren't too easy to discern to begin with when only 8 angles have different sprites - // and it does not affect movement at all anyway. - if (metal->player && metal->player->drawangle>>24 != oldmetal.angle) - { - oldmetal.angle = metal->player->drawangle>>24; - ziptic |= GZT_ANGLE; - WRITEUINT8(demo_p,oldmetal.angle); - } - - // 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); - } - - // Check for sprite set changes - if (metal->sprite != oldmetal.sprite) - { - oldmetal.sprite = metal->sprite; - ghostext.flags |= EZT_SPRITE; - } - - if ((height = FixedDiv(metal->height, metal->scale)) != oldmetal.height) - { - oldmetal.height = height; - ghostext.flags |= EZT_HEIGHT; - } - - if (ghostext.flags & ~(EZT_COLOR|EZT_HIT)) // these two aren't handled by metal ever - { - ziptic |= GZT_EXTRA; - - if (ghostext.scale == ghostext.lastscale) - ghostext.flags &= ~EZT_SCALE; - - WRITEUINT8(demo_p,ghostext.flags); - if (ghostext.flags & EZT_SCALE) - { - WRITEFIXED(demo_p,ghostext.scale); - ghostext.lastscale = ghostext.scale; - } - if (ghostext.flags & EZT_SPRITE) - WRITEUINT16(demo_p,oldmetal.sprite); - if (ghostext.flags & EZT_HEIGHT) - { - height >>= FRACBITS; - WRITEINT16(demo_p, height); - } - ghostext.flags = 0; - } - - if (metal->player && metal->player->followmobj && !(metal->player->followmobj->sprite == SPR_NULL || (metal->player->followmobj->flags2 & MF2_DONTDRAW))) - { - INT16 temp; - UINT8 *followtic_p = demo_p++; - UINT8 followtic = 0; - - ziptic |= GZT_FOLLOW; - - if (metal->player->followmobj->skin) - followtic |= FZT_SKIN; - - if (!(oldmetal.flags2 & MF2_AMBUSH)) - { - followtic |= FZT_SPAWNED; - WRITEINT16(demo_p,metal->player->followmobj->info->height>>FRACBITS); - if (metal->player->followmobj->flags2 & MF2_LINKDRAW) - followtic |= FZT_LINKDRAW; - if (metal->player->followmobj->colorized) - followtic |= FZT_COLORIZED; - if (followtic & FZT_SKIN) - WRITEUINT8(demo_p,(UINT8)(((skin_t *)(metal->player->followmobj->skin))-skins)); - oldmetal.flags2 |= MF2_AMBUSH; - } - - if (metal->player->followmobj->scale != metal->scale) - { - followtic |= FZT_SCALE; - WRITEFIXED(demo_p,metal->player->followmobj->scale); - } - - temp = (INT16)((metal->player->followmobj->x-metal->x)>>8); - WRITEINT16(demo_p,temp); - temp = (INT16)((metal->player->followmobj->y-metal->y)>>8); - WRITEINT16(demo_p,temp); - temp = (INT16)((metal->player->followmobj->z-metal->z)>>8); - WRITEINT16(demo_p,temp); - if (followtic & FZT_SKIN) - WRITEUINT8(demo_p,metal->player->followmobj->sprite2); - WRITEUINT16(demo_p,metal->player->followmobj->sprite); - WRITEUINT32(demo_p,metal->player->followmobj->frame); // NOT & FF_FRAMEMASK here, so 32 bits - WRITEUINT8(demo_p,metal->player->followmobj->color); - - *followtic_p = followtic; - } - else - oldmetal.flags2 &= ~MF2_AMBUSH; - - *ziptic_p = ziptic; - - // attention here for the ticcmd size! - // latest demos with mouse aiming byte in ticcmd - if (demo_p >= demoend - 32) - { - G_StopMetalRecording(false); // no more space - return; - } -} - -// -// G_RecordDemo -// -void G_RecordDemo(const char *name) -{ - INT32 maxsize; - - strcpy(demoname, name); - strcat(demoname, ".lmp"); - maxsize = 1024*1024; - if (M_CheckParm("-maxdemo") && M_IsNextParm()) - maxsize = atoi(M_GetNextParm()) * 1024; -// if (demobuffer) -// free(demobuffer); - demo_p = NULL; - demobuffer = malloc(maxsize); - demoend = demobuffer + maxsize; - - demorecording = true; -} - -void G_RecordMetal(void) -{ - INT32 maxsize; - maxsize = 1024*1024; - if (M_CheckParm("-maxdemo") && M_IsNextParm()) - maxsize = atoi(M_GetNextParm()) * 1024; - demo_p = NULL; - demobuffer = malloc(maxsize); - demoend = demobuffer + maxsize; - metalrecording = true; -} - -void G_BeginRecording(void) -{ - UINT8 i; - char name[16]; - player_t *player = &players[consoleplayer]; - - if (demo_p) - return; - memset(name,0,sizeof(name)); - - demo_p = demobuffer; - demoflags = DF_GHOST|(modeattacking<>DF_ATTACKSHIFT) - { - case ATTACKING_NONE: // 0 - break; - case ATTACKING_RECORD: // 1 - demotime_p = demo_p; - WRITEUINT32(demo_p,UINT32_MAX); // time - WRITEUINT32(demo_p,0); // score - WRITEUINT16(demo_p,0); // rings - break; - case ATTACKING_NIGHTS: // 2 - demotime_p = demo_p; - WRITEUINT32(demo_p,UINT32_MAX); // time - WRITEUINT32(demo_p,0); // score - break; - default: // 3 - break; - } - - WRITEUINT32(demo_p,P_GetInitSeed()); - - // Name - for (i = 0; i < 16 && cv_playername.string[i]; i++) - name[i] = cv_playername.string[i]; - for (; i < 16; i++) - name[i] = '\0'; - M_Memcpy(demo_p,name,16); - demo_p += 16; - - // Skin - for (i = 0; i < 16 && cv_skin.string[i]; i++) - name[i] = cv_skin.string[i]; - for (; i < 16; i++) - name[i] = '\0'; - M_Memcpy(demo_p,name,16); - demo_p += 16; - - // Color - for (i = 0; i < 16 && cv_playercolor.string[i]; i++) - name[i] = cv_playercolor.string[i]; - for (; i < 16; i++) - name[i] = '\0'; - M_Memcpy(demo_p,name,16); - demo_p += 16; - - // Stats - WRITEUINT8(demo_p,player->charability); - WRITEUINT8(demo_p,player->charability2); - WRITEUINT8(demo_p,player->actionspd>>FRACBITS); - WRITEUINT8(demo_p,player->mindash>>FRACBITS); - WRITEUINT8(demo_p,player->maxdash>>FRACBITS); - WRITEUINT8(demo_p,player->normalspeed>>FRACBITS); - WRITEUINT8(demo_p,player->runspeed>>FRACBITS); - WRITEUINT8(demo_p,player->thrustfactor); - WRITEUINT8(demo_p,player->accelstart); - WRITEUINT8(demo_p,player->acceleration); - WRITEUINT8(demo_p,player->height>>FRACBITS); - WRITEUINT8(demo_p,player->spinheight>>FRACBITS); - WRITEUINT8(demo_p,player->camerascale>>FRACBITS); - WRITEUINT8(demo_p,player->shieldscale>>FRACBITS); - - // Trying to convert it back to % causes demo desync due to precision loss. - // Don't do it. - WRITEFIXED(demo_p, player->jumpfactor); - - // And mobjtype_t is best with UINT32 too... - WRITEUINT32(demo_p, player->followitem); - - // Save pflag data - see SendWeaponPref() - { - UINT8 buf = 0; - pflags_t pflags = 0; - if (cv_flipcam.value) - { - buf |= 0x01; - pflags |= PF_FLIPCAM; - } - if (cv_analog[0].value) - { - buf |= 0x02; - pflags |= PF_ANALOGMODE; - } - if (cv_directionchar[0].value) - { - buf |= 0x04; - 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 - CV_SaveNetVars(&demo_p); - - memset(&oldcmd,0,sizeof(oldcmd)); - memset(&oldghost,0,sizeof(oldghost)); - memset(&ghostext,0,sizeof(ghostext)); - ghostext.lastcolor = ghostext.color = GHC_NORMAL; - ghostext.lastscale = ghostext.scale = FRACUNIT; - - if (player->mo) - { - oldghost.x = player->mo->x; - oldghost.y = player->mo->y; - oldghost.z = player->mo->z; - oldghost.angle = player->mo->angle>>24; - - // preticker started us gravity flipped - if (player->mo->eflags & MFE_VERTICALFLIP) - ghostext.flags |= EZT_FLIP; - } -} - -void G_BeginMetal(void) -{ - mobj_t *mo = players[consoleplayer].mo; - -#if 0 - if (demo_p) - return; -#endif - - demo_p = demobuffer; - - // Write header. - M_Memcpy(demo_p, DEMOHEADER, 12); demo_p += 12; - WRITEUINT8(demo_p,VERSION); - WRITEUINT8(demo_p,SUBVERSION); - WRITEUINT16(demo_p,DEMOVERSION); - - // demo checksum - demo_p += 16; - - 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; - oldmetal.y = mo->y; - oldmetal.z = mo->z; - oldmetal.angle = mo->angle>>24; -} - -void G_SetDemoTime(UINT32 ptime, UINT32 pscore, UINT16 prings) -{ - if (!demorecording || !demotime_p) - return; - if (demoflags & DF_RECORDATTACK) - { - WRITEUINT32(demotime_p, ptime); - WRITEUINT32(demotime_p, pscore); - WRITEUINT16(demotime_p, prings); - demotime_p = NULL; - } - else if (demoflags & DF_NIGHTSATTACK) - { - WRITEUINT32(demotime_p, ptime); - WRITEUINT32(demotime_p, pscore); - demotime_p = NULL; - } -} - -// Returns bitfield: -// 1 == new demo has lower time -// 2 == new demo has higher score -// 4 == new demo has higher rings -UINT8 G_CmpDemoTime(char *oldname, char *newname) -{ - UINT8 *buffer,*p; - UINT8 flags; - UINT32 oldtime, newtime, oldscore, newscore; - UINT16 oldrings, newrings, oldversion; - size_t bufsize ATTRUNUSED; - UINT8 c; - UINT16 s ATTRUNUSED; - UINT8 aflags = 0; - - // load the new file - FIL_DefaultExtension(newname, ".lmp"); - bufsize = FIL_ReadFile(newname, &buffer); - I_Assert(bufsize != 0); - p = buffer; - - // read demo header - I_Assert(!memcmp(p, DEMOHEADER, 12)); - p += 12; // DEMOHEADER - c = READUINT8(p); // VERSION - I_Assert(c == VERSION); - c = READUINT8(p); // SUBVERSION - I_Assert(c == SUBVERSION); - s = READUINT16(p); - I_Assert(s == DEMOVERSION); - p += 16; // demo checksum - I_Assert(!memcmp(p, "PLAY", 4)); - p += 4; // PLAY - p += 2; // gamemap - p += 16; // map md5 - flags = READUINT8(p); // demoflags - - aflags = flags & (DF_RECORDATTACK|DF_NIGHTSATTACK); - I_Assert(aflags); - if (flags & DF_RECORDATTACK) - { - newtime = READUINT32(p); - newscore = READUINT32(p); - newrings = READUINT16(p); - } - else if (flags & DF_NIGHTSATTACK) - { - newtime = READUINT32(p); - newscore = READUINT32(p); - newrings = 0; - } - else // appease compiler - return 0; - - Z_Free(buffer); - - // load old file - FIL_DefaultExtension(oldname, ".lmp"); - if (!FIL_ReadFile(oldname, &buffer)) - { - CONS_Alert(CONS_ERROR, M_GetText("Failed to read file '%s'.\n"), oldname); - return UINT8_MAX; - } - p = buffer; - - // read demo header - if (memcmp(p, DEMOHEADER, 12)) - { - CONS_Alert(CONS_NOTICE, M_GetText("File '%s' invalid format. It will be overwritten.\n"), oldname); - Z_Free(buffer); - return UINT8_MAX; - } p += 12; // DEMOHEADER - p++; // VERSION - p++; // SUBVERSION - oldversion = READUINT16(p); - switch(oldversion) // demoversion - { - case DEMOVERSION: // latest always supported - break; - // too old, cannot support. - default: - CONS_Alert(CONS_NOTICE, M_GetText("File '%s' invalid format. It will be overwritten.\n"), oldname); - Z_Free(buffer); - return UINT8_MAX; - } - p += 16; // demo checksum - if (memcmp(p, "PLAY", 4)) - { - CONS_Alert(CONS_NOTICE, M_GetText("File '%s' invalid format. It will be overwritten.\n"), oldname); - Z_Free(buffer); - return UINT8_MAX; - } p += 4; // "PLAY" - if (oldversion <= 0x0008) - p++; // gamemap - else - p += 2; // gamemap - p += 16; // mapmd5 - flags = READUINT8(p); - if (!(flags & aflags)) - { - CONS_Alert(CONS_NOTICE, M_GetText("File '%s' not from same game mode. It will be overwritten.\n"), oldname); - Z_Free(buffer); - return UINT8_MAX; - } - if (flags & DF_RECORDATTACK) - { - oldtime = READUINT32(p); - oldscore = READUINT32(p); - oldrings = READUINT16(p); - } - else if (flags & DF_NIGHTSATTACK) - { - oldtime = READUINT32(p); - oldscore = READUINT32(p); - oldrings = 0; - } - else // appease compiler - return UINT8_MAX; - - Z_Free(buffer); - - c = 0; - if (newtime < oldtime - || (newtime == oldtime && (newscore > oldscore || newrings > oldrings))) - c |= 1; // Better time - if (newscore > oldscore - || (newscore == oldscore && newtime < oldtime)) - c |= 1<<1; // Better score - if (newrings > oldrings - || (newrings == oldrings && newtime < oldtime)) - c |= 1<<2; // Better rings - return c; -} - -// -// G_PlayDemo -// -void G_DeferedPlayDemo(const char *name) -{ - COM_BufAddText("playdemo \""); - COM_BufAddText(name); - COM_BufAddText("\"\n"); -} - -// -// Start a demo from a .LMP file or from a wad resource -// -void G_DoPlayDemo(char *defdemoname) -{ - UINT8 i; - lumpnum_t l; - char skin[17],color[17],*n,*pdemoname; - UINT8 version,subversion,charability,charability2,thrustfactor,accelstart,acceleration; - pflags_t pflags; - UINT32 randseed, followitem; - fixed_t camerascale,shieldscale,actionspd,mindash,maxdash,normalspeed,runspeed,jumpfactor,height,spinheight; - char msg[1024]; - - skin[16] = '\0'; - color[16] = '\0'; - - n = defdemoname+strlen(defdemoname); - while (*n != '/' && *n != '\\' && n != defdemoname) - n--; - if (n != defdemoname) - n++; - pdemoname = ZZ_Alloc(strlen(n)+1); - strcpy(pdemoname,n); - - // Internal if no extension, external if one exists - if (FIL_CheckExtension(defdemoname)) - { - //FIL_DefaultExtension(defdemoname, ".lmp"); - if (!FIL_ReadFile(defdemoname, &demobuffer)) - { - snprintf(msg, 1024, M_GetText("Failed to read file '%s'.\n"), defdemoname); - CONS_Alert(CONS_ERROR, "%s", msg); - gameaction = ga_nothing; - M_StartMessage(msg, NULL, MM_NOTHING); - return; - } - demo_p = demobuffer; - } - // load demo resource from WAD - else if ((l = W_CheckNumForName(defdemoname)) == LUMPERROR) - { - snprintf(msg, 1024, M_GetText("Failed to read lump '%s'.\n"), defdemoname); - CONS_Alert(CONS_ERROR, "%s", msg); - gameaction = ga_nothing; - M_StartMessage(msg, NULL, MM_NOTHING); - return; - } - else // it's an internal demo - demobuffer = demo_p = W_CacheLumpNum(l, PU_STATIC); - - // read demo header - gameaction = ga_nothing; - demoplayback = true; - if (memcmp(demo_p, DEMOHEADER, 12)) - { - snprintf(msg, 1024, M_GetText("%s is not a SRB2 replay file.\n"), pdemoname); - CONS_Alert(CONS_ERROR, "%s", msg); - M_StartMessage(msg, NULL, MM_NOTHING); - Z_Free(pdemoname); - Z_Free(demobuffer); - demoplayback = false; - titledemo = false; - return; - } - demo_p += 12; // DEMOHEADER - - version = READUINT8(demo_p); - subversion = READUINT8(demo_p); - demoversion = READUINT16(demo_p); - switch(demoversion) - { - case DEMOVERSION: // latest always supported - break; - // too old, cannot support. - default: - snprintf(msg, 1024, M_GetText("%s is an incompatible replay format and cannot be played.\n"), pdemoname); - CONS_Alert(CONS_ERROR, "%s", msg); - M_StartMessage(msg, NULL, MM_NOTHING); - Z_Free(pdemoname); - Z_Free(demobuffer); - demoplayback = false; - titledemo = false; - return; - } - demo_p += 16; // demo checksum - if (memcmp(demo_p, "PLAY", 4)) - { - snprintf(msg, 1024, M_GetText("%s is the wrong type of recording and cannot be played.\n"), pdemoname); - CONS_Alert(CONS_ERROR, "%s", msg); - M_StartMessage(msg, NULL, MM_NOTHING); - Z_Free(pdemoname); - Z_Free(demobuffer); - demoplayback = false; - titledemo = false; - return; - } - demo_p += 4; // "PLAY" - gamemap = READINT16(demo_p); - demo_p += 16; // mapmd5 - - demoflags = READUINT8(demo_p); - modeattacking = (demoflags & DF_ATTACKMASK)>>DF_ATTACKSHIFT; - CON_ToggleOff(); - - hu_demoscore = 0; - hu_demotime = UINT32_MAX; - hu_demorings = 0; - - switch (modeattacking) - { - case ATTACKING_NONE: // 0 - break; - case ATTACKING_RECORD: // 1 - hu_demotime = READUINT32(demo_p); - hu_demoscore = READUINT32(demo_p); - hu_demorings = READUINT16(demo_p); - break; - case ATTACKING_NIGHTS: // 2 - hu_demotime = READUINT32(demo_p); - hu_demoscore = READUINT32(demo_p); - break; - default: // 3 - modeattacking = ATTACKING_NONE; - break; - } - - // Random seed - randseed = READUINT32(demo_p); - - // Player name - M_Memcpy(player_names[0],demo_p,16); - demo_p += 16; - - // Skin - M_Memcpy(skin,demo_p,16); - demo_p += 16; - - // Color - M_Memcpy(color,demo_p,16); - demo_p += 16; - - charability = READUINT8(demo_p); - charability2 = READUINT8(demo_p); - actionspd = (fixed_t)READUINT8(demo_p)<color = players[0].skincolor; - oldghost.x = players[0].mo->x; - oldghost.y = players[0].mo->y; - oldghost.z = players[0].mo->z; - } - - // Set saved attribute values - // No cheat checking here, because even if they ARE wrong... - // it would only break the replay if we clipped them. - players[0].camerascale = camerascale; - players[0].shieldscale = shieldscale; - players[0].charability = charability; - players[0].charability2 = charability2; - players[0].actionspd = actionspd; - players[0].mindash = mindash; - players[0].maxdash = maxdash; - players[0].normalspeed = normalspeed; - players[0].runspeed = runspeed; - players[0].thrustfactor = thrustfactor; - players[0].accelstart = accelstart; - players[0].acceleration = acceleration; - players[0].height = height; - players[0].spinheight = spinheight; - players[0].jumpfactor = jumpfactor; - players[0].followitem = followitem; - players[0].pflags = pflags; - - demo_start = true; -} - -void G_AddGhost(char *defdemoname) -{ - INT32 i; - lumpnum_t l; - char name[17],skin[17],color[17],*n,*pdemoname,md5[16]; - demoghost *gh; - UINT8 flags; - UINT8 *buffer,*p; - mapthing_t *mthing; - UINT16 count, ghostversion; - - name[16] = '\0'; - skin[16] = '\0'; - color[16] = '\0'; - - n = defdemoname+strlen(defdemoname); - while (*n != '/' && *n != '\\' && n != defdemoname) - n--; - if (n != defdemoname) - n++; - pdemoname = ZZ_Alloc(strlen(n)+1); - strcpy(pdemoname,n); - - // Internal if no extension, external if one exists - if (FIL_CheckExtension(defdemoname)) - { - //FIL_DefaultExtension(defdemoname, ".lmp"); - if (!FIL_ReadFileTag(defdemoname, &buffer, PU_LEVEL)) - { - CONS_Alert(CONS_ERROR, M_GetText("Failed to read file '%s'.\n"), defdemoname); - Z_Free(pdemoname); - return; - } - p = buffer; - } - // load demo resource from WAD - else if ((l = W_CheckNumForName(defdemoname)) == LUMPERROR) - { - CONS_Alert(CONS_ERROR, M_GetText("Failed to read lump '%s'.\n"), defdemoname); - Z_Free(pdemoname); - return; - } - else // it's an internal demo - buffer = p = W_CacheLumpNum(l, PU_LEVEL); - - // read demo header - if (memcmp(p, DEMOHEADER, 12)) - { - CONS_Alert(CONS_NOTICE, M_GetText("Ghost %s: Not a SRB2 replay.\n"), pdemoname); - Z_Free(pdemoname); - Z_Free(buffer); - return; - } p += 12; // DEMOHEADER - p++; // VERSION - p++; // SUBVERSION - ghostversion = READUINT16(p); - switch(ghostversion) - { - case DEMOVERSION: // latest always supported - break; - // too old, cannot support. - default: - CONS_Alert(CONS_NOTICE, M_GetText("Ghost %s: Demo version incompatible.\n"), pdemoname); - Z_Free(pdemoname); - Z_Free(buffer); - return; - } - M_Memcpy(md5, p, 16); p += 16; // demo checksum - for (gh = ghosts; gh; gh = gh->next) - if (!memcmp(md5, gh->checksum, 16)) // another ghost in the game already has this checksum? - { // Don't add another one, then! - CONS_Debug(DBG_SETUP, "Rejecting duplicate ghost %s (MD5 was matched)\n", pdemoname); - Z_Free(pdemoname); - Z_Free(buffer); - return; - } - if (memcmp(p, "PLAY", 4)) - { - CONS_Alert(CONS_NOTICE, M_GetText("Ghost %s: Demo format unacceptable.\n"), pdemoname); - Z_Free(pdemoname); - Z_Free(buffer); - return; - } p += 4; // "PLAY" - if (ghostversion <= 0x0008) - p++; // gamemap - else - p += 2; // gamemap - p += 16; // mapmd5 (possibly check for consistency?) - flags = READUINT8(p); - if (!(flags & DF_GHOST)) - { - CONS_Alert(CONS_NOTICE, M_GetText("Ghost %s: No ghost data in this demo.\n"), pdemoname); - Z_Free(pdemoname); - Z_Free(buffer); - return; - } - switch ((flags & DF_ATTACKMASK)>>DF_ATTACKSHIFT) - { - case ATTACKING_NONE: // 0 - break; - case ATTACKING_RECORD: // 1 - p += 10; // demo time, score, and rings - break; - case ATTACKING_NIGHTS: // 2 - p += 8; // demo time left, score - break; - default: // 3 - break; - } - - p += 4; // random seed - - // Player name (TODO: Display this somehow if it doesn't match cv_playername!) - M_Memcpy(name, p,16); - p += 16; - - // Skin - M_Memcpy(skin, p,16); - p += 16; - - // Color - M_Memcpy(color, p,16); - p += 16; - - // Ghosts do not have a player structure to put this in. - p++; // charability - p++; // charability2 - p++; // actionspd - p++; // mindash - p++; // maxdash - p++; // normalspeed - p++; // runspeed - p++; // thrustfactor - p++; // accelstart - p++; // acceleration - p++; // height - p++; // spinheight - p++; // camerascale - p++; // shieldscale - p += 4; // jumpfactor - p += 4; // followitem - - p++; // pflag data - - // net var data - count = READUINT16(p); - while (count--) - { - p += 2; - SKIPSTRING(p); - p++; - } - - if (*p == DEMOMARKER) - { - CONS_Alert(CONS_NOTICE, M_GetText("Failed to add ghost %s: Replay is empty.\n"), pdemoname); - Z_Free(pdemoname); - Z_Free(buffer); - return; - } - - gh = Z_Calloc(sizeof(demoghost), PU_LEVEL, NULL); - gh->next = ghosts; - gh->buffer = buffer; - M_Memcpy(gh->checksum, md5, 16); - gh->p = p; - - ghosts = gh; - - gh->version = ghostversion; - mthing = playerstarts[0]; - I_Assert(mthing); - { // A bit more complex than P_SpawnPlayer because ghosts aren't solid and won't just push themselves out of the ceiling. - fixed_t z,f,c; - fixed_t offset = mthing->z << FRACBITS; - gh->mo = P_SpawnMobj(mthing->x << FRACBITS, mthing->y << FRACBITS, 0, MT_GHOST); - gh->mo->angle = FixedAngle(mthing->angle << FRACBITS); - f = gh->mo->floorz; - c = gh->mo->ceilingz - mobjinfo[MT_PLAYER].height; - if (!!(mthing->options & MTF_AMBUSH) ^ !!(mthing->options & MTF_OBJECTFLIP)) - { - z = c - offset; - if (z < f) - z = f; - } - else - { - z = f + offset; - if (z > c) - z = c; - } - gh->mo->z = z; - } - - gh->oldmo.x = gh->mo->x; - gh->oldmo.y = gh->mo->y; - gh->oldmo.z = gh->mo->z; - - // Set skin - gh->mo->skin = &skins[0]; - for (i = 0; i < numskins; i++) - if (!stricmp(skins[i].name,skin)) - { - gh->mo->skin = &skins[i]; - break; - } - gh->oldmo.skin = gh->mo->skin; - - // Set color - gh->mo->color = ((skin_t*)gh->mo->skin)->prefcolor; - for (i = 0; i < MAXSKINCOLORS; i++) - if (!stricmp(Color_Names[i],color)) - { - gh->mo->color = (UINT8)i; - break; - } - gh->oldmo.color = gh->mo->color; - - 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_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); - Z_Free(pdemoname); -} - -// -// G_TimeDemo -// NOTE: name is a full filename for external demos -// -static INT32 restorecv_vidwait; - -void G_TimeDemo(const char *name) -{ - nodrawers = M_CheckParm("-nodraw"); - noblit = M_CheckParm("-noblit"); - restorecv_vidwait = cv_vidwait.value; - if (cv_vidwait.value) - CV_Set(&cv_vidwait, "0"); - timingdemo = true; - singletics = true; - framecount = 0; - demostarttime = I_GetTime(); - G_DeferedPlayDemo(name); -} - -void G_DoPlayMetal(void) -{ - lumpnum_t l; - mobj_t *mo = NULL; - thinker_t *th; - - // it's an internal demo - if ((l = W_CheckNumForName(va("%sMS",G_BuildMapName(gamemap)))) == LUMPERROR) - { - CONS_Alert(CONS_WARNING, M_GetText("No bot recording for this map.\n")); - return; - } - else - metalbuffer = metal_p = W_CacheLumpNum(l, PU_STATIC); - - // find metal sonic - for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) - { - if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) - continue; - - mo = (mobj_t *)th; - if (mo->type != MT_METALSONIC_RACE) - continue; - - break; - } - if (th == &thlist[THINK_MOBJ]) - { - CONS_Alert(CONS_ERROR, M_GetText("Failed to find bot entity.\n")); - Z_Free(metalbuffer); - return; - } - - // read demo header - metal_p += 12; // DEMOHEADER - metal_p++; // VERSION - metal_p++; // SUBVERSION - metalversion = READUINT16(metal_p); - switch(metalversion) - { - case DEMOVERSION: // latest always supported - break; - // too old, cannot support. - default: - CONS_Alert(CONS_WARNING, M_GetText("Failed to load bot recording for this map, format version incompatible.\n")); - Z_Free(metalbuffer); - return; - } - metal_p += 16; // demo checksum - if (memcmp(metal_p, "METL", 4)) - { - CONS_Alert(CONS_WARNING, M_GetText("Failed to load bot recording for this map, wasn't recorded in Metal format.\n")); - Z_Free(metalbuffer); - return; - } metal_p += 4; // "METL" - - // read initial tic - memset(&oldmetal,0,sizeof(oldmetal)); - oldmetal.x = mo->x; - oldmetal.y = mo->y; - oldmetal.z = mo->z; - metalplayback = mo; -} - -void G_DoneLevelLoad(void) -{ - CONS_Printf(M_GetText("Loaded level in %f sec\n"), (double)(I_GetTime() - demostarttime) / TICRATE); - framecount = 0; - demostarttime = I_GetTime(); -} - -/* -=================== -= -= G_CheckDemoStatus -= -= Called after a death or level completion to allow demos to be cleaned up -= Returns true if a new demo loop action will take place -=================== -*/ - -// Stops metal sonic's demo. Separate from other functions because metal + replays can coexist -void G_StopMetalDemo(void) -{ - - // Metal Sonic finishing doesn't end the game, dammit. - Z_Free(metalbuffer); - metalbuffer = NULL; - metalplayback = NULL; - metal_p = NULL; -} - -// Stops metal sonic recording. -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; - for (i = 0; i < 16; i++, p++) - *p = P_RandomByte(); // This MD5 was chosen by fair dice roll and most likely < 50% correct. - } -#else - 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. - } - free(demobuffer); - metalrecording = false; - if (saved) - I_Error("Saved to %sMS.LMP", G_BuildMapName(gamemap)); - I_Error("Failed to save demo!"); -} - -// reset engine variable set for the demos -// called from stopdemo command, map command, and g_checkdemoStatus. -void G_StopDemo(void) -{ - Z_Free(demobuffer); - demobuffer = NULL; - demoplayback = false; - titledemo = false; - timingdemo = false; - singletics = false; - - if (gamestate == GS_INTERMISSION) - Y_EndIntermission(); // cleanup - - G_SetGamestate(GS_NULL); - wipegamestate = GS_NULL; - SV_StopServer(); - SV_ResetServer(); -} - -boolean G_CheckDemoStatus(void) -{ - boolean saved; - - while (ghosts) - { - demoghost *next = ghosts->next; - Z_Free(ghosts); - ghosts = next; - } - ghosts = NULL; - - - // DO NOT end metal sonic demos here - - if (timingdemo) - { - INT32 demotime; - double f1, f2; - demotime = I_GetTime() - demostarttime; - if (!demotime) - return true; - G_StopDemo(); - timingdemo = false; - f1 = (double)demotime; - f2 = (double)framecount*TICRATE; - - CONS_Printf(M_GetText("timed %u gametics in %d realtics - %u frames\n%f seconds, %f avg fps\n"), - leveltime,demotime,(UINT32)framecount,f1/TICRATE,f2/f1); - - // CSV-readable timedemo results, for external parsing - if (timedemo_csv) - { - FILE *f; - const char *csvpath = va("%s"PATHSEP"%s", srb2home, "timedemo.csv"); - const char *header = "id,demoname,seconds,avgfps,leveltime,demotime,framecount,ticrate,rendermode,vidmode,vidwidth,vidheight,procbits\n"; - const char *rowformat = "\"%s\",\"%s\",%f,%f,%u,%d,%u,%u,%u,%u,%u,%u,%u\n"; - boolean headerrow = !FIL_FileExists(csvpath); - UINT8 procbits = 0; - - // Bitness - if (sizeof(void*) == 4) - procbits = 32; - else if (sizeof(void*) == 8) - procbits = 64; - - f = fopen(csvpath, "a+"); - - if (f) - { - if (headerrow) - fputs(header, f); - fprintf(f, rowformat, - timedemo_csv_id,timedemo_name,f1/TICRATE,f2/f1,leveltime,demotime,(UINT32)framecount,TICRATE,rendermode,vid.modenum,vid.width,vid.height,procbits); - fclose(f); - CONS_Printf("Timedemo results saved to '%s'\n", csvpath); - } - else - { - // Just print the CSV output to console - CON_LogMessage(header); - CONS_Printf(rowformat, - timedemo_csv_id,timedemo_name,f1/TICRATE,f2/f1,leveltime,demotime,(UINT32)framecount,TICRATE,rendermode,vid.modenum,vid.width,vid.height,procbits); - } - } - - if (restorecv_vidwait != cv_vidwait.value) - CV_SetValue(&cv_vidwait, restorecv_vidwait); - D_AdvanceDemo(); - return true; - } - - if (demoplayback) - { - if (singledemo) - I_Quit(); - G_StopDemo(); - - if (modeattacking) - M_EndModeAttackRun(); - else - D_AdvanceDemo(); - - return true; - } - - if (demorecording) - { - UINT8 *p = demobuffer+16; // checksum position -#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. -#else - WRITEUINT8(demo_p, DEMOMARKER); // add the demo end marker - md5_buffer((char *)p+16, demo_p - (p+16), p); // make a checksum of everything after the checksum in the file. -#endif - saved = FIL_WriteFile(va(pandf, srb2home, demoname), demobuffer, demo_p - demobuffer); // finally output the file. - free(demobuffer); - demorecording = false; - - if (modeattacking != ATTACKING_RECORD) - { - if (saved) - CONS_Printf(M_GetText("Demo %s recorded\n"), demoname); - else - CONS_Alert(CONS_WARNING, M_GetText("Demo %s not saved\n"), demoname); - } - return true; - } - - return false; -} - // // G_SetGamestate // diff --git a/src/g_game.h b/src/g_game.h index c4c40d84b..b4216a001 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -17,6 +17,7 @@ #include "doomdef.h" #include "doomstat.h" #include "d_event.h" +#include "g_demo.h" extern char gamedatafilename[64]; extern char timeattackfolder[64]; @@ -31,21 +32,6 @@ extern char player_names[MAXPLAYERS][MAXPLAYERNAME+1]; extern player_t players[MAXPLAYERS]; extern boolean playeringame[MAXPLAYERS]; -// ====================================== -// DEMO playback/recording related stuff. -// ====================================== - -// demoplaying back and demo recording -extern boolean demoplayback, titledemo, demorecording, timingdemo; -extern tic_t demostarttime; - -// Quit after playing a demo from cmdline. -extern boolean singledemo; -extern boolean demo_start; -extern boolean demosynced; - -extern mobj_t *metalplayback; - // gametic at level start extern tic_t levelstarttic; @@ -173,7 +159,6 @@ void G_DoLoadLevel(boolean resetplayer); void G_StartTitleCard(void); void G_PreLevelTitleCard(void); boolean G_IsTitleCardAvailable(void); -void G_DeferedPlayDemo(const char *demo); // Can be called by the startup code or M_Responder, calls P_SetupLevel. void G_LoadGame(UINT32 slot, INT16 mapoverride); @@ -184,54 +169,6 @@ void G_SaveGame(UINT32 slot); void G_SaveGameOver(UINT32 slot, boolean modifylives); -// Only called by startup code. -void G_RecordDemo(const char *name); -void G_RecordMetal(void); -void G_BeginRecording(void); -void G_BeginMetal(void); - -// Only called by shutdown code. -void G_SetDemoTime(UINT32 ptime, UINT32 pscore, UINT16 prings); -UINT8 G_CmpDemoTime(char *oldname, char *newname); - -typedef enum -{ - GHC_NORMAL = 0, - GHC_SUPER, - GHC_FIREFLOWER, - GHC_INVINCIBLE, - GHC_NIGHTSSKIN, // not actually a colour - GHC_RETURNSKIN // ditto -} ghostcolor_t; - -// Record/playback tics -void G_ReadDemoTiccmd(ticcmd_t *cmd, INT32 playernum); -void G_WriteDemoTiccmd(ticcmd_t *cmd, INT32 playernum); -void G_GhostAddThok(void); -void G_GhostAddSpin(void); -void G_GhostAddRev(void); -void G_GhostAddColor(ghostcolor_t color); -void G_GhostAddFlip(void); -void G_GhostAddScale(fixed_t scale); -void G_GhostAddHit(mobj_t *victim); -void G_WriteGhostTic(mobj_t *ghost); -void G_ConsGhostTic(void); -void G_GhostTicker(void); -void G_ReadMetalTic(mobj_t *metal); -void G_WriteMetalTic(mobj_t *metal); -void G_SaveMetal(UINT8 **buffer); -void G_LoadMetal(UINT8 **buffer); - -void G_DoPlayDemo(char *defdemoname); -void G_TimeDemo(const char *name); -void G_AddGhost(char *defdemoname); -void G_DoPlayMetal(void); -void G_DoneLevelLoad(void); -void G_StopMetalDemo(void); -ATTRNORETURN void FUNCNORETURN G_StopMetalRecording(boolean kill); -void G_StopDemo(void); -boolean G_CheckDemoStatus(void); - extern UINT32 gametypedefaultrules[NUMGAMETYPES]; extern UINT32 gametypetol[NUMGAMETYPES]; extern INT16 gametyperankings[NUMGAMETYPES]; From 1a142340884f9a4216a845c7482e6acd2e0aecf4 Mon Sep 17 00:00:00 2001 From: Monster Iestyn Date: Thu, 19 Mar 2020 20:13:39 +0000 Subject: [PATCH 42/54] update Makefile, CMakeLists.txt and MSVC project files --- src/CMakeLists.txt | 2 ++ src/Makefile | 1 + src/sdl/Srb2SDL-vc10.vcxproj | 2 ++ src/sdl/Srb2SDL-vc10.vcxproj.filters | 6 ++++++ 4 files changed, 11 insertions(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bc4a6114e..e1e7689a6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -16,6 +16,7 @@ set(SRB2_CORE_SOURCES f_finale.c f_wipe.c filesrch.c + g_demo.c g_game.c g_input.c hu_stuff.c @@ -71,6 +72,7 @@ set(SRB2_CORE_HEADERS f_finale.h fastcmp.h filesrch.h + g_demo.h g_game.h g_input.h g_state.h diff --git a/src/Makefile b/src/Makefile index 701f2cfda..43cf48905 100644 --- a/src/Makefile +++ b/src/Makefile @@ -424,6 +424,7 @@ OBJS:=$(i_main_o) \ $(OBJDIR)/z_zone.o \ $(OBJDIR)/f_finale.o \ $(OBJDIR)/f_wipe.o \ + $(OBJDIR)/g_demo.o \ $(OBJDIR)/g_game.o \ $(OBJDIR)/g_input.o \ $(OBJDIR)/am_map.o \ diff --git a/src/sdl/Srb2SDL-vc10.vcxproj b/src/sdl/Srb2SDL-vc10.vcxproj index 7e260f4c0..6335b3028 100644 --- a/src/sdl/Srb2SDL-vc10.vcxproj +++ b/src/sdl/Srb2SDL-vc10.vcxproj @@ -213,6 +213,7 @@ + @@ -364,6 +365,7 @@ + diff --git a/src/sdl/Srb2SDL-vc10.vcxproj.filters b/src/sdl/Srb2SDL-vc10.vcxproj.filters index 21820551a..a226e8397 100644 --- a/src/sdl/Srb2SDL-vc10.vcxproj.filters +++ b/src/sdl/Srb2SDL-vc10.vcxproj.filters @@ -180,6 +180,9 @@ F_Frame + + G_Game + G_Game @@ -600,6 +603,9 @@ F_Frame + + G_Game + G_Game From dd76be16cbe6cfe075376fff1b358e1e57d305b2 Mon Sep 17 00:00:00 2001 From: Monster Iestyn Date: Thu, 19 Mar 2020 20:42:51 +0000 Subject: [PATCH 43/54] Create G_FreeGhosts, for the benefit of G_DeferedInitNew (assuming it actually needs to do `ghosts = NULL;` at all) --- src/g_demo.c | 21 +++++++++++++-------- src/g_demo.h | 1 + src/g_game.c | 2 +- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/g_demo.c b/src/g_demo.c index 22cd34ad5..9adafae54 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -2223,6 +2223,18 @@ void G_AddGhost(char *defdemoname) Z_Free(pdemoname); } +// Clean up all ghosts +void G_FreeGhosts(void) +{ + while (ghosts) + { + demoghost *next = ghosts->next; + Z_Free(ghosts); + ghosts = next; + } + ghosts = NULL; +} + // // G_TimeDemo // NOTE: name is a full filename for external demos @@ -2389,14 +2401,7 @@ boolean G_CheckDemoStatus(void) { boolean saved; - while (ghosts) - { - demoghost *next = ghosts->next; - Z_Free(ghosts); - ghosts = next; - } - ghosts = NULL; - + G_FreeGhosts(); // DO NOT end metal sonic demos here diff --git a/src/g_demo.h b/src/g_demo.h index 89be6e343..df25042c4 100644 --- a/src/g_demo.h +++ b/src/g_demo.h @@ -75,6 +75,7 @@ void G_DeferedPlayDemo(const char *demo); void G_DoPlayDemo(char *defdemoname); void G_TimeDemo(const char *name); void G_AddGhost(char *defdemoname); +void G_FreeGhosts(void); void G_DoPlayMetal(void); void G_DoneLevelLoad(void); void G_StopMetalDemo(void); diff --git a/src/g_game.c b/src/g_game.c index fb70e2995..e22961b9a 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -4490,7 +4490,7 @@ void G_DeferedInitNew(boolean pultmode, const char *mapname, INT32 pickedchar, b if (demoplayback) COM_BufAddText("stopdemo\n"); - ghosts = NULL; + G_FreeGhosts(); // TODO: do we actually need to do this? // this leave the actual game if needed SV_StartSinglePlayerServer(); From 6610150bfeb11796cacad3d6453ab98030f1cd4b Mon Sep 17 00:00:00 2001 From: Monster Iestyn Date: Thu, 19 Mar 2020 20:43:56 +0000 Subject: [PATCH 44/54] We definitely don't need these files to compile! --- src/g_demo.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/g_demo.c b/src/g_demo.c index 9adafae54..c589482b6 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -16,14 +16,12 @@ #include "d_main.h" #include "d_player.h" #include "d_clisrv.h" -//#include "f_finale.h" #include "p_setup.h" #include "i_system.h" #include "m_random.h" #include "p_local.h" #include "r_draw.h" #include "r_main.h" -//#include "s_sound.h" #include "g_game.h" #include "g_demo.h" #include "m_cheat.h" From 4236ae7c083f388d0c184c165a724ade3560f242 Mon Sep 17 00:00:00 2001 From: Monster Iestyn Date: Thu, 19 Mar 2020 21:10:23 +0000 Subject: [PATCH 45/54] We don't need m_cheat.h either in g_demo.c --- src/g_demo.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/g_demo.c b/src/g_demo.c index c589482b6..199f7d68e 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -24,7 +24,6 @@ #include "r_main.h" #include "g_game.h" #include "g_demo.h" -#include "m_cheat.h" #include "m_misc.h" #include "m_menu.h" #include "m_argv.h" From 5eaa73b6ac8194a17a6af54f46fd7a2b7be46746 Mon Sep 17 00:00:00 2001 From: Jaime Passos Date: Thu, 19 Mar 2020 19:09:34 -0300 Subject: [PATCH 46/54] Fix lib_setSpriteInfo indexes being off-by-one --- src/lua_infolib.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/lua_infolib.c b/src/lua_infolib.c index 6760b4203..d099b24a9 100644 --- a/src/lua_infolib.c +++ b/src/lua_infolib.c @@ -389,10 +389,7 @@ static int lib_setSpriteInfo(lua_State *L) lua_Integer i = 0; const char *str = NULL; if (lua_isnumber(L, 2)) - { i = lua_tointeger(L, 2); - i++; // shift index in case of missing rotsprite support - } else str = luaL_checkstring(L, 2); From 99a5192872b6ad9d827985a38de4dc9411c08e38 Mon Sep 17 00:00:00 2001 From: sphere Date: Tue, 24 Mar 2020 16:10:30 +0100 Subject: [PATCH 47/54] Hide record/NiGHTS attack if there are no available maps for them. --- src/m_menu.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/m_menu.c b/src/m_menu.c index 6c22c6586..c6babc805 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -7990,12 +7990,18 @@ static void M_CustomLevelSelect(INT32 choice) static void M_SinglePlayerMenu(INT32 choice) { (void)choice; - SP_MainMenu[sptutorial].status = - tutorialmap ? IT_CALL|IT_STRING : IT_NOTHING|IT_DISABLED; - SP_MainMenu[sprecordattack].status = - (M_SecretUnlocked(SECRET_RECORDATTACK)) ? IT_CALL|IT_STRING : IT_SECRET; - SP_MainMenu[spnightsmode].status = - (M_SecretUnlocked(SECRET_NIGHTSMODE)) ? IT_CALL|IT_STRING : IT_SECRET; + + if (M_GametypeHasLevels(LLM_RECORDATTACK)) + SP_MainMenu[sprecordattack].status = (M_SecretUnlocked(SECRET_RECORDATTACK)) ? IT_CALL|IT_STRING : IT_SECRET; + else + SP_MainMenu[sprecordattack].status = IT_NOTHING|IT_DISABLED; + + if (M_GametypeHasLevels(LLM_NIGHTSATTACK)) + SP_MainMenu[spnightsmode].status = (M_SecretUnlocked(SECRET_NIGHTSMODE)) ? IT_CALL|IT_STRING : IT_SECRET; + else + SP_MainMenu[spnightsmode].status = IT_NOTHING|IT_DISABLED; + + SP_MainMenu[sptutorial].status = tutorialmap ? IT_CALL|IT_STRING : IT_NOTHING|IT_DISABLED; M_SetupNextMenu(&SP_MainDef); } From 92c74814cf20a9351f8bc36887378531155dbf6b Mon Sep 17 00:00:00 2001 From: MascaraSnake Date: Wed, 25 Mar 2020 08:34:52 +0100 Subject: [PATCH 48/54] Standardize the behavior of FF_INVERTSIDES and FF_INVERTPLANES --- src/dehacked.c | 4 ++-- src/hardware/hw_main.c | 12 ++++++++---- src/p_sight.c | 4 ++-- src/p_spec.c | 2 +- src/r_bsp.c | 8 ++++---- src/r_defs.h | 4 ++-- src/r_segs.c | 28 ++++++++++++++++------------ 7 files changed, 35 insertions(+), 27 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index c6cd0b9e5..20d23d680 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -9648,11 +9648,11 @@ struct { {"FF_CUTEXTRA",FF_CUTEXTRA}, ///< Cuts out hidden translucent pixels. {"FF_CUTLEVEL",FF_CUTLEVEL}, ///< Cuts out all hidden pixels. {"FF_CUTSPRITES",FF_CUTSPRITES}, ///< Final step in making 3D water. - {"FF_BOTHPLANES",FF_BOTHPLANES}, ///< Renders both planes all the time. + {"FF_BOTHPLANES",FF_BOTHPLANES}, ///< Render inside and outside planes. {"FF_EXTRA",FF_EXTRA}, ///< Gets cut by ::FF_CUTEXTRA. {"FF_TRANSLUCENT",FF_TRANSLUCENT}, ///< See through! {"FF_FOG",FF_FOG}, ///< Fog "brush." - {"FF_INVERTPLANES",FF_INVERTPLANES}, ///< Reverse the plane visibility rules. + {"FF_INVERTPLANES",FF_INVERTPLANES}, ///< Only render inside planes. {"FF_ALLSIDES",FF_ALLSIDES}, ///< Render inside and outside sides. {"FF_INVERTSIDES",FF_INVERTSIDES}, ///< Only render inside sides. {"FF_DOUBLESHADOW",FF_DOUBLESHADOW}, ///< Make two lightlist entries to reset light? diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index c56f0ec06..51c6471ac 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -1902,7 +1902,9 @@ static void HWR_StoreWallRange(double startfrac, double endfrac) { for (rover = gr_backsector->ffloors; rover; rover = rover->next) { - if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_RENDERSIDES) || (rover->flags & FF_INVERTSIDES)) + if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_RENDERSIDES)) + continue; + if (!(rover->flags & FF_ALLSIDES) && rover->flags & FF_INVERTSIDES) continue; if (*rover->topheight < lowcut || *rover->bottomheight > highcut) continue; @@ -2042,7 +2044,9 @@ static void HWR_StoreWallRange(double startfrac, double endfrac) { for (rover = gr_frontsector->ffloors; rover; rover = rover->next) { - if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_RENDERSIDES) || !(rover->flags & FF_ALLSIDES)) + if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_RENDERSIDES)) + continue; + if (!(rover->flags & FF_ALLSIDES || rover->flags & FF_INVERTSIDES)) continue; if (*rover->topheight < lowcut || *rover->bottomheight > highcut) continue; @@ -3427,7 +3431,7 @@ static void HWR_Subsector(size_t num) if (centerHeight <= locCeilingHeight && centerHeight >= locFloorHeight && - ((dup_viewz < cullHeight && !(rover->flags & FF_INVERTPLANES)) || + ((dup_viewz < cullHeight && (rover->flags & FF_BOTHPLANES || !(rover->flags & FF_INVERTPLANES))) || (dup_viewz > cullHeight && (rover->flags & FF_BOTHPLANES || rover->flags & FF_INVERTPLANES)))) { if (rover->flags & FF_FOG) @@ -3488,7 +3492,7 @@ static void HWR_Subsector(size_t num) if (centerHeight >= locFloorHeight && centerHeight <= locCeilingHeight && - ((dup_viewz > cullHeight && !(rover->flags & FF_INVERTPLANES)) || + ((dup_viewz > cullHeight && (rover->flags & FF_BOTHPLANES || !(rover->flags & FF_INVERTPLANES))) || (dup_viewz < cullHeight && (rover->flags & FF_BOTHPLANES || rover->flags & FF_INVERTPLANES)))) { if (rover->flags & FF_FOG) diff --git a/src/p_sight.c b/src/p_sight.c index c9083b99b..3d1ee9e60 100644 --- a/src/p_sight.c +++ b/src/p_sight.c @@ -495,7 +495,7 @@ boolean P_CheckSight(mobj_t *t1, mobj_t *t2) if (rover->flags & FF_SOLID) continue; // shortcut since neither mobj can be inside the 3dfloor - if (!(rover->flags & FF_INVERTPLANES)) + if (rover->flags & FF_BOTHPLANES || !(rover->flags & FF_INVERTPLANES)) { if (los.sightzstart >= topz1 && t2->z + t2->height < topz2) return false; // blocked by upper outside plane @@ -504,7 +504,7 @@ boolean P_CheckSight(mobj_t *t1, mobj_t *t2) return false; // blocked by lower outside plane } - if (rover->flags & FF_INVERTPLANES || rover->flags & FF_BOTHPLANES) + if (rover->flags & FF_BOTHPLANES || rover->flags & FF_INVERTPLANES) { if (los.sightzstart < topz1 && t2->z >= topz2) return false; // blocked by upper inside plane diff --git a/src/p_spec.c b/src/p_spec.c index cd26dcf9e..dee816e47 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -7069,7 +7069,7 @@ void P_SpawnSpecials(boolean fromnetsave) break; case 202: // Fog - ffloorflags = FF_EXISTS|FF_RENDERALL|FF_FOG|FF_BOTHPLANES|FF_INVERTPLANES|FF_ALLSIDES|FF_INVERTSIDES|FF_CUTEXTRA|FF_EXTRA|FF_DOUBLESHADOW|FF_CUTSPRITES; + ffloorflags = FF_EXISTS|FF_RENDERALL|FF_FOG|FF_INVERTPLANES|FF_INVERTSIDES|FF_CUTEXTRA|FF_EXTRA|FF_DOUBLESHADOW|FF_CUTSPRITES; sec = sides[*lines[i].sidenum].sector - sectors; // SoM: Because it's fog, check for an extra colormap and set the fog flag... if (sectors[sec].extra_colormap) diff --git a/src/r_bsp.c b/src/r_bsp.c index 85113be43..77ab2a82f 100644 --- a/src/r_bsp.c +++ b/src/r_bsp.c @@ -955,8 +955,8 @@ static void R_Subsector(size_t num) *rover->bottomheight; if (planecenterz <= ceilingcenterz && planecenterz >= floorcenterz - && ((viewz < heightcheck && !(rover->flags & FF_INVERTPLANES)) - || (viewz > heightcheck && (rover->flags & FF_BOTHPLANES)))) + && ((viewz < heightcheck && (rover->flags & FF_BOTHPLANES || !(rover->flags & FF_INVERTPLANES))) + || (viewz > heightcheck && (rover->flags & FF_BOTHPLANES || rover->flags & FF_INVERTPLANES)))) { light = R_GetPlaneLight(frontsector, planecenterz, viewz < heightcheck); @@ -993,8 +993,8 @@ static void R_Subsector(size_t num) *rover->topheight; if (planecenterz >= floorcenterz && planecenterz <= ceilingcenterz - && ((viewz > heightcheck && !(rover->flags & FF_INVERTPLANES)) - || (viewz < heightcheck && (rover->flags & FF_BOTHPLANES)))) + && ((viewz > heightcheck && (rover->flags & FF_BOTHPLANES || !(rover->flags & FF_INVERTPLANES))) + || (viewz < heightcheck && (rover->flags & FF_BOTHPLANES || rover->flags & FF_INVERTPLANES)))) { light = R_GetPlaneLight(frontsector, planecenterz, viewz < heightcheck); diff --git a/src/r_defs.h b/src/r_defs.h index 0c6ff1d30..5f62ec058 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -130,11 +130,11 @@ typedef enum FF_CUTEXTRA = 0x100, ///< Cuts out hidden translucent pixels. FF_CUTLEVEL = 0x180, ///< Cuts out all hidden pixels. FF_CUTSPRITES = 0x200, ///< Final step in making 3D water. - FF_BOTHPLANES = 0x400, ///< Renders both planes all the time. + FF_BOTHPLANES = 0x400, ///< Render inside and outside planes. FF_EXTRA = 0x800, ///< Gets cut by ::FF_CUTEXTRA. FF_TRANSLUCENT = 0x1000, ///< See through! FF_FOG = 0x2000, ///< Fog "brush." - FF_INVERTPLANES = 0x4000, ///< Reverse the plane visibility rules. + FF_INVERTPLANES = 0x4000, ///< Only render inside planes. FF_ALLSIDES = 0x8000, ///< Render inside and outside sides. FF_INVERTSIDES = 0x10000, ///< Only render inside sides. FF_DOUBLESHADOW = 0x20000, ///< Make two lightlist entries to reset light? diff --git a/src/r_segs.c b/src/r_segs.c index d8f1981ee..d4b07ff13 100644 --- a/src/r_segs.c +++ b/src/r_segs.c @@ -2165,7 +2165,7 @@ void R_StoreWallRange(INT32 start, INT32 stop) { if (!(rover->flags & FF_RENDERSIDES) || !(rover->flags & FF_EXISTS)) continue; - if (rover->flags & FF_INVERTSIDES) + if (!(rover->flags & FF_ALLSIDES) && rover->flags & FF_INVERTSIDES) continue; if (rover->norender == leveltime) @@ -2220,7 +2220,7 @@ void R_StoreWallRange(INT32 start, INT32 stop) { if (!(rover->flags & FF_RENDERSIDES) || !(rover->flags & FF_EXISTS)) continue; - if (!(rover->flags & FF_ALLSIDES)) + if (!(rover->flags & FF_ALLSIDES || rover->flags & FF_INVERTSIDES)) continue; if (rover->norender == leveltime) @@ -2275,7 +2275,9 @@ void R_StoreWallRange(INT32 start, INT32 stop) { for (rover = backsector->ffloors, i = 0; rover && i < MAXFFLOORS; rover = rover->next) { - if (!(rover->flags & FF_RENDERSIDES) || !(rover->flags & FF_EXISTS) || rover->flags & FF_INVERTSIDES) + if (!(rover->flags & FF_RENDERSIDES) || !(rover->flags & FF_EXISTS)) + continue; + if (!(rover->flags & FF_ALLSIDES) && rover->flags & FF_INVERTSIDES) continue; if (rover->norender == leveltime) continue; @@ -2295,7 +2297,9 @@ void R_StoreWallRange(INT32 start, INT32 stop) { for (rover = frontsector->ffloors, i = 0; rover && i < MAXFFLOORS; rover = rover->next) { - if (!(rover->flags & FF_RENDERSIDES) || !(rover->flags & FF_EXISTS) || !(rover->flags & FF_ALLSIDES)) + if (!(rover->flags & FF_RENDERSIDES) || !(rover->flags & FF_EXISTS)) + continue; + if (!(rover->flags & FF_ALLSIDES || rover->flags & FF_INVERTSIDES)) continue; if (rover->norender == leveltime) continue; @@ -2620,8 +2624,8 @@ void R_StoreWallRange(INT32 start, INT32 stop) if ((roverleft>>4 <= worldhigh || roverright>>4 <= worldhighslope) && (roverleft>>4 >= worldlow || roverright>>4 >= worldlowslope) && - ((viewz < planevistest && !(rover->flags & FF_INVERTPLANES)) || - (viewz > planevistest && (rover->flags & FF_BOTHPLANES)))) + ((viewz < planevistest && (rover->flags & FF_BOTHPLANES || !(rover->flags & FF_INVERTPLANES))) || + (viewz > planevistest && (rover->flags & FF_BOTHPLANES || rover->flags & FF_INVERTPLANES)))) { //ffloor[i].slope = *rover->b_slope; ffloor[i].b_pos = roverleft; @@ -2643,8 +2647,8 @@ void R_StoreWallRange(INT32 start, INT32 stop) if ((roverleft>>4 <= worldhigh || roverright>>4 <= worldhighslope) && (roverleft>>4 >= worldlow || roverright>>4 >= worldlowslope) && - ((viewz > planevistest && !(rover->flags & FF_INVERTPLANES)) || - (viewz < planevistest && (rover->flags & FF_BOTHPLANES)))) + ((viewz > planevistest && (rover->flags & FF_BOTHPLANES || !(rover->flags & FF_INVERTPLANES))) || + (viewz < planevistest && (rover->flags & FF_BOTHPLANES || rover->flags & FF_INVERTPLANES)))) { //ffloor[i].slope = *rover->t_slope; ffloor[i].b_pos = roverleft; @@ -2677,8 +2681,8 @@ void R_StoreWallRange(INT32 start, INT32 stop) if ((roverleft>>4 <= worldhigh || roverright>>4 <= worldhighslope) && (roverleft>>4 >= worldlow || roverright>>4 >= worldlowslope) && - ((viewz < planevistest && !(rover->flags & FF_INVERTPLANES)) || - (viewz > planevistest && (rover->flags & FF_BOTHPLANES)))) + ((viewz < planevistest && (rover->flags & FF_BOTHPLANES || !(rover->flags & FF_INVERTPLANES))) || + (viewz > planevistest && (rover->flags & FF_BOTHPLANES || rover->flags & FF_INVERTPLANES)))) { //ffloor[i].slope = *rover->b_slope; ffloor[i].b_pos = roverleft; @@ -2700,8 +2704,8 @@ void R_StoreWallRange(INT32 start, INT32 stop) if ((roverleft>>4 <= worldhigh || roverright>>4 <= worldhighslope) && (roverleft>>4 >= worldlow || roverright>>4 >= worldlowslope) && - ((viewz > planevistest && !(rover->flags & FF_INVERTPLANES)) || - (viewz < planevistest && (rover->flags & FF_BOTHPLANES)))) + ((viewz > planevistest && (rover->flags & FF_BOTHPLANES || !(rover->flags & FF_INVERTPLANES))) || + (viewz < planevistest && (rover->flags & FF_BOTHPLANES || rover->flags & FF_INVERTPLANES)))) { //ffloor[i].slope = *rover->t_slope; ffloor[i].b_pos = roverleft; From 8130599a4f01378e67308c98570e43df80e1118c Mon Sep 17 00:00:00 2001 From: Louis-Antoine Date: Sat, 28 Mar 2020 15:23:15 +0100 Subject: [PATCH 49/54] Fix disconnected players being kicked for high ping --- src/d_clisrv.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 71e32977a..92602d68a 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -4855,7 +4855,8 @@ static inline void PingUpdate(void) { for (i = 1; i < MAXPLAYERS; i++) { - if (playeringame[i] && (realpingtable[i] / pingmeasurecount > (unsigned)cv_maxping.value)) + if (playeringame[i] && !players[i].quittime + && (realpingtable[i] / pingmeasurecount > (unsigned)cv_maxping.value)) { if (players[i].jointime > 30 * TICRATE) laggers[i] = true; @@ -4874,8 +4875,8 @@ static inline void PingUpdate(void) if (playeringame[i] && laggers[i]) { pingtimeout[i]++; + // ok your net has been bad for too long, you deserve to die. if (pingtimeout[i] > cv_pingtimeout.value) -// ok your net has been bad for too long, you deserve to die. { pingtimeout[i] = 0; SendKick(i, KICK_MSG_PING_HIGH | KICK_MSG_KEEP_BODY); From 2e27b3ea87cdc870774a1cbe068494d9a1df233d Mon Sep 17 00:00:00 2001 From: Monster Iestyn Date: Mon, 30 Mar 2020 19:58:46 +0100 Subject: [PATCH 50/54] Turn the fixed-float convert macros into inline functions, like those in GZDoom. The old macros get to be just the same as calling these inline functions. --- src/m_fixed.h | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/m_fixed.h b/src/m_fixed.h index 7fdb9ad0a..49e7cc096 100644 --- a/src/m_fixed.h +++ b/src/m_fixed.h @@ -38,8 +38,20 @@ typedef INT32 fixed_t; /*! \brief convert fixed_t into floating number */ -#define FIXED_TO_FLOAT(x) (((float)(x)) / ((float)FRACUNIT)) -#define FLOAT_TO_FIXED(f) (fixed_t)((f) * ((float)FRACUNIT)) + +FUNCMATH FUNCINLINE static inline float FixedToFloat(fixed_t x) +{ + return x / (float)FRACUNIT; +} + +FUNCMATH FUNCINLINE static inline fixed_t FloatToFixed(float f) +{ + return (fixed_t)(f * FRACUNIT); +} + +// for backwards compat +#define FIXED_TO_FLOAT(x) FixedToFloat(x) // (((float)(x)) / ((float)FRACUNIT)) +#define FLOAT_TO_FIXED(f) FloatToFixed(f) // (fixed_t)((f) * ((float)FRACUNIT)) #if defined (__WATCOMC__) && FRACBITS == 16 From befae492fe996ddedd4e9c6e0feaaa74a3b889bd Mon Sep 17 00:00:00 2001 From: Monster Iestyn Date: Mon, 30 Mar 2020 20:25:56 +0100 Subject: [PATCH 51/54] use ATTRINLINE rather than inline to match some of the other functions in this file, though I'm told modern compilers ignore inlining info as they actually decide themselves now (though I could pretend we still support the older ones lol) --- src/m_fixed.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/m_fixed.h b/src/m_fixed.h index 49e7cc096..cc54c1aea 100644 --- a/src/m_fixed.h +++ b/src/m_fixed.h @@ -39,12 +39,12 @@ typedef INT32 fixed_t; \brief convert fixed_t into floating number */ -FUNCMATH FUNCINLINE static inline float FixedToFloat(fixed_t x) +FUNCMATH FUNCINLINE static ATTRINLINE float FixedToFloat(fixed_t x) { return x / (float)FRACUNIT; } -FUNCMATH FUNCINLINE static inline fixed_t FloatToFixed(float f) +FUNCMATH FUNCINLINE static ATTRINLINE fixed_t FloatToFixed(float f) { return (fixed_t)(f * FRACUNIT); } From ad12e29fda52e5b75c01f36fdecfdd1d65a3f4d5 Mon Sep 17 00:00:00 2001 From: Louis-Antoine Date: Mon, 30 Mar 2020 22:11:48 +0200 Subject: [PATCH 52/54] Fix allowjoin off affecting single player --- src/d_clisrv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index aa5d4cb9a..56ce21c3d 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1338,7 +1338,7 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime) netbuffer->u.serverinfo.numberofplayer = (UINT8)D_NumPlayers(); netbuffer->u.serverinfo.maxplayer = (UINT8)cv_maxplayers.value; - if (FindRejoinerNum(node) != -1) + if (!node || FindRejoinerNum(node) != -1) netbuffer->u.serverinfo.refusereason = 0; else if (!cv_allownewplayer.value) netbuffer->u.serverinfo.refusereason = 1; From 3074880ac1c7e5259998fb3df4c1372d7d386d75 Mon Sep 17 00:00:00 2001 From: sphere Date: Thu, 2 Apr 2020 16:07:38 +0200 Subject: [PATCH 53/54] Actually check for available maps correctly. Whoops! --- src/m_menu.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/m_menu.c b/src/m_menu.c index c6babc805..a40aea89e 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -7991,12 +7991,14 @@ static void M_SinglePlayerMenu(INT32 choice) { (void)choice; - if (M_GametypeHasLevels(LLM_RECORDATTACK)) + levellistmode = LLM_RECORDATTACK; + if (M_PrepareLevelPlatter(-1, true)) SP_MainMenu[sprecordattack].status = (M_SecretUnlocked(SECRET_RECORDATTACK)) ? IT_CALL|IT_STRING : IT_SECRET; else SP_MainMenu[sprecordattack].status = IT_NOTHING|IT_DISABLED; - if (M_GametypeHasLevels(LLM_NIGHTSATTACK)) + levellistmode = LLM_NIGHTSATTACK; + if (M_PrepareLevelPlatter(-1, true)) SP_MainMenu[spnightsmode].status = (M_SecretUnlocked(SECRET_NIGHTSMODE)) ? IT_CALL|IT_STRING : IT_SECRET; else SP_MainMenu[spnightsmode].status = IT_NOTHING|IT_DISABLED; From 806884a3f4cce2744cdf626982279e8ef3494f3f Mon Sep 17 00:00:00 2001 From: sphere Date: Thu, 2 Apr 2020 17:59:56 +0200 Subject: [PATCH 54/54] Use M_GametypeHasLevels again (properly this time) to save memory. --- src/m_menu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/m_menu.c b/src/m_menu.c index a40aea89e..4af94e77b 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -7992,13 +7992,13 @@ static void M_SinglePlayerMenu(INT32 choice) (void)choice; levellistmode = LLM_RECORDATTACK; - if (M_PrepareLevelPlatter(-1, true)) + if (M_GametypeHasLevels(-1)) SP_MainMenu[sprecordattack].status = (M_SecretUnlocked(SECRET_RECORDATTACK)) ? IT_CALL|IT_STRING : IT_SECRET; else SP_MainMenu[sprecordattack].status = IT_NOTHING|IT_DISABLED; levellistmode = LLM_NIGHTSATTACK; - if (M_PrepareLevelPlatter(-1, true)) + if (M_GametypeHasLevels(-1)) SP_MainMenu[spnightsmode].status = (M_SecretUnlocked(SECRET_NIGHTSMODE)) ? IT_CALL|IT_STRING : IT_SECRET; else SP_MainMenu[spnightsmode].status = IT_NOTHING|IT_DISABLED;