From c35b6d32515bd56576d97869c0846c30608a359a Mon Sep 17 00:00:00 2001 From: staphen Date: Tue, 5 Mar 2024 20:58:09 -0500 Subject: [PATCH 1/2] Rename glSeedTbl --- Source/control.cpp | 4 ++-- Source/diablo.cpp | 14 +++++++------- Source/diablo.h | 2 +- Source/loadsave.cpp | 6 +++--- Source/lua/modules/dev/level.cpp | 8 ++++---- Source/msg.cpp | 2 +- Source/multi.cpp | 2 +- Source/objects.cpp | 2 +- Source/quests.cpp | 2 +- Source/stores.cpp | 2 +- test/fixtures/memory_map/levelSeed.txt | 2 +- 11 files changed, 23 insertions(+), 23 deletions(-) diff --git a/Source/control.cpp b/Source/control.cpp index 7919cff8d78..23324748803 100644 --- a/Source/control.cpp +++ b/Source/control.cpp @@ -536,7 +536,7 @@ std::string TextCmdLevelSeed(const std::string_view parameter) return StrCat( "Seedinfo for ", levelType, " ", currlevel, "\n", - "seed: ", glSeedTbl[currlevel], "\n", + "seed: ", DungeonSeeds[currlevel], "\n", #ifdef _DEBUG "Mid1: ", glMid1Seed[currlevel], "\n", "Mid2: ", glMid2Seed[currlevel], "\n", @@ -546,7 +546,7 @@ std::string TextCmdLevelSeed(const std::string_view parameter) "\n", gameId, " ", mode, "\n", questPool, " quests: ", questFlags, "\n", - "Storybook: ", glSeedTbl[16]); + "Storybook: ", DungeonSeeds[16]); } std::vector TextCmdList = { diff --git a/Source/diablo.cpp b/Source/diablo.cpp index fe3a610e6ea..3d4794091f6 100644 --- a/Source/diablo.cpp +++ b/Source/diablo.cpp @@ -106,7 +106,7 @@ namespace devilution { -uint32_t glSeedTbl[NUMLEVELS]; +uint32_t DungeonSeeds[NUMLEVELS]; Point MousePosition; bool gbRunGame; bool gbRunGameResult; @@ -1339,7 +1339,7 @@ void LoadAllGFX() */ void CreateLevel(lvl_entry entry) { - CreateDungeon(glSeedTbl[currlevel], entry); + CreateDungeon(DungeonSeeds[currlevel], entry); switch (leveltype) { case DTYPE_TOWN: @@ -2837,7 +2837,7 @@ void LoadGameLevel(bool firstflag, lvl_entry lvldir) if (pcurs > CURSOR_HAND && pcurs < CURSOR_FIRSTITEM) { NewCursor(CURSOR_HAND); } - SetRndSeed(glSeedTbl[currlevel]); + SetRndSeed(DungeonSeeds[currlevel]); IncProgress(); MakeLightTable(); SetDungeonMicros(); @@ -2857,12 +2857,12 @@ void LoadGameLevel(bool firstflag, lvl_entry lvldir) InitAutomapOnce(); } if (!setlevel) { - SetRndSeed(glSeedTbl[currlevel]); + SetRndSeed(DungeonSeeds[currlevel]); } else { // Maps are not randomly generated, but the monsters max hitpoints are. // So we need to ensure that we have a stable seed when generating quest/set-maps. // For this purpose we reuse the normal dungeon seeds. - SetRndSeed(glSeedTbl[static_cast(setlvlnum)]); + SetRndSeed(DungeonSeeds[static_cast(setlvlnum)]); } if (leveltype == DTYPE_TOWN) { @@ -2894,7 +2894,7 @@ void LoadGameLevel(bool firstflag, lvl_entry lvldir) CreateLevel(lvldir); IncProgress(); LoadLevelSOLData(); - SetRndSeed(glSeedTbl[currlevel]); + SetRndSeed(DungeonSeeds[currlevel]); if (leveltype != DTYPE_TOWN) { GetLevelMTypes(); @@ -2942,7 +2942,7 @@ void LoadGameLevel(bool firstflag, lvl_entry lvldir) visited = visited || player._pLvlVisited[currlevel]; } - SetRndSeed(glSeedTbl[currlevel]); + SetRndSeed(DungeonSeeds[currlevel]); if (leveltype != DTYPE_TOWN) { if (firstflag || lvldir == ENTRY_LOAD || !myPlayer._pLvlVisited[currlevel] || gbIsMultiplayer) { diff --git a/Source/diablo.h b/Source/diablo.h index a0116a14c62..974efe412a9 100644 --- a/Source/diablo.h +++ b/Source/diablo.h @@ -58,7 +58,7 @@ enum class MouseActionType : uint8_t { OperateObject, }; -extern uint32_t glSeedTbl[NUMLEVELS]; +extern uint32_t DungeonSeeds[NUMLEVELS]; extern Point MousePosition; extern DVL_API_FOR_TEST bool gbRunGame; extern bool gbRunGameResult; diff --git a/Source/loadsave.cpp b/Source/loadsave.cpp index 8bcfb44b371..7fd17164073 100644 --- a/Source/loadsave.cpp +++ b/Source/loadsave.cpp @@ -2136,7 +2136,7 @@ void LoadGame(bool firstflag) app_fatal(_("Player is on a Hellfire only level")); for (uint8_t i = 0; i < giNumberOfLevels; i++) { - glSeedTbl[i] = file.NextBE(); + DungeonSeeds[i] = file.NextBE(); file.Skip(4); // Skip loading gnLevelTypeTbl } @@ -2418,7 +2418,7 @@ void SaveGameData(SaveWriter &saveWriter) file.WriteBE(ActiveObjectCount); for (uint8_t i = 0; i < giNumberOfLevels; i++) { - file.WriteBE(glSeedTbl[i]); + file.WriteBE(DungeonSeeds[i]); file.WriteBE(getHellfireLevelType(GetLevelType(i))); } @@ -2554,7 +2554,7 @@ void SaveLevel(SaveWriter &saveWriter) DoUnVision(myPlayer.position.tile, myPlayer._pLightRad); // fix for vision staying on the level if (leveltype == DTYPE_TOWN) - glSeedTbl[0] = AdvanceRndSeed(); + DungeonSeeds[0] = AdvanceRndSeed(); char szName[MaxMpqPathSize]; GetTempLevelNames(szName); diff --git a/Source/lua/modules/dev/level.cpp b/Source/lua/modules/dev/level.cpp index be3d6b6d00b..e5bc2278444 100644 --- a/Source/lua/modules/dev/level.cpp +++ b/Source/lua/modules/dev/level.cpp @@ -25,7 +25,7 @@ namespace { std::string ExportDun() { - const std::string levelName = StrCat(currlevel, "-", glSeedTbl[currlevel], ".dun"); + const std::string levelName = StrCat(currlevel, "-", DungeonSeeds[currlevel], ".dun"); FILE *dunFile = OpenFile(levelName.c_str(), "ab"); WriteLE16(dunFile, DMAXX); @@ -101,7 +101,7 @@ std::string DebugCmdResetLevel(uint8_t level, std::optional seed) DeltaClearLevel(level); if (seed.has_value()) { - glSeedTbl[level] = *seed; + DungeonSeeds[level] = *seed; return StrCat("Successfully reset level ", level, " with seed ", *seed, "."); } return StrCat("Successfully reset level ", level, "."); @@ -109,11 +109,11 @@ std::string DebugCmdResetLevel(uint8_t level, std::optional seed) std::string DebugCmdLevelSeed(std::optional level) { - constexpr size_t NumLevels = sizeof(glSeedTbl) / sizeof(glSeedTbl[0]); + constexpr size_t NumLevels = sizeof(DungeonSeeds) / sizeof(DungeonSeeds[0]); if (level.has_value() && *level >= NumLevels) { return StrCat("level out of range, max: ", NumLevels - 1); } - return StrCat(glSeedTbl[level.value_or(currlevel)]); + return StrCat(DungeonSeeds[level.value_or(currlevel)]); } } // namespace diff --git a/Source/msg.cpp b/Source/msg.cpp index 8d5cb7fc3d9..5019b182ffb 100644 --- a/Source/msg.cpp +++ b/Source/msg.cpp @@ -750,7 +750,7 @@ void DeltaLeaveSync(uint8_t bLevel) if (!gbIsMultiplayer) return; if (leveltype == DTYPE_TOWN) { - glSeedTbl[0] = AdvanceRndSeed(); + DungeonSeeds[0] = AdvanceRndSeed(); return; } diff --git a/Source/multi.cpp b/Source/multi.cpp index 91b85afabd1..ad0cbd86dd7 100644 --- a/Source/multi.cpp +++ b/Source/multi.cpp @@ -791,7 +791,7 @@ bool NetInit(bool bSinglePlayer) gnTickDelay = 1000 / sgGameInitInfo.nTickRate; for (int i = 0; i < NUMLEVELS; i++) { - glSeedTbl[i] = AdvanceRndSeed(); + DungeonSeeds[i] = AdvanceRndSeed(); } PublicGame = DvlNet_IsPublicGame(); diff --git a/Source/objects.cpp b/Source/objects.cpp index 0268c5140bb..236a471fe60 100644 --- a/Source/objects.cpp +++ b/Source/objects.cpp @@ -1375,7 +1375,7 @@ void AddPedestalOfBlood(Object &pedestalOfBlood) void AddStoryBook(Object &storyBook) { - storyBook._oVar1 = (glSeedTbl[16] >> 16) % 3; + storyBook._oVar1 = (DungeonSeeds[16] >> 16) % 3; if (currlevel == 4) storyBook._oVar2 = StoryText[storyBook._oVar1][0]; else if (currlevel == 8) diff --git a/Source/quests.cpp b/Source/quests.cpp index b84cfe9650e..1bc5d5e7cea 100644 --- a/Source/quests.cpp +++ b/Source/quests.cpp @@ -275,7 +275,7 @@ void InitQuests() if (!UseMultiplayerQuests() && *sgOptions.Gameplay.randomizeQuests) { // Quests are set from the seed used to generate level 16. - InitialiseQuestPools(glSeedTbl[15], Quests); + InitialiseQuestPools(DungeonSeeds[15], Quests); } if (gbIsSpawn) { diff --git a/Source/stores.cpp b/Source/stores.cpp index c37dff5cef7..0cdd99e44bb 100644 --- a/Source/stores.cpp +++ b/Source/stores.cpp @@ -2135,7 +2135,7 @@ void SetupTownStores() l = i; } } else { - SetRndSeed(glSeedTbl[currlevel] * SDL_GetTicks()); + SetRndSeed(DungeonSeeds[currlevel] * SDL_GetTicks()); } l = std::clamp(l + 2, 6, 16); diff --git a/test/fixtures/memory_map/levelSeed.txt b/test/fixtures/memory_map/levelSeed.txt index 83ab4ff6abe..0a0561b80a4 100644 --- a/test/fixtures/memory_map/levelSeed.txt +++ b/test/fixtures/memory_map/levelSeed.txt @@ -1,2 +1,2 @@ -R 32 glSeedTbl +R 32 DungeonSeeds R 32 gnLevelTypeTbl From 6f13c310ee4d526e66184e3bdb923dafd1043c36 Mon Sep 17 00:00:00 2001 From: staphen Date: Tue, 5 Mar 2024 21:23:22 -0500 Subject: [PATCH 2/2] Capture the level seed and use it to skip failed dungeon layouts --- Source/diablo.cpp | 1 + Source/diablo.h | 1 + Source/levels/drlg_l1.cpp | 4 ++++ Source/levels/drlg_l2.cpp | 4 ++++ Source/levels/drlg_l3.cpp | 5 +++++ Source/levels/drlg_l4.cpp | 5 +++++ Source/loadsave.cpp | 28 ++++++++++++++++++++++++++++ Source/lua/modules/dev/level.cpp | 1 + Source/multi.cpp | 1 + 9 files changed, 50 insertions(+) diff --git a/Source/diablo.cpp b/Source/diablo.cpp index 3d4794091f6..ab71edbe72c 100644 --- a/Source/diablo.cpp +++ b/Source/diablo.cpp @@ -107,6 +107,7 @@ namespace devilution { uint32_t DungeonSeeds[NUMLEVELS]; +std::optional LevelSeeds[NUMLEVELS]; Point MousePosition; bool gbRunGame; bool gbRunGameResult; diff --git a/Source/diablo.h b/Source/diablo.h index 974efe412a9..4375bebd820 100644 --- a/Source/diablo.h +++ b/Source/diablo.h @@ -59,6 +59,7 @@ enum class MouseActionType : uint8_t { }; extern uint32_t DungeonSeeds[NUMLEVELS]; +extern std::optional LevelSeeds[NUMLEVELS]; extern Point MousePosition; extern DVL_API_FOR_TEST bool gbRunGame; extern bool gbRunGameResult; diff --git a/Source/levels/drlg_l1.cpp b/Source/levels/drlg_l1.cpp index 5d1b8f5c832..f12bbc064dd 100644 --- a/Source/levels/drlg_l1.cpp +++ b/Source/levels/drlg_l1.cpp @@ -1165,6 +1165,9 @@ bool PlaceStairs(lvl_entry entry) void GenerateLevel(lvl_entry entry) { + if (LevelSeeds[currlevel]) + SetRndSeed(*LevelSeeds[currlevel]); + size_t minarea = 761; switch (currlevel) { case 1: @@ -1181,6 +1184,7 @@ void GenerateLevel(lvl_entry entry) DRLG_InitTrans(); do { + LevelSeeds[currlevel] = GetLCGEngineState(); FirstRoom(); } while (FindArea() < minarea); diff --git a/Source/levels/drlg_l2.cpp b/Source/levels/drlg_l2.cpp index b2093da7b3f..b01f9659337 100644 --- a/Source/levels/drlg_l2.cpp +++ b/Source/levels/drlg_l2.cpp @@ -2669,7 +2669,11 @@ bool PlaceStairs(lvl_entry entry) void GenerateLevel(lvl_entry entry) { + if (LevelSeeds[currlevel]) + SetRndSeed(*LevelSeeds[currlevel]); + while (true) { + LevelSeeds[currlevel] = GetLCGEngineState(); nRoomCnt = 0; InitDungeonFlags(); DRLG_InitTrans(); diff --git a/Source/levels/drlg_l3.cpp b/Source/levels/drlg_l3.cpp index 1c6801b99dd..f3590c6cc82 100644 --- a/Source/levels/drlg_l3.cpp +++ b/Source/levels/drlg_l3.cpp @@ -12,6 +12,7 @@ #include "monster.h" #include "objdat.h" #include "objects.h" +#include "player.h" #include "quests.h" namespace devilution { @@ -1988,7 +1989,11 @@ bool PlaceStairs(lvl_entry entry) void GenerateLevel(lvl_entry entry) { + if (LevelSeeds[currlevel]) + SetRndSeed(*LevelSeeds[currlevel]); + while (true) { + LevelSeeds[currlevel] = GetLCGEngineState(); InitDungeonFlags(); int x1 = GenerateRnd(20) + 10; int y1 = GenerateRnd(20) + 10; diff --git a/Source/levels/drlg_l4.cpp b/Source/levels/drlg_l4.cpp index 883d0e0486f..d7260aa1c58 100644 --- a/Source/levels/drlg_l4.cpp +++ b/Source/levels/drlg_l4.cpp @@ -13,6 +13,7 @@ #include "monster.h" #include "multi.h" #include "objdat.h" +#include "player.h" namespace devilution { @@ -1138,11 +1139,15 @@ bool PlaceStairs(lvl_entry entry) void GenerateLevel(lvl_entry entry) { + if (LevelSeeds[currlevel]) + SetRndSeed(*LevelSeeds[currlevel]); + while (true) { DRLG_InitTrans(); constexpr size_t Minarea = 692; do { + LevelSeeds[currlevel] = GetLCGEngineState(); InitDungeonFlags(); FirstRoom(); CloseOuterBorders(); diff --git a/Source/loadsave.cpp b/Source/loadsave.cpp index 7fd17164073..de0494e9c11 100644 --- a/Source/loadsave.cpp +++ b/Source/loadsave.cpp @@ -2027,6 +2027,21 @@ void SaveHotkeys(SaveWriter &saveWriter, const Player &player) file.WriteLE(static_cast(player._pRSplType)); } +void LoadLevelSeeds() +{ + LoadHelper file(OpenSaveArchive(gSaveNumber), "levelseeds"); + if (!file.IsValid()) + return; + + for (int i = 0; i < giNumberOfLevels; i++) { + if (file.NextLE() != 0) { + LevelSeeds[i] = file.NextLE(); + } else { + LevelSeeds[i] = std::nullopt; + } + } +} + void LoadHeroItems(Player &player) { LoadHelper file(OpenSaveArchive(gSaveNumber), "heroitems"); @@ -2137,6 +2152,7 @@ void LoadGame(bool firstflag) for (uint8_t i = 0; i < giNumberOfLevels; i++) { DungeonSeeds[i] = file.NextBE(); + LevelSeeds[i] = std::nullopt; file.Skip(4); // Skip loading gnLevelTypeTbl } @@ -2304,6 +2320,18 @@ void LoadGame(bool firstflag) gbIsHellfireSaveGame = gbIsHellfire; } +void SaveLevelSeeds(SaveWriter &saveWriter) +{ + SaveHelper file(saveWriter, "levelseeds", giNumberOfLevels * (sizeof(uint8_t) + sizeof(uint32_t))); + + for (int i = 0; i < giNumberOfLevels; i++) { + file.WriteLE(LevelSeeds[i] ? 1 : 0); + if (LevelSeeds[i]) { + file.WriteLE(*LevelSeeds[i]); + } + } +} + void SaveHeroItems(SaveWriter &saveWriter, Player &player) { size_t itemCount = static_cast(NUM_INVLOC) + InventoryGridCells + MaxBeltItems; diff --git a/Source/lua/modules/dev/level.cpp b/Source/lua/modules/dev/level.cpp index e5bc2278444..0a102a3a1f6 100644 --- a/Source/lua/modules/dev/level.cpp +++ b/Source/lua/modules/dev/level.cpp @@ -102,6 +102,7 @@ std::string DebugCmdResetLevel(uint8_t level, std::optional seed) if (seed.has_value()) { DungeonSeeds[level] = *seed; + LevelSeeds[level] = std::nullopt; return StrCat("Successfully reset level ", level, " with seed ", *seed, "."); } return StrCat("Successfully reset level ", level, "."); diff --git a/Source/multi.cpp b/Source/multi.cpp index ad0cbd86dd7..fb99a31b8ed 100644 --- a/Source/multi.cpp +++ b/Source/multi.cpp @@ -792,6 +792,7 @@ bool NetInit(bool bSinglePlayer) for (int i = 0; i < NUMLEVELS; i++) { DungeonSeeds[i] = AdvanceRndSeed(); + LevelSeeds[i] = std::nullopt; } PublicGame = DvlNet_IsPublicGame();