Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Capture the level seed and use it to skip failed dungeon layouts #7003

Merged
merged 2 commits into from
Mar 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Source/control.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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<TextCmdItem> TextCmdList = {
Expand Down
15 changes: 8 additions & 7 deletions Source/diablo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@

namespace devilution {

uint32_t glSeedTbl[NUMLEVELS];
uint32_t DungeonSeeds[NUMLEVELS];
std::optional<uint32_t> LevelSeeds[NUMLEVELS];
Point MousePosition;
bool gbRunGame;
bool gbRunGameResult;
Expand Down Expand Up @@ -1339,7 +1340,7 @@ void LoadAllGFX()
*/
void CreateLevel(lvl_entry entry)
{
CreateDungeon(glSeedTbl[currlevel], entry);
CreateDungeon(DungeonSeeds[currlevel], entry);

switch (leveltype) {
case DTYPE_TOWN:
Expand Down Expand Up @@ -2837,7 +2838,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();
Expand All @@ -2857,12 +2858,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<size_t>(setlvlnum)]);
SetRndSeed(DungeonSeeds[static_cast<size_t>(setlvlnum)]);
}

if (leveltype == DTYPE_TOWN) {
Expand Down Expand Up @@ -2894,7 +2895,7 @@ void LoadGameLevel(bool firstflag, lvl_entry lvldir)
CreateLevel(lvldir);
IncProgress();
LoadLevelSOLData();
SetRndSeed(glSeedTbl[currlevel]);
SetRndSeed(DungeonSeeds[currlevel]);

if (leveltype != DTYPE_TOWN) {
GetLevelMTypes();
Expand Down Expand Up @@ -2942,7 +2943,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) {
Expand Down
3 changes: 2 additions & 1 deletion Source/diablo.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ enum class MouseActionType : uint8_t {
OperateObject,
};

extern uint32_t glSeedTbl[NUMLEVELS];
extern uint32_t DungeonSeeds[NUMLEVELS];
extern std::optional<uint32_t> LevelSeeds[NUMLEVELS];
extern Point MousePosition;
extern DVL_API_FOR_TEST bool gbRunGame;
extern bool gbRunGameResult;
Expand Down
4 changes: 4 additions & 0 deletions Source/levels/drlg_l1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -1181,6 +1184,7 @@ void GenerateLevel(lvl_entry entry)
DRLG_InitTrans();

do {
LevelSeeds[currlevel] = GetLCGEngineState();
FirstRoom();
} while (FindArea() < minarea);

Expand Down
4 changes: 4 additions & 0 deletions Source/levels/drlg_l2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
5 changes: 5 additions & 0 deletions Source/levels/drlg_l3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "monster.h"
#include "objdat.h"
#include "objects.h"
#include "player.h"
#include "quests.h"

namespace devilution {
Expand Down Expand Up @@ -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;
Expand Down
5 changes: 5 additions & 0 deletions Source/levels/drlg_l4.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "monster.h"
#include "multi.h"
#include "objdat.h"
#include "player.h"

namespace devilution {

Expand Down Expand Up @@ -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();
Expand Down
34 changes: 31 additions & 3 deletions Source/loadsave.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2027,6 +2027,21 @@ void SaveHotkeys(SaveWriter &saveWriter, const Player &player)
file.WriteLE<uint8_t>(static_cast<uint8_t>(player._pRSplType));
}

void LoadLevelSeeds()
{
LoadHelper file(OpenSaveArchive(gSaveNumber), "levelseeds");
if (!file.IsValid())
return;

for (int i = 0; i < giNumberOfLevels; i++) {
if (file.NextLE<uint8_t>() != 0) {
LevelSeeds[i] = file.NextLE<uint32_t>();
} else {
LevelSeeds[i] = std::nullopt;
}
}
}

void LoadHeroItems(Player &player)
{
LoadHelper file(OpenSaveArchive(gSaveNumber), "heroitems");
Expand Down Expand Up @@ -2136,7 +2151,8 @@ 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<uint32_t>();
DungeonSeeds[i] = file.NextBE<uint32_t>();
LevelSeeds[i] = std::nullopt;
file.Skip(4); // Skip loading gnLevelTypeTbl
}

Expand Down Expand Up @@ -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<uint8_t>(LevelSeeds[i] ? 1 : 0);
if (LevelSeeds[i]) {
file.WriteLE<uint32_t>(*LevelSeeds[i]);
}
}
}

void SaveHeroItems(SaveWriter &saveWriter, Player &player)
{
size_t itemCount = static_cast<size_t>(NUM_INVLOC) + InventoryGridCells + MaxBeltItems;
Expand Down Expand Up @@ -2418,7 +2446,7 @@ void SaveGameData(SaveWriter &saveWriter)
file.WriteBE<int32_t>(ActiveObjectCount);

for (uint8_t i = 0; i < giNumberOfLevels; i++) {
file.WriteBE<uint32_t>(glSeedTbl[i]);
file.WriteBE<uint32_t>(DungeonSeeds[i]);
file.WriteBE<int32_t>(getHellfireLevelType(GetLevelType(i)));
}

Expand Down Expand Up @@ -2554,7 +2582,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);
Expand Down
9 changes: 5 additions & 4 deletions Source/lua/modules/dev/level.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -101,19 +101,20 @@ std::string DebugCmdResetLevel(uint8_t level, std::optional<int> seed)
DeltaClearLevel(level);

if (seed.has_value()) {
glSeedTbl[level] = *seed;
DungeonSeeds[level] = *seed;
LevelSeeds[level] = std::nullopt;
return StrCat("Successfully reset level ", level, " with seed ", *seed, ".");
}
return StrCat("Successfully reset level ", level, ".");
}

std::string DebugCmdLevelSeed(std::optional<uint8_t> 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
Expand Down
2 changes: 1 addition & 1 deletion Source/msg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -750,7 +750,7 @@ void DeltaLeaveSync(uint8_t bLevel)
if (!gbIsMultiplayer)
return;
if (leveltype == DTYPE_TOWN) {
glSeedTbl[0] = AdvanceRndSeed();
DungeonSeeds[0] = AdvanceRndSeed();
return;
}

Expand Down
3 changes: 2 additions & 1 deletion Source/multi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -791,7 +791,8 @@ bool NetInit(bool bSinglePlayer)
gnTickDelay = 1000 / sgGameInitInfo.nTickRate;

for (int i = 0; i < NUMLEVELS; i++) {
glSeedTbl[i] = AdvanceRndSeed();
DungeonSeeds[i] = AdvanceRndSeed();
LevelSeeds[i] = std::nullopt;
}
PublicGame = DvlNet_IsPublicGame();

Expand Down
2 changes: 1 addition & 1 deletion Source/objects.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion Source/quests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion Source/stores.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion test/fixtures/memory_map/levelSeed.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
R 32 glSeedTbl
R 32 DungeonSeeds
R 32 gnLevelTypeTbl
Loading