diff --git a/source/common/engine/startupinfo.h b/source/common/engine/startupinfo.h index f47fa351f..ec4ee2b30 100644 --- a/source/common/engine/startupinfo.h +++ b/source/common/engine/startupinfo.h @@ -9,11 +9,11 @@ struct FStartupInfo uint32_t FgColor; // Foreground color for title banner uint32_t BkColor; // Background color for title banner FString Song; + FString con; int Type; int LoadLights = -1; int LoadBrightmaps = -1; int LoadWidescreen = -1; - int modern = 0; enum { DefaultStartup, diff --git a/source/common/filesystem/file_directory.cpp b/source/common/filesystem/file_directory.cpp index 4f2c94eba..8c7b57876 100644 --- a/source/common/filesystem/file_directory.cpp +++ b/source/common/filesystem/file_directory.cpp @@ -154,13 +154,28 @@ int FDirectory::AddDirectory(const char *dirpath) } else { - if (strstr(fi, ".orig") || strstr(fi, ".bak")) + if (strstr(fi, ".orig") || strstr(fi, ".bak") || strstr(fi, ".cache")) { // We shouldn't add backup files to the file system continue; } size_t size = 0; FString fn = FString(dirpath) + fi; + + // The next one is courtesy of EDuke32. :( + // Putting cache files in the application directory is very bad style. + // Unfortunately, having a garbage file named "texture" present will cause serious problems down the line. + if (!stricmp(fi, "textures")) + { + FILE* f = fopen(fn, "rb"); + if (f) + { + char check[3]{}; + fread(check, 1, 3, f); + if (!memcmp(check, "LZ4", 3)) continue; + } + } + if (GetFileInfo(fn, &size, nullptr)) { AddEntry(fn, (int)size); diff --git a/source/core/automap.cpp b/source/core/automap.cpp index 258ad9089..fc68724d9 100644 --- a/source/core/automap.cpp +++ b/source/core/automap.cpp @@ -427,9 +427,12 @@ void drawredlines(int cposx, int cposy, int czoom, int cang) for (j = startwall, wal = &wall[startwall]; j < endwall; j++, wal++) { int k = wal->nextwall; - if (k < 0 || k >= MAXWALLS) continue; + if (k < 0 || k >= numwalls) continue; - if (sector[wal->nextsector].ceilingz == z1 && sector[wal->nextsector].floorz == z2) + int s = wal->nextsector; + if (s < 0 || s >= numsectors) continue; + + if (sector[s].ceilingz == z1 && sector[s].floorz == z2) if (((wal->cstat | wall[wal->nextwall].cstat) & (16 + 32)) == 0) continue; if (ShowRedLine(j, i)) diff --git a/source/core/gamecontrol.cpp b/source/core/gamecontrol.cpp index ceb42ca80..108693080 100644 --- a/source/core/gamecontrol.cpp +++ b/source/core/gamecontrol.cpp @@ -126,7 +126,7 @@ FMemArena dump; // this is for memory blocks than cannot be deallocated without InputState inputState; int ShowStartupWindow(TArray &); -FString GetGameFronUserFiles(); +TArray GetGameFronUserFiles(); void InitFileSystem(TArray&); void I_SetWindowTitle(const char* caption); void S_ParseSndInfo(); @@ -654,14 +654,25 @@ static TArray SetupGame() int groupno = -1; - // If the user has specified a file name, let's see if we know it. - // - FString game = GetGameFronUserFiles(); + auto game = GetGameFronUserFiles(); if (userConfig.gamegrp.IsEmpty()) { - userConfig.gamegrp = game; + for (auto& str : game) + { + for (auto& grp : groups) + { + if (grp.FileInfo.gameid.CompareNoCase(str) == 0) + { + userConfig.gamegrp = grp.FileName; + goto foundit; + } + } + } } + foundit: + // If the user has specified a file name, let's see if we know it. + // if (userConfig.gamegrp.Len()) { FString gamegrplower = "/" + userConfig.gamegrp.MakeLower(); @@ -757,7 +768,7 @@ static TArray SetupGame() if (ugroup.FileInfo.gamefilter.IsNotEmpty()) LumpFilter = ugroup.FileInfo.gamefilter; g_gameType |= ugroup.FileInfo.flags; } - if (userConfig.DefaultCon.IsEmpty()) userConfig.DefaultCon = selectedScript; + if (userConfig.DefaultCon.IsEmpty()) userConfig.DefaultCon = GameStartupInfo.con.IsNotEmpty()? GameStartupInfo.con : selectedScript; if (userConfig.DefaultDef.IsEmpty()) userConfig.DefaultDef = selectedDef; // This can only happen with a custom game that does not define any filter. diff --git a/source/core/gamecontrol.h b/source/core/gamecontrol.h index e05ff9650..f2917b396 100644 --- a/source/core/gamecontrol.h +++ b/source/core/gamecontrol.h @@ -139,6 +139,7 @@ struct GrpInfo FString defname; FString rtsname; FString gamefilter; + FString gameid; uint32_t CRC = 0; uint32_t dependencyCRC = 0; size_t size = 0; diff --git a/source/core/initfs.cpp b/source/core/initfs.cpp index 3ad2283f4..f511378ca 100644 --- a/source/core/initfs.cpp +++ b/source/core/initfs.cpp @@ -60,10 +60,10 @@ static const char* validexts[] = { "*.grp", "*.zip", "*.pk3", "*.pk4", "*.7z", " // //========================================================================== -static FString ParseGameInfo(TArray& pwads, const char* fn, const char* data, int size) +static TArray ParseGameInfo(TArray& pwads, const char* fn, const char* data, int size) { FScanner sc; - FString iwad; + TArray bases; int pos = 0; const char* lastSlash = strrchr(fn, '/'); @@ -78,7 +78,7 @@ static FString ParseGameInfo(TArray& pwads, const char* fn, const char* if (!nextKey.CompareNoCase("GAME")) { sc.MustGetString(); - iwad = sc.String; + bases.Push(sc.String); } else if (!nextKey.CompareNoCase("LOAD")) { @@ -122,10 +122,10 @@ static FString ParseGameInfo(TArray& pwads, const char* fn, const char* sc.MustGetString(); GameStartupInfo.BkColor = V_GetColor(NULL, sc); } - else if (!nextKey.CompareNoCase("MODERN")) + else if (!nextKey.CompareNoCase("CON")) { - sc.MustGetNumber(); - GameStartupInfo.modern = sc.Number ? 1 : -1; + sc.MustGetString(); + GameStartupInfo.con = sc.String;; } else { @@ -136,7 +136,7 @@ static FString ParseGameInfo(TArray& pwads, const char* fn, const char* } while (sc.CheckToken(',')); } } - return iwad; + return bases; } //========================================================================== // @@ -144,7 +144,7 @@ static FString ParseGameInfo(TArray& pwads, const char* fn, const char* // //========================================================================== -static FString CheckGameInfo(TArray& pwads) +static TArray CheckGameInfo(TArray& pwads) { // scan the list of WADs backwards to find the last one that contains a GAMEINFO lump for (int i = pwads.Size() - 1; i >= 0; i--) @@ -184,15 +184,15 @@ static FString CheckGameInfo(TArray& pwads) if (FName(lmp->getName(), true) == gameinfo) { // Found one! - FString iwad = ParseGameInfo(pwads, resfile->FileName, (const char*)lmp->Lock(), lmp->LumpSize); + auto bases = ParseGameInfo(pwads, resfile->FileName, (const char*)lmp->Lock(), lmp->LumpSize); delete resfile; - return iwad; + return bases; } } delete resfile; } } - return ""; + return TArray(); } //========================================================================== @@ -201,7 +201,7 @@ static FString CheckGameInfo(TArray& pwads) // //========================================================================== -FString GetGameFronUserFiles() +TArray GetGameFronUserFiles() { TArray Files; diff --git a/source/core/menu/razemenu.cpp b/source/core/menu/razemenu.cpp index 528a9175b..6895f7d22 100644 --- a/source/core/menu/razemenu.cpp +++ b/source/core/menu/razemenu.cpp @@ -437,7 +437,7 @@ static void BuildEpisodeMenu() { ld->mItems.Pop(popped); } - + if (isBlood()) gDefaultSkill = 2; ld->mSelectedItem = gDefaultSkill + ld->mItems.Size(); // account for pre-added items int y = ld->mYpos; diff --git a/source/core/searchpaths.cpp b/source/core/searchpaths.cpp index 750e0e7b6..d3f998ac2 100644 --- a/source/core/searchpaths.cpp +++ b/source/core/searchpaths.cpp @@ -460,6 +460,11 @@ static TArray ParseGrpInfo(const char *fn, FileReader &fr, TMapangle, input.avel, &sPlayerInput[nLocalPlayer].actions, scaleAdjust); sethorizon(&pPlayer->horizon, input.horz, &sPlayerInput[nLocalPlayer].actions, scaleAdjust); + + if (input.horz) + { + pPlayer->bPlayerPan = pPlayer->bLockPan = true; + } } pPlayer->angle.processhelpers(scaleAdjust); diff --git a/source/games/exhumed/src/player.cpp b/source/games/exhumed/src/player.cpp index 322fb6ecf..96df6d5b5 100644 --- a/source/games/exhumed/src/player.cpp +++ b/source/games/exhumed/src/player.cpp @@ -436,7 +436,7 @@ void RestartPlayer(short nPlayer) nYDamage[nPlayer] = 0; nXDamage[nPlayer] = 0; - plr->horizon.ohoriz = plr->horizon.horiz = q16horiz(0); + plr->nDestVertPan = plr->horizon.ohoriz = plr->horizon.horiz = q16horiz(0); nBreathTimer[nPlayer] = 90; nTauntTimer[nPlayer] = RandomSize(3) + 3; @@ -456,6 +456,8 @@ void RestartPlayer(short nPlayer) SetMagicFrame(); RestoreGreenPal(); + + plr->bPlayerPan = plr->bLockPan = false; } ototalvel[nPlayer] = totalvel[nPlayer] = 0; @@ -1030,13 +1032,14 @@ void FuncPlayer(int a, int nDamage, int nRun) StopLocalSound(); InitSpiritHead(); + PlayerList[nPlayer].nDestVertPan = q16horiz(0); if (currentLevel->levelNumber == 11) { - PlayerList[nPlayer].horizon.settarget(46); + PlayerList[nPlayer].nDestVertPan = q16horiz(46); } else { - PlayerList[nPlayer].horizon.settarget(11); + PlayerList[nPlayer].nDestVertPan = q16horiz(11); } } } @@ -1064,7 +1067,7 @@ void FuncPlayer(int a, int nDamage, int nRun) zVelB = -zVelB; } - if (zVelB > 512 && PlayerList[nPlayer].horizon.horiz.asq16() != 0 && (sPlayerInput[nPlayer].actions & (SB_AIMMODE))) { + if (zVelB > 512 && !PlayerList[nPlayer].horizon.horiz.asq16() && !(sPlayerInput[nPlayer].actions & SB_AIMMODE)) { sPlayerInput[nPlayer].actions |= SB_CENTERVIEW; } } @@ -1163,29 +1166,9 @@ void FuncPlayer(int a, int nDamage, int nRun) } sectdone: - if (!PlayerList[nPlayer].horizon.horiz.asbuild() || PlayerList[nPlayer].bIsFalling) + if (!PlayerList[nPlayer].bPlayerPan && !PlayerList[nPlayer].bLockPan) { - // Calculate base pan amount based on how much the player is falling. - fixed_t dVertPan = (spr_z - sprite[nPlayerSprite].z) << 9; - if (!bUnderwater && dVertPan != 0) - { - fixed_t adjustment; - - if (dVertPan >= IntToFixed(4)) - adjustment = 4; - else if (dVertPan <= -IntToFixed(4)) - adjustment = -4; - else - adjustment = dVertPan << 1; - - PlayerList[nPlayer].horizon.addadjustment(adjustment); - PlayerList[nPlayer].bIsFalling = true; - } - else - { - sPlayerInput[nPlayer].actions |= SB_CENTERVIEW; - PlayerList[nPlayer].bIsFalling = false; - } + PlayerList[nPlayer].nDestVertPan = q16horiz(clamp((spr_z - sprite[nPlayerSprite].z) << 9, gi->playerHorizMin(), gi->playerHorizMax())); } playerX -= sprite[nPlayerSprite].x; @@ -2653,11 +2636,40 @@ loc_1BD2E: PlayerList[nPlayer].field_2 = 0; } + Player* pPlayer = &PlayerList[nPlayer]; + + if (actions & (SB_LOOK_UP | SB_LOOK_DOWN)) + { + pPlayer->nDestVertPan = pPlayer->horizon.horiz; + pPlayer->bPlayerPan = pPlayer->bLockPan = true; + } + else if (actions & (SB_AIM_UP | SB_AIM_DOWN | SB_CENTERVIEW)) + { + pPlayer->nDestVertPan = pPlayer->horizon.horiz; + pPlayer->bPlayerPan = pPlayer->bLockPan = false; + } + if (SyncInput()) { - Player* pPlayer = &PlayerList[nPlayer]; sethorizon(&pPlayer->horizon, sPlayerInput[nPlayer].pan, &sPlayerInput[nLocalPlayer].actions); } + + if (sPlayerInput[nPlayer].pan) + { + pPlayer->nDestVertPan = pPlayer->horizon.horiz; + pPlayer->bPlayerPan = pPlayer->bLockPan = true; + } + + if (totalvel[nPlayer] > 20) + { + pPlayer->bPlayerPan = false; + } + + double nVertPan = (pPlayer->nDestVertPan - pPlayer->horizon.horiz).asq16() * (1. / (FRACUNIT << 2)); + if (nVertPan != 0) + { + pPlayer->horizon.addadjustment(abs(nVertPan) >= 4 ? clamp(nVertPan, -4, 4) : nVertPan * 2.); + } } else // else, player's health is less than 0 { @@ -2849,7 +2861,6 @@ FSerializer& Serialize(FSerializer& arc, const char* keyname, Player& w, Player* ("field38", w.field_38) ("field3a", w.field_3A) ("field3c", w.field_3C) - ("bIsFalling", w.bIsFalling) ("seq", w.nSeq) ("horizon", w.horizon) ("angle", w.angle) diff --git a/source/games/exhumed/src/player.h b/source/games/exhumed/src/player.h index 577883603..0715cdbd2 100644 --- a/source/games/exhumed/src/player.h +++ b/source/games/exhumed/src/player.h @@ -71,7 +71,8 @@ struct Player short field_3A; short field_3C; short nRun; - bool bIsFalling; + bool bPlayerPan, bLockPan; + fixedhoriz nDestVertPan; PlayerHorizon horizon; PlayerAngle angle; diff --git a/wadsrc/static/engine/grpinfo.txt b/wadsrc/static/engine/grpinfo.txt index 82b2b1e26..9dcf1b2d7 100644 --- a/wadsrc/static/engine/grpinfo.txt +++ b/wadsrc/static/engine/grpinfo.txt @@ -4,7 +4,7 @@ CRC DUKEKR_CRC 0xAA4F6A40 DUKE15_CRC 0xFD3DCFF1 DUKEPP_CRC 0xF514A6AC - DUKEWT_CRC 0x982AFE4A + DukeWorldTour_CRC 0x982AFE4A DUKE099_CRC 0x02F18900 DUKE10_CRC 0xA28AA589 DUKE11_CRC 0x912E1E8D @@ -54,6 +54,7 @@ grpinfo dependency 0 FgColor 0xffff00 BkColor 0xff6f00 + GameID "Duke13" } grpinfo @@ -66,6 +67,7 @@ grpinfo dependency 0 FgColor 0xffff00 BkColor 0xff6f00 + GameID "Duke13" } grpinfo @@ -78,18 +80,20 @@ grpinfo dependency 0 FgColor 0xffff00 BkColor 0xff6f00 + GameID "Duke3D" } grpinfo { name "Duke Nukem 3D: Atomic Edition (WT)" size 44356548 - crc DUKEWT_CRC + crc DukeWorldTour_CRC flags GAMEFLAG_DUKE gamefilter "Duke.Duke" dependency 0 FgColor 0xffff00 BkColor 0xff6f00 + GameID "DukeWorldTour" } grpinfo // another variant? I got this one, dated 2016-10-12 @@ -102,6 +106,7 @@ grpinfo // another variant? I got this one, dated 2016-10-12 dependency 0 FgColor 0xffff00 BkColor 0xff6f00 + GameID "DukeWorldTour" } grpinfo @@ -114,6 +119,7 @@ grpinfo dependency 0 FgColor 0xffff00 BkColor 0xff6f00 + GameID "Duke3D" } grpinfo @@ -196,6 +202,7 @@ grpinfo flags GAMEFLAG_DUKE|GAMEFLAG_ADDON|GAMEFLAG_DUKEDC dependency DUKE13_CRC gamefilter "Duke.DukeDC" + GameID "DukeDC13" } grpinfo @@ -206,6 +213,7 @@ grpinfo flags GAMEFLAG_DUKE|GAMEFLAG_ADDON|GAMEFLAG_DUKEDC dependency DUKE15_CRC gamefilter "Duke.DukeDC" + GameID "DukeDC" } grpinfo @@ -216,6 +224,7 @@ grpinfo flags GAMEFLAG_DUKE|GAMEFLAG_ADDON|GAMEFLAG_DUKEDC dependency DUKE15_CRC gamefilter "Duke.DukeDC" + GameID "DukeDC" } grpinfo @@ -227,6 +236,7 @@ grpinfo flags GAMEFLAG_DUKE|GAMEFLAG_ADDON|GAMEFLAG_DUKEDC dependency DUKE15_CRC gamefilter "Duke.DukeDC" + GameID "DukeDC" } // These are a bit strange but without anything to check I do not know in what way. @@ -244,6 +254,7 @@ grpinfo gamefilter "Duke.Vacation" FgColor 0x00004f BkColor 0x8f8fff + GameID "DukeVacation" } grpinfo @@ -257,6 +268,7 @@ grpinfo gamefilter "Duke.Vacation" FgColor 0x00004f BkColor 0x8f8fff + GameID "DukeVacation" } grpinfo @@ -270,6 +282,7 @@ grpinfo gamefilter "Duke.NWinter" FgColor 0 BkColor 0x8f8f8f + GameID "NuclearWinter" } grpinfo @@ -291,6 +304,7 @@ grpinfo dependency DUKE15_CRC mustcontain "FIREFLYTROOPER.CON", "FLAMETHROWER.CON", "music/E5L1_BulletDam.ogg", "sound/VO_E5L1_Duke_CreamAndSugar.ogg" gamefilter "Duke.Worldtour" + GameID "DukeWorldTour" } grpinfo @@ -339,6 +353,7 @@ grpinfo gamefilter "Blood.Blood" FgColor 0 BkColor 0x7f003f + GameID "Blood10" } grpinfo @@ -354,6 +369,7 @@ grpinfo gamefilter "Blood.Blood" FgColor 0 BkColor 0xaf0024 + GameID "Blood11" } grpinfo @@ -369,6 +385,7 @@ grpinfo gamefilter "Blood.Blood" FgColor 0 BkColor 0xaf0024 + GameID "Blood" } grpinfo @@ -384,6 +401,7 @@ grpinfo gamefilter "Blood.Blood" FgColor 0 BkColor 0x7f002f + GameID "Blood" } /* this doesn't work with the current setup. @@ -411,6 +429,7 @@ grpinfo dependency BLOOD_CRC loadart "CPART07.AR_", "CPART15.AR_" gamefilter "Blood.Cryptic" + GameID "CyrpticPassage" } addon @@ -423,6 +442,7 @@ addon dependency BLOOD_CRC loadart "CPART07.AR_", "CPART15.AR_" gamefilter "Blood.Cryptic" + GameID "CyrpticPassage" } addon @@ -436,6 +456,7 @@ addon dependency BLOOD_CRC loadart "CPART07.AR_", "CPART15.AR_" gamefilter "Blood.Cryptic" + GameID "CyrpticPassage" } addon @@ -448,6 +469,7 @@ addon flags GAMEFLAG_BLOOD|GAMEFLAG_ADDON dependency BLOOD_CRC gamefilter "Blood.Cryptic" + GameID "CyrpticPassage" } grpinfo @@ -460,6 +482,7 @@ grpinfo gamefilter "ShadowWarrior.ShadowWarrior" FgColor 0 BkColor 0xaf0000 + GameID "ShadowWarrior" } grpinfo @@ -472,6 +495,7 @@ grpinfo gamefilter "ShadowWarrior.ShadowWarrior" FgColor 0 BkColor 0xaf0000 + GameID "ShadowWarrior" } grpinfo @@ -484,6 +508,7 @@ grpinfo gamefilter "ShadowWarrior.ShadowWarrior" FgColor 0 BkColor 0xaf0000 + GameID "ShadowWarrior" } grpinfo @@ -496,6 +521,7 @@ grpinfo gamefilter "ShadowWarrior.ShadowWarrior" FgColor 0 BkColor 0xaf0000 + GameID "ShadowWarrior" } grpinfo @@ -555,6 +581,7 @@ grpinfo defname "sw.def" dependency SWREG12_CRC gamefilter "ShadowWarrior.Wanton" + GameID "WantonDestruction" } grpinfo @@ -566,6 +593,7 @@ grpinfo defname "sw.def" dependency SWREG12_CRC gamefilter "ShadowWarrior.Wanton" + GameID "WantonDestruction" } grpinfo @@ -578,6 +606,7 @@ grpinfo dependency SWREG12_CRC gamefilter "ShadowWarrior.Wanton" deletecontent "swcustom.txt" // not localizable and also not present in the alternative packages. + GameID "WantonDestruction" } grpinfo @@ -590,6 +619,7 @@ grpinfo dependency SWREG12_CRC gamefilter "ShadowWarrior.TwinDragon" deletecontent "swcustom.txt" // not localizable and also not present in the alternative package. + GameID "TwinDragon" } grpinfo @@ -601,6 +631,7 @@ grpinfo defname "twindrag.def" dependency SWREG12_CRC gamefilter "ShadowWarrior.TwinDragon" + GameID "TwinDragon" } grpinfo @@ -612,6 +643,7 @@ grpinfo defname "twindrag.def" dependency SWREG12_CRC gamefilter "ShadowWarrior.TwinDragon" + GameID "TwinDragon" } grpinfo @@ -625,6 +657,7 @@ grpinfo gamefilter "Redneck.Redneck" FgColor 0xbc9e89 BkColor 0x7f3300 + GameID "Redneck" } grpinfo @@ -638,6 +671,7 @@ grpinfo gamefilter "Redneck.RidesAgain" FgColor 0xbc9e89 BkColor 0x7f3300 + GameID "RedneckRides" } grpinfo @@ -651,6 +685,7 @@ grpinfo loadart "TILESA66.ART", "TILESB66.ART" // replaces TILES009 and TILES023. gamefilter "Redneck.Route66" deletecontent "turd66.anm*turdmov.anm", "turd66.voc*turdmov.voc", "end66.anm*rr_outro.anm", "end66.voc*rr_outro.voc" // This renames instead of deleting + GameID "Route66" } addon @@ -664,6 +699,7 @@ addon loadart "TILESA66.ART", "TILESB66.ART" // replaces TILES009 and TILES023. gamefilter "Redneck.Route66" deletecontent "turd66.anm*turdmov.anm", "turd66.voc*turdmov.voc", "end66.anm*rr_outro.anm", "end66.voc*rr_outro.voc" // This renames instead of deleting + GameID "Route66" } grpinfo @@ -676,6 +712,7 @@ grpinfo gamefilter "Nam.Nam" FgColor 0 BkColor 0x78720d + GameID "Nam" } grpinfo @@ -688,6 +725,7 @@ grpinfo gamefilter "Nam.Napalm" FgColor 0 BkColor 0x78720d + GameID "Nam" } grpinfo @@ -700,6 +738,7 @@ grpinfo gamefilter "WW2GI.WW2GI" FgColor 0 BkColor 0x78720d + GameID "WW2GI" } grpinfo @@ -711,6 +750,7 @@ grpinfo flags GAMEFLAG_WW2GI|GAMEFLAG_ADDON dependency WW2GI_CRC gamefilter "WW2GI.Platoon" + GameID "PlatoonLeader" } grpinfo @@ -723,6 +763,7 @@ grpinfo gamefilter "Exhumed.Powerslave" FgColor 0xff0000 BkColor 0xffff33 + GameID "Exhumed" } grpinfo @@ -735,6 +776,7 @@ grpinfo gamefilter "Exhumed.Exhumed" FgColor 0xff0000 BkColor 0xffff33 + GameID "Exhumed" } grpinfo diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 907b26f2d..4ddf92bca 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -1888,7 +1888,7 @@ OptionMenu "EngineCredits2" OptionMenu "EngineCredits2a" { title "$MNU_CREDITS" - Submenu " ---->", "EngineCredits3" + Submenu " ---->", "EngineCredits2b" StaticText "RedNukem / NBlood", 1 StaticText "Alexey \"Nuke.YKT\" Skrybykin", 0 StaticText "" @@ -1900,6 +1900,16 @@ OptionMenu "EngineCredits2a" StaticText "sirlemonhead", 0 } +OptionMenu "EngineCredits2b" +{ + title "$MNU_CREDITS" + Submenu " ---->", "EngineCredits3" + StaticText "Widescreen graphics", 1 + StaticText "James Manning   fgsfds            ", 0 + StaticText "Dzierzan    Phredreeke      ", 0 + StaticText "MaxED        Kinsie   ", 0 +} + OptionMenu "EngineCredits3" { title "$MNU_CREDITS"