|
|
|
|
@ -33,11 +33,12 @@ namespace devilution {
|
|
|
|
|
|
|
|
|
|
bool gbIsHellfireSaveGame; |
|
|
|
|
uint8_t giNumberOfLevels; |
|
|
|
|
uint8_t giNumberQuests; |
|
|
|
|
uint8_t giNumberOfSmithPremiumItems; |
|
|
|
|
|
|
|
|
|
namespace { |
|
|
|
|
|
|
|
|
|
uint8_t giNumberQuests; |
|
|
|
|
uint8_t giNumberOfSmithPremiumItems; |
|
|
|
|
|
|
|
|
|
template <class T> |
|
|
|
|
T SwapLE(T in) |
|
|
|
|
{ |
|
|
|
|
@ -206,25 +207,6 @@ public:
|
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
void RemoveInvalidItem(ItemStruct *pItem) |
|
|
|
|
{ |
|
|
|
|
bool isInvalid = !IsItemAvailable(pItem->IDidx) || !IsUniqueAvailable(pItem->_iUid); |
|
|
|
|
|
|
|
|
|
if (!gbIsHellfire) { |
|
|
|
|
isInvalid = isInvalid || (pItem->_itype == ITYPE_STAFF && GetSpellStaffLevel(pItem->_iSpell) == -1); |
|
|
|
|
isInvalid = isInvalid || (pItem->_iMiscId == IMISC_BOOK && GetSpellBookLevel(pItem->_iSpell) == -1); |
|
|
|
|
isInvalid = isInvalid || pItem->_iDamAcFlags != 0; |
|
|
|
|
isInvalid = isInvalid || pItem->_iPrePower > IDI_LASTDIABLO; |
|
|
|
|
isInvalid = isInvalid || pItem->_iSufPower > IDI_LASTDIABLO; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (isInvalid) { |
|
|
|
|
pItem->_itype = ITYPE_NONE; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void LoadItemData(LoadHelper *file, ItemStruct *pItem) |
|
|
|
|
{ |
|
|
|
|
pItem->_iSeed = file->NextLE<int32_t>(); |
|
|
|
|
@ -817,133 +799,6 @@ static void LoadPortal(LoadHelper *file, int i)
|
|
|
|
|
pPortal->setlvl = file->NextBool32(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
_item_indexes RemapItemIdxFromDiablo(_item_indexes i) |
|
|
|
|
{ |
|
|
|
|
constexpr auto GetItemIdValue = [](int i) -> int { |
|
|
|
|
if (i == IDI_SORCERER) { |
|
|
|
|
return 166; |
|
|
|
|
} |
|
|
|
|
if (i >= 156) { |
|
|
|
|
i += 5; // Hellfire exclusive items
|
|
|
|
|
} |
|
|
|
|
if (i >= 88) { |
|
|
|
|
i += 1; // Scroll of Search
|
|
|
|
|
} |
|
|
|
|
if (i >= 83) { |
|
|
|
|
i += 4; // Oils
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return i; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
return static_cast<_item_indexes>(GetItemIdValue(i)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
_item_indexes RemapItemIdxToDiablo(_item_indexes i) |
|
|
|
|
{ |
|
|
|
|
constexpr auto GetItemIdValue = [](int i) -> int { |
|
|
|
|
if (i == 166) { |
|
|
|
|
return IDI_SORCERER; |
|
|
|
|
} |
|
|
|
|
if ((i >= 83 && i <= 86) || i == 92 || i >= 161) { |
|
|
|
|
return -1; // Hellfire exclusive items
|
|
|
|
|
} |
|
|
|
|
if (i >= 93) { |
|
|
|
|
i -= 1; // Scroll of Search
|
|
|
|
|
} |
|
|
|
|
if (i >= 87) { |
|
|
|
|
i -= 4; // Oils
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return i; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
return static_cast<_item_indexes>(GetItemIdValue(i)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
_item_indexes RemapItemIdxFromSpawn(_item_indexes i) |
|
|
|
|
{ |
|
|
|
|
constexpr auto GetItemIdValue = [](int i) { |
|
|
|
|
if (i >= 62) { |
|
|
|
|
i += 9; // Medium and heavy armors
|
|
|
|
|
} |
|
|
|
|
if (i >= 96) { |
|
|
|
|
i += 1; // Scroll of Stone Curse
|
|
|
|
|
} |
|
|
|
|
if (i >= 98) { |
|
|
|
|
i += 1; // Scroll of Guardian
|
|
|
|
|
} |
|
|
|
|
if (i >= 99) { |
|
|
|
|
i += 1; // Scroll of ...
|
|
|
|
|
} |
|
|
|
|
if (i >= 101) { |
|
|
|
|
i += 1; // Scroll of Golem
|
|
|
|
|
} |
|
|
|
|
if (i >= 102) { |
|
|
|
|
i += 1; // Scroll of None
|
|
|
|
|
} |
|
|
|
|
if (i >= 104) { |
|
|
|
|
i += 1; // Scroll of Apocalypse
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return i; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
return static_cast<_item_indexes>(GetItemIdValue(i)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
_item_indexes RemapItemIdxToSpawn(_item_indexes i) |
|
|
|
|
{ |
|
|
|
|
constexpr auto GetItemIdValue = [](int i) { |
|
|
|
|
if (i >= 104) { |
|
|
|
|
i -= 1; // Scroll of Apocalypse
|
|
|
|
|
} |
|
|
|
|
if (i >= 102) { |
|
|
|
|
i -= 1; // Scroll of None
|
|
|
|
|
} |
|
|
|
|
if (i >= 101) { |
|
|
|
|
i -= 1; // Scroll of Golem
|
|
|
|
|
} |
|
|
|
|
if (i >= 99) { |
|
|
|
|
i -= 1; // Scroll of ...
|
|
|
|
|
} |
|
|
|
|
if (i >= 98) { |
|
|
|
|
i -= 1; // Scroll of Guardian
|
|
|
|
|
} |
|
|
|
|
if (i >= 96) { |
|
|
|
|
i -= 1; // Scroll of Stone Curse
|
|
|
|
|
} |
|
|
|
|
if (i >= 71) { |
|
|
|
|
i -= 9; // Medium and heavy armors
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return i; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
return static_cast<_item_indexes>(GetItemIdValue(i)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool IsHeaderValid(uint32_t magicNumber) |
|
|
|
|
{ |
|
|
|
|
gbIsHellfireSaveGame = false; |
|
|
|
|
if (magicNumber == LoadLE32("SHAR")) { |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
if (magicNumber == LoadLE32("SHLF")) { |
|
|
|
|
gbIsHellfireSaveGame = true; |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
if (!gbIsSpawn && magicNumber == LoadLE32("RETL")) { |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
if (!gbIsSpawn && magicNumber == LoadLE32("HELF")) { |
|
|
|
|
gbIsHellfireSaveGame = true; |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void ConvertLevels() |
|
|
|
|
{ |
|
|
|
|
// Backup current level state
|
|
|
|
|
@ -994,43 +849,6 @@ static void ConvertLevels()
|
|
|
|
|
leveltype = tmpLeveltype; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void LoadHotkeys() |
|
|
|
|
{ |
|
|
|
|
LoadHelper file("hotkeys"); |
|
|
|
|
if (!file.IsValid()) |
|
|
|
|
return; |
|
|
|
|
|
|
|
|
|
auto &myPlayer = Players[MyPlayerId]; |
|
|
|
|
|
|
|
|
|
for (auto &spellId : myPlayer._pSplHotKey) { |
|
|
|
|
spellId = static_cast<spell_id>(file.NextLE<int32_t>()); |
|
|
|
|
} |
|
|
|
|
for (auto &spellType : myPlayer._pSplTHotKey) { |
|
|
|
|
spellType = static_cast<spell_type>(file.NextLE<int8_t>()); |
|
|
|
|
} |
|
|
|
|
myPlayer._pRSpell = static_cast<spell_id>(file.NextLE<int32_t>()); |
|
|
|
|
myPlayer._pRSplType = static_cast<spell_type>(file.NextLE<int8_t>()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void SaveHotkeys() |
|
|
|
|
{ |
|
|
|
|
auto &myPlayer = Players[MyPlayerId]; |
|
|
|
|
|
|
|
|
|
const size_t nHotkeyTypes = sizeof(myPlayer._pSplHotKey) / sizeof(myPlayer._pSplHotKey[0]); |
|
|
|
|
const size_t nHotkeySpells = sizeof(myPlayer._pSplTHotKey) / sizeof(myPlayer._pSplTHotKey[0]); |
|
|
|
|
|
|
|
|
|
SaveHelper file("hotkeys", (nHotkeyTypes * 4) + nHotkeySpells + 4 + 1); |
|
|
|
|
|
|
|
|
|
for (auto &spellId : myPlayer._pSplHotKey) { |
|
|
|
|
file.WriteLE<int32_t>(spellId); |
|
|
|
|
} |
|
|
|
|
for (auto &spellType : myPlayer._pSplTHotKey) { |
|
|
|
|
file.WriteLE<uint8_t>(spellType); |
|
|
|
|
} |
|
|
|
|
file.WriteLE<int32_t>(myPlayer._pRSpell); |
|
|
|
|
file.WriteLE<uint8_t>(myPlayer._pRSplType); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void LoadMatchingItems(LoadHelper *file, const int n, ItemStruct *pItem) |
|
|
|
|
{ |
|
|
|
|
ItemStruct tempItem; |
|
|
|
|
@ -1045,31 +863,6 @@ static void LoadMatchingItems(LoadHelper *file, const int n, ItemStruct *pItem)
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void LoadHeroItems(PlayerStruct &player) |
|
|
|
|
{ |
|
|
|
|
LoadHelper file("heroitems"); |
|
|
|
|
if (!file.IsValid()) |
|
|
|
|
return; |
|
|
|
|
|
|
|
|
|
gbIsHellfireSaveGame = file.NextBool8(); |
|
|
|
|
|
|
|
|
|
LoadMatchingItems(&file, NUM_INVLOC, player.InvBody); |
|
|
|
|
LoadMatchingItems(&file, NUM_INV_GRID_ELEM, player.InvList); |
|
|
|
|
LoadMatchingItems(&file, MAXBELTITEMS, player.SpdList); |
|
|
|
|
|
|
|
|
|
gbIsHellfireSaveGame = gbIsHellfire; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void RemoveEmptyInventory(PlayerStruct &player) |
|
|
|
|
{ |
|
|
|
|
for (int i = NUM_INV_GRID_ELEM; i > 0; i--) { |
|
|
|
|
int idx = player.InvGrid[i - 1]; |
|
|
|
|
if (idx > 0 && player.InvList[idx - 1].isEmpty()) { |
|
|
|
|
player.RemoveInvItem(idx - 1); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void RemoveEmptyLevelItems() |
|
|
|
|
{ |
|
|
|
|
for (int i = ActiveItemCount; i > 0; i--) { |
|
|
|
|
@ -1081,219 +874,17 @@ void RemoveEmptyLevelItems()
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Load game state |
|
|
|
|
* @param firstflag Can be set to false if we are simply reloading the current game |
|
|
|
|
*/ |
|
|
|
|
void LoadGame(bool firstflag) |
|
|
|
|
static void SaveItem(SaveHelper *file, ItemStruct *pItem) |
|
|
|
|
{ |
|
|
|
|
FreeGameMem(); |
|
|
|
|
pfile_remove_temp_files(); |
|
|
|
|
|
|
|
|
|
LoadHelper file("game"); |
|
|
|
|
if (!file.IsValid()) |
|
|
|
|
app_fatal("%s", _("Unable to open save file archive")); |
|
|
|
|
|
|
|
|
|
if (!IsHeaderValid(file.NextLE<uint32_t>())) |
|
|
|
|
app_fatal("%s", _("Invalid save file")); |
|
|
|
|
|
|
|
|
|
if (gbIsHellfireSaveGame) { |
|
|
|
|
giNumberOfLevels = 25; |
|
|
|
|
giNumberQuests = 24; |
|
|
|
|
giNumberOfSmithPremiumItems = 15; |
|
|
|
|
} else { |
|
|
|
|
// Todo initialize additional levels and quests if we are running Hellfire
|
|
|
|
|
giNumberOfLevels = 17; |
|
|
|
|
giNumberQuests = 16; |
|
|
|
|
giNumberOfSmithPremiumItems = 6; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
setlevel = file.NextBool8(); |
|
|
|
|
setlvlnum = static_cast<_setlevels>(file.NextBE<uint32_t>()); |
|
|
|
|
currlevel = file.NextBE<uint32_t>(); |
|
|
|
|
leveltype = static_cast<dungeon_type>(file.NextBE<uint32_t>()); |
|
|
|
|
if (!setlevel) |
|
|
|
|
leveltype = gnLevelTypeTbl[currlevel]; |
|
|
|
|
int viewX = file.NextBE<int32_t>(); |
|
|
|
|
int viewY = file.NextBE<int32_t>(); |
|
|
|
|
invflag = file.NextBool8(); |
|
|
|
|
chrflag = file.NextBool8(); |
|
|
|
|
int tmpNummonsters = file.NextBE<int32_t>(); |
|
|
|
|
int tmpNumitems = file.NextBE<int32_t>(); |
|
|
|
|
int tmpNummissiles = file.NextBE<int32_t>(); |
|
|
|
|
int tmpNobjects = file.NextBE<int32_t>(); |
|
|
|
|
|
|
|
|
|
if (!gbIsHellfire && currlevel > 17) |
|
|
|
|
app_fatal("%s", _("Player is on a Hellfire only level")); |
|
|
|
|
|
|
|
|
|
for (uint8_t i = 0; i < giNumberOfLevels; i++) { |
|
|
|
|
glSeedTbl[i] = file.NextBE<uint32_t>(); |
|
|
|
|
file.Skip(4); // Skip loading gnLevelTypeTbl
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
LoadPlayer(&file, MyPlayerId); |
|
|
|
|
|
|
|
|
|
sgGameInitInfo.nDifficulty = Players[MyPlayerId].pDifficulty; |
|
|
|
|
if (sgGameInitInfo.nDifficulty < DIFF_NORMAL || sgGameInitInfo.nDifficulty > DIFF_HELL) |
|
|
|
|
sgGameInitInfo.nDifficulty = DIFF_NORMAL; |
|
|
|
|
|
|
|
|
|
for (int i = 0; i < giNumberQuests; i++) |
|
|
|
|
LoadQuest(&file, i); |
|
|
|
|
for (int i = 0; i < MAXPORTAL; i++) |
|
|
|
|
LoadPortal(&file, i); |
|
|
|
|
|
|
|
|
|
if (gbIsHellfireSaveGame != gbIsHellfire) { |
|
|
|
|
ConvertLevels(); |
|
|
|
|
RemoveEmptyInventory(Players[MyPlayerId]); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
LoadGameLevel(firstflag, ENTRY_LOAD); |
|
|
|
|
SyncInitPlr(MyPlayerId); |
|
|
|
|
SyncPlrAnim(MyPlayerId); |
|
|
|
|
|
|
|
|
|
ViewX = viewX; |
|
|
|
|
ViewY = viewY; |
|
|
|
|
ActiveMonsterCount = tmpNummonsters; |
|
|
|
|
ActiveItemCount = tmpNumitems; |
|
|
|
|
ActiveMissileCount = tmpNummissiles; |
|
|
|
|
ActiveObjectCount = tmpNobjects; |
|
|
|
|
|
|
|
|
|
for (int &monstkill : MonsterKillCounts) |
|
|
|
|
monstkill = file.NextBE<int32_t>(); |
|
|
|
|
|
|
|
|
|
if (leveltype != DTYPE_TOWN) { |
|
|
|
|
for (int &monsterId : ActiveMonsters) |
|
|
|
|
monsterId = file.NextBE<int32_t>(); |
|
|
|
|
for (int i = 0; i < ActiveMonsterCount; i++) |
|
|
|
|
LoadMonster(&file, ActiveMonsters[i]); |
|
|
|
|
for (int &missileId : ActiveMissiles) |
|
|
|
|
missileId = file.NextLE<int8_t>(); |
|
|
|
|
for (int &missileId : AvailableMissiles) |
|
|
|
|
missileId = file.NextLE<int8_t>(); |
|
|
|
|
for (int i = 0; i < ActiveMissileCount; i++) |
|
|
|
|
LoadMissile(&file, ActiveMissiles[i]); |
|
|
|
|
for (int &objectId : ActiveObjects) |
|
|
|
|
objectId = file.NextLE<int8_t>(); |
|
|
|
|
for (int &objectId : AvailableObjects) |
|
|
|
|
objectId = file.NextLE<int8_t>(); |
|
|
|
|
for (int i = 0; i < ActiveObjectCount; i++) |
|
|
|
|
LoadObject(&file, ActiveObjects[i]); |
|
|
|
|
for (int i = 0; i < ActiveObjectCount; i++) |
|
|
|
|
SyncObjectAnim(Objects[ActiveObjects[i]]); |
|
|
|
|
|
|
|
|
|
ActiveLightCount = file.NextBE<int32_t>(); |
|
|
|
|
|
|
|
|
|
for (uint8_t &lightId : ActiveLights) |
|
|
|
|
lightId = file.NextLE<uint8_t>(); |
|
|
|
|
for (int i = 0; i < ActiveLightCount; i++) |
|
|
|
|
LoadLighting(&file, &Lights[ActiveLights[i]]); |
|
|
|
|
|
|
|
|
|
VisionId = file.NextBE<int32_t>(); |
|
|
|
|
VisionCount = file.NextBE<int32_t>(); |
|
|
|
|
|
|
|
|
|
for (int i = 0; i < VisionCount; i++) |
|
|
|
|
LoadLighting(&file, &VisionList[i]); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for (int &itemId : ActiveItems) |
|
|
|
|
itemId = file.NextLE<int8_t>(); |
|
|
|
|
for (int &itemId : AvailableItems) |
|
|
|
|
itemId = file.NextLE<int8_t>(); |
|
|
|
|
for (int i = 0; i < ActiveItemCount; i++) |
|
|
|
|
LoadItem(&file, ActiveItems[i]); |
|
|
|
|
for (bool &uniqueItemFlag : UniqueItemFlags) |
|
|
|
|
uniqueItemFlag = file.NextBool8(); |
|
|
|
|
|
|
|
|
|
for (int j = 0; j < MAXDUNY; j++) { |
|
|
|
|
for (int i = 0; i < MAXDUNX; i++) // NOLINT(modernize-loop-convert)
|
|
|
|
|
dLight[i][j] = file.NextLE<int8_t>(); |
|
|
|
|
} |
|
|
|
|
for (int j = 0; j < MAXDUNY; j++) { |
|
|
|
|
for (int i = 0; i < MAXDUNX; i++) // NOLINT(modernize-loop-convert)
|
|
|
|
|
dFlags[i][j] = file.NextLE<int8_t>(); |
|
|
|
|
} |
|
|
|
|
for (int j = 0; j < MAXDUNY; j++) { |
|
|
|
|
for (int i = 0; i < MAXDUNX; i++) // NOLINT(modernize-loop-convert)
|
|
|
|
|
dPlayer[i][j] = file.NextLE<int8_t>(); |
|
|
|
|
} |
|
|
|
|
for (int j = 0; j < MAXDUNY; j++) { |
|
|
|
|
for (int i = 0; i < MAXDUNX; i++) // NOLINT(modernize-loop-convert)
|
|
|
|
|
dItem[i][j] = file.NextLE<int8_t>(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (leveltype != DTYPE_TOWN) { |
|
|
|
|
for (int j = 0; j < MAXDUNY; j++) { |
|
|
|
|
for (int i = 0; i < MAXDUNX; i++) // NOLINT(modernize-loop-convert)
|
|
|
|
|
dMonster[i][j] = file.NextBE<int32_t>(); |
|
|
|
|
} |
|
|
|
|
for (int j = 0; j < MAXDUNY; j++) { |
|
|
|
|
for (int i = 0; i < MAXDUNX; i++) // NOLINT(modernize-loop-convert)
|
|
|
|
|
dDead[i][j] = file.NextLE<int8_t>(); |
|
|
|
|
} |
|
|
|
|
for (int j = 0; j < MAXDUNY; j++) { |
|
|
|
|
for (int i = 0; i < MAXDUNX; i++) // NOLINT(modernize-loop-convert)
|
|
|
|
|
dObject[i][j] = file.NextLE<int8_t>(); |
|
|
|
|
} |
|
|
|
|
for (int j = 0; j < MAXDUNY; j++) { |
|
|
|
|
for (int i = 0; i < MAXDUNX; i++) // NOLINT(modernize-loop-convert)
|
|
|
|
|
dLight[i][j] = file.NextLE<int8_t>(); |
|
|
|
|
} |
|
|
|
|
for (int j = 0; j < MAXDUNY; j++) { |
|
|
|
|
for (int i = 0; i < MAXDUNX; i++) // NOLINT(modernize-loop-convert)
|
|
|
|
|
dPreLight[i][j] = file.NextLE<int8_t>(); |
|
|
|
|
} |
|
|
|
|
for (int j = 0; j < DMAXY; j++) { |
|
|
|
|
for (int i = 0; i < DMAXX; i++) // NOLINT(modernize-loop-convert)
|
|
|
|
|
AutomapView[i][j] = file.NextBool8(); |
|
|
|
|
} |
|
|
|
|
for (int j = 0; j < MAXDUNY; j++) { |
|
|
|
|
for (int i = 0; i < MAXDUNX; i++) // NOLINT(modernize-loop-convert)
|
|
|
|
|
dMissile[i][j] = file.NextLE<int8_t>(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
numpremium = file.NextBE<int32_t>(); |
|
|
|
|
premiumlevel = file.NextBE<int32_t>(); |
|
|
|
|
|
|
|
|
|
for (int i = 0; i < giNumberOfSmithPremiumItems; i++) |
|
|
|
|
LoadPremium(&file, i); |
|
|
|
|
if (gbIsHellfire && !gbIsHellfireSaveGame) |
|
|
|
|
SpawnPremium(MyPlayerId); |
|
|
|
|
|
|
|
|
|
AutomapActive = file.NextBool8(); |
|
|
|
|
AutoMapScale = file.NextBE<int32_t>(); |
|
|
|
|
AutomapZoomReset(); |
|
|
|
|
ResyncQuests(); |
|
|
|
|
|
|
|
|
|
if (leveltype != DTYPE_TOWN) |
|
|
|
|
ProcessLightList(); |
|
|
|
|
|
|
|
|
|
RedoPlayerVision(); |
|
|
|
|
ProcessVisionList(); |
|
|
|
|
missiles_process_charge(); |
|
|
|
|
ResetPal(); |
|
|
|
|
NewCursor(CURSOR_HAND); |
|
|
|
|
gbProcessPlayers = true; |
|
|
|
|
|
|
|
|
|
if (gbIsHellfireSaveGame != gbIsHellfire) { |
|
|
|
|
RemoveEmptyLevelItems(); |
|
|
|
|
SaveGame(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
gbIsHellfireSaveGame = gbIsHellfire; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void SaveItem(SaveHelper *file, ItemStruct *pItem) |
|
|
|
|
{ |
|
|
|
|
auto idx = pItem->IDidx; |
|
|
|
|
if (!gbIsHellfire) |
|
|
|
|
idx = RemapItemIdxToDiablo(idx); |
|
|
|
|
if (gbIsSpawn) |
|
|
|
|
idx = RemapItemIdxToSpawn(idx); |
|
|
|
|
int iType = pItem->_itype; |
|
|
|
|
if (idx == -1) { |
|
|
|
|
idx = _item_indexes::IDI_GOLD; |
|
|
|
|
iType = ITYPE_NONE; |
|
|
|
|
auto idx = pItem->IDidx; |
|
|
|
|
if (!gbIsHellfire) |
|
|
|
|
idx = RemapItemIdxToDiablo(idx); |
|
|
|
|
if (gbIsSpawn) |
|
|
|
|
idx = RemapItemIdxToSpawn(idx); |
|
|
|
|
int iType = pItem->_itype; |
|
|
|
|
if (idx == -1) { |
|
|
|
|
idx = _item_indexes::IDI_GOLD; |
|
|
|
|
iType = ITYPE_NONE; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
file->WriteLE<int32_t>(pItem->_iSeed); |
|
|
|
|
@ -1856,6 +1447,416 @@ static void SavePortal(SaveHelper *file, int i)
|
|
|
|
|
const int DiabloItemSaveSize = 368; |
|
|
|
|
const int HellfireItemSaveSize = 372; |
|
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
void RemoveInvalidItem(ItemStruct *pItem) |
|
|
|
|
{ |
|
|
|
|
bool isInvalid = !IsItemAvailable(pItem->IDidx) || !IsUniqueAvailable(pItem->_iUid); |
|
|
|
|
|
|
|
|
|
if (!gbIsHellfire) { |
|
|
|
|
isInvalid = isInvalid || (pItem->_itype == ITYPE_STAFF && GetSpellStaffLevel(pItem->_iSpell) == -1); |
|
|
|
|
isInvalid = isInvalid || (pItem->_iMiscId == IMISC_BOOK && GetSpellBookLevel(pItem->_iSpell) == -1); |
|
|
|
|
isInvalid = isInvalid || pItem->_iDamAcFlags != 0; |
|
|
|
|
isInvalid = isInvalid || pItem->_iPrePower > IDI_LASTDIABLO; |
|
|
|
|
isInvalid = isInvalid || pItem->_iSufPower > IDI_LASTDIABLO; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (isInvalid) { |
|
|
|
|
pItem->_itype = ITYPE_NONE; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
_item_indexes RemapItemIdxFromDiablo(_item_indexes i) |
|
|
|
|
{ |
|
|
|
|
constexpr auto GetItemIdValue = [](int i) -> int { |
|
|
|
|
if (i == IDI_SORCERER) { |
|
|
|
|
return 166; |
|
|
|
|
} |
|
|
|
|
if (i >= 156) { |
|
|
|
|
i += 5; // Hellfire exclusive items
|
|
|
|
|
} |
|
|
|
|
if (i >= 88) { |
|
|
|
|
i += 1; // Scroll of Search
|
|
|
|
|
} |
|
|
|
|
if (i >= 83) { |
|
|
|
|
i += 4; // Oils
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return i; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
return static_cast<_item_indexes>(GetItemIdValue(i)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
_item_indexes RemapItemIdxToDiablo(_item_indexes i) |
|
|
|
|
{ |
|
|
|
|
constexpr auto GetItemIdValue = [](int i) -> int { |
|
|
|
|
if (i == 166) { |
|
|
|
|
return IDI_SORCERER; |
|
|
|
|
} |
|
|
|
|
if ((i >= 83 && i <= 86) || i == 92 || i >= 161) { |
|
|
|
|
return -1; // Hellfire exclusive items
|
|
|
|
|
} |
|
|
|
|
if (i >= 93) { |
|
|
|
|
i -= 1; // Scroll of Search
|
|
|
|
|
} |
|
|
|
|
if (i >= 87) { |
|
|
|
|
i -= 4; // Oils
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return i; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
return static_cast<_item_indexes>(GetItemIdValue(i)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
_item_indexes RemapItemIdxFromSpawn(_item_indexes i) |
|
|
|
|
{ |
|
|
|
|
constexpr auto GetItemIdValue = [](int i) { |
|
|
|
|
if (i >= 62) { |
|
|
|
|
i += 9; // Medium and heavy armors
|
|
|
|
|
} |
|
|
|
|
if (i >= 96) { |
|
|
|
|
i += 1; // Scroll of Stone Curse
|
|
|
|
|
} |
|
|
|
|
if (i >= 98) { |
|
|
|
|
i += 1; // Scroll of Guardian
|
|
|
|
|
} |
|
|
|
|
if (i >= 99) { |
|
|
|
|
i += 1; // Scroll of ...
|
|
|
|
|
} |
|
|
|
|
if (i >= 101) { |
|
|
|
|
i += 1; // Scroll of Golem
|
|
|
|
|
} |
|
|
|
|
if (i >= 102) { |
|
|
|
|
i += 1; // Scroll of None
|
|
|
|
|
} |
|
|
|
|
if (i >= 104) { |
|
|
|
|
i += 1; // Scroll of Apocalypse
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return i; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
return static_cast<_item_indexes>(GetItemIdValue(i)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
_item_indexes RemapItemIdxToSpawn(_item_indexes i) |
|
|
|
|
{ |
|
|
|
|
constexpr auto GetItemIdValue = [](int i) { |
|
|
|
|
if (i >= 104) { |
|
|
|
|
i -= 1; // Scroll of Apocalypse
|
|
|
|
|
} |
|
|
|
|
if (i >= 102) { |
|
|
|
|
i -= 1; // Scroll of None
|
|
|
|
|
} |
|
|
|
|
if (i >= 101) { |
|
|
|
|
i -= 1; // Scroll of Golem
|
|
|
|
|
} |
|
|
|
|
if (i >= 99) { |
|
|
|
|
i -= 1; // Scroll of ...
|
|
|
|
|
} |
|
|
|
|
if (i >= 98) { |
|
|
|
|
i -= 1; // Scroll of Guardian
|
|
|
|
|
} |
|
|
|
|
if (i >= 96) { |
|
|
|
|
i -= 1; // Scroll of Stone Curse
|
|
|
|
|
} |
|
|
|
|
if (i >= 71) { |
|
|
|
|
i -= 9; // Medium and heavy armors
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return i; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
return static_cast<_item_indexes>(GetItemIdValue(i)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool IsHeaderValid(uint32_t magicNumber) |
|
|
|
|
{ |
|
|
|
|
gbIsHellfireSaveGame = false; |
|
|
|
|
if (magicNumber == LoadLE32("SHAR")) { |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
if (magicNumber == LoadLE32("SHLF")) { |
|
|
|
|
gbIsHellfireSaveGame = true; |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
if (!gbIsSpawn && magicNumber == LoadLE32("RETL")) { |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
if (!gbIsSpawn && magicNumber == LoadLE32("HELF")) { |
|
|
|
|
gbIsHellfireSaveGame = true; |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void LoadHotkeys() |
|
|
|
|
{ |
|
|
|
|
LoadHelper file("hotkeys"); |
|
|
|
|
if (!file.IsValid()) |
|
|
|
|
return; |
|
|
|
|
|
|
|
|
|
auto &myPlayer = Players[MyPlayerId]; |
|
|
|
|
|
|
|
|
|
for (auto &spellId : myPlayer._pSplHotKey) { |
|
|
|
|
spellId = static_cast<spell_id>(file.NextLE<int32_t>()); |
|
|
|
|
} |
|
|
|
|
for (auto &spellType : myPlayer._pSplTHotKey) { |
|
|
|
|
spellType = static_cast<spell_type>(file.NextLE<int8_t>()); |
|
|
|
|
} |
|
|
|
|
myPlayer._pRSpell = static_cast<spell_id>(file.NextLE<int32_t>()); |
|
|
|
|
myPlayer._pRSplType = static_cast<spell_type>(file.NextLE<int8_t>()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void SaveHotkeys() |
|
|
|
|
{ |
|
|
|
|
auto &myPlayer = Players[MyPlayerId]; |
|
|
|
|
|
|
|
|
|
const size_t nHotkeyTypes = sizeof(myPlayer._pSplHotKey) / sizeof(myPlayer._pSplHotKey[0]); |
|
|
|
|
const size_t nHotkeySpells = sizeof(myPlayer._pSplTHotKey) / sizeof(myPlayer._pSplTHotKey[0]); |
|
|
|
|
|
|
|
|
|
SaveHelper file("hotkeys", (nHotkeyTypes * 4) + nHotkeySpells + 4 + 1); |
|
|
|
|
|
|
|
|
|
for (auto &spellId : myPlayer._pSplHotKey) { |
|
|
|
|
file.WriteLE<int32_t>(spellId); |
|
|
|
|
} |
|
|
|
|
for (auto &spellType : myPlayer._pSplTHotKey) { |
|
|
|
|
file.WriteLE<uint8_t>(spellType); |
|
|
|
|
} |
|
|
|
|
file.WriteLE<int32_t>(myPlayer._pRSpell); |
|
|
|
|
file.WriteLE<uint8_t>(myPlayer._pRSplType); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void LoadHeroItems(PlayerStruct &player) |
|
|
|
|
{ |
|
|
|
|
LoadHelper file("heroitems"); |
|
|
|
|
if (!file.IsValid()) |
|
|
|
|
return; |
|
|
|
|
|
|
|
|
|
gbIsHellfireSaveGame = file.NextBool8(); |
|
|
|
|
|
|
|
|
|
LoadMatchingItems(&file, NUM_INVLOC, player.InvBody); |
|
|
|
|
LoadMatchingItems(&file, NUM_INV_GRID_ELEM, player.InvList); |
|
|
|
|
LoadMatchingItems(&file, MAXBELTITEMS, player.SpdList); |
|
|
|
|
|
|
|
|
|
gbIsHellfireSaveGame = gbIsHellfire; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void RemoveEmptyInventory(PlayerStruct &player) |
|
|
|
|
{ |
|
|
|
|
for (int i = NUM_INV_GRID_ELEM; i > 0; i--) { |
|
|
|
|
int idx = player.InvGrid[i - 1]; |
|
|
|
|
if (idx > 0 && player.InvList[idx - 1].isEmpty()) { |
|
|
|
|
player.RemoveInvItem(idx - 1); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Load game state |
|
|
|
|
* @param firstflag Can be set to false if we are simply reloading the current game |
|
|
|
|
*/ |
|
|
|
|
void LoadGame(bool firstflag) |
|
|
|
|
{ |
|
|
|
|
FreeGameMem(); |
|
|
|
|
pfile_remove_temp_files(); |
|
|
|
|
|
|
|
|
|
LoadHelper file("game"); |
|
|
|
|
if (!file.IsValid()) |
|
|
|
|
app_fatal("%s", _("Unable to open save file archive")); |
|
|
|
|
|
|
|
|
|
if (!IsHeaderValid(file.NextLE<uint32_t>())) |
|
|
|
|
app_fatal("%s", _("Invalid save file")); |
|
|
|
|
|
|
|
|
|
if (gbIsHellfireSaveGame) { |
|
|
|
|
giNumberOfLevels = 25; |
|
|
|
|
giNumberQuests = 24; |
|
|
|
|
giNumberOfSmithPremiumItems = 15; |
|
|
|
|
} else { |
|
|
|
|
// Todo initialize additional levels and quests if we are running Hellfire
|
|
|
|
|
giNumberOfLevels = 17; |
|
|
|
|
giNumberQuests = 16; |
|
|
|
|
giNumberOfSmithPremiumItems = 6; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
setlevel = file.NextBool8(); |
|
|
|
|
setlvlnum = static_cast<_setlevels>(file.NextBE<uint32_t>()); |
|
|
|
|
currlevel = file.NextBE<uint32_t>(); |
|
|
|
|
leveltype = static_cast<dungeon_type>(file.NextBE<uint32_t>()); |
|
|
|
|
if (!setlevel) |
|
|
|
|
leveltype = gnLevelTypeTbl[currlevel]; |
|
|
|
|
int viewX = file.NextBE<int32_t>(); |
|
|
|
|
int viewY = file.NextBE<int32_t>(); |
|
|
|
|
invflag = file.NextBool8(); |
|
|
|
|
chrflag = file.NextBool8(); |
|
|
|
|
int tmpNummonsters = file.NextBE<int32_t>(); |
|
|
|
|
int tmpNumitems = file.NextBE<int32_t>(); |
|
|
|
|
int tmpNummissiles = file.NextBE<int32_t>(); |
|
|
|
|
int tmpNobjects = file.NextBE<int32_t>(); |
|
|
|
|
|
|
|
|
|
if (!gbIsHellfire && currlevel > 17) |
|
|
|
|
app_fatal("%s", _("Player is on a Hellfire only level")); |
|
|
|
|
|
|
|
|
|
for (uint8_t i = 0; i < giNumberOfLevels; i++) { |
|
|
|
|
glSeedTbl[i] = file.NextBE<uint32_t>(); |
|
|
|
|
file.Skip(4); // Skip loading gnLevelTypeTbl
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
LoadPlayer(&file, MyPlayerId); |
|
|
|
|
|
|
|
|
|
sgGameInitInfo.nDifficulty = Players[MyPlayerId].pDifficulty; |
|
|
|
|
if (sgGameInitInfo.nDifficulty < DIFF_NORMAL || sgGameInitInfo.nDifficulty > DIFF_HELL) |
|
|
|
|
sgGameInitInfo.nDifficulty = DIFF_NORMAL; |
|
|
|
|
|
|
|
|
|
for (int i = 0; i < giNumberQuests; i++) |
|
|
|
|
LoadQuest(&file, i); |
|
|
|
|
for (int i = 0; i < MAXPORTAL; i++) |
|
|
|
|
LoadPortal(&file, i); |
|
|
|
|
|
|
|
|
|
if (gbIsHellfireSaveGame != gbIsHellfire) { |
|
|
|
|
ConvertLevels(); |
|
|
|
|
RemoveEmptyInventory(Players[MyPlayerId]); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
LoadGameLevel(firstflag, ENTRY_LOAD); |
|
|
|
|
SyncInitPlr(MyPlayerId); |
|
|
|
|
SyncPlrAnim(MyPlayerId); |
|
|
|
|
|
|
|
|
|
ViewX = viewX; |
|
|
|
|
ViewY = viewY; |
|
|
|
|
ActiveMonsterCount = tmpNummonsters; |
|
|
|
|
ActiveItemCount = tmpNumitems; |
|
|
|
|
ActiveMissileCount = tmpNummissiles; |
|
|
|
|
ActiveObjectCount = tmpNobjects; |
|
|
|
|
|
|
|
|
|
for (int &monstkill : MonsterKillCounts) |
|
|
|
|
monstkill = file.NextBE<int32_t>(); |
|
|
|
|
|
|
|
|
|
if (leveltype != DTYPE_TOWN) { |
|
|
|
|
for (int &monsterId : ActiveMonsters) |
|
|
|
|
monsterId = file.NextBE<int32_t>(); |
|
|
|
|
for (int i = 0; i < ActiveMonsterCount; i++) |
|
|
|
|
LoadMonster(&file, ActiveMonsters[i]); |
|
|
|
|
for (int &missileId : ActiveMissiles) |
|
|
|
|
missileId = file.NextLE<int8_t>(); |
|
|
|
|
for (int &missileId : AvailableMissiles) |
|
|
|
|
missileId = file.NextLE<int8_t>(); |
|
|
|
|
for (int i = 0; i < ActiveMissileCount; i++) |
|
|
|
|
LoadMissile(&file, ActiveMissiles[i]); |
|
|
|
|
for (int &objectId : ActiveObjects) |
|
|
|
|
objectId = file.NextLE<int8_t>(); |
|
|
|
|
for (int &objectId : AvailableObjects) |
|
|
|
|
objectId = file.NextLE<int8_t>(); |
|
|
|
|
for (int i = 0; i < ActiveObjectCount; i++) |
|
|
|
|
LoadObject(&file, ActiveObjects[i]); |
|
|
|
|
for (int i = 0; i < ActiveObjectCount; i++) |
|
|
|
|
SyncObjectAnim(Objects[ActiveObjects[i]]); |
|
|
|
|
|
|
|
|
|
ActiveLightCount = file.NextBE<int32_t>(); |
|
|
|
|
|
|
|
|
|
for (uint8_t &lightId : ActiveLights) |
|
|
|
|
lightId = file.NextLE<uint8_t>(); |
|
|
|
|
for (int i = 0; i < ActiveLightCount; i++) |
|
|
|
|
LoadLighting(&file, &Lights[ActiveLights[i]]); |
|
|
|
|
|
|
|
|
|
VisionId = file.NextBE<int32_t>(); |
|
|
|
|
VisionCount = file.NextBE<int32_t>(); |
|
|
|
|
|
|
|
|
|
for (int i = 0; i < VisionCount; i++) |
|
|
|
|
LoadLighting(&file, &VisionList[i]); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for (int &itemId : ActiveItems) |
|
|
|
|
itemId = file.NextLE<int8_t>(); |
|
|
|
|
for (int &itemId : AvailableItems) |
|
|
|
|
itemId = file.NextLE<int8_t>(); |
|
|
|
|
for (int i = 0; i < ActiveItemCount; i++) |
|
|
|
|
LoadItem(&file, ActiveItems[i]); |
|
|
|
|
for (bool &uniqueItemFlag : UniqueItemFlags) |
|
|
|
|
uniqueItemFlag = file.NextBool8(); |
|
|
|
|
|
|
|
|
|
for (int j = 0; j < MAXDUNY; j++) { |
|
|
|
|
for (int i = 0; i < MAXDUNX; i++) // NOLINT(modernize-loop-convert)
|
|
|
|
|
dLight[i][j] = file.NextLE<int8_t>(); |
|
|
|
|
} |
|
|
|
|
for (int j = 0; j < MAXDUNY; j++) { |
|
|
|
|
for (int i = 0; i < MAXDUNX; i++) // NOLINT(modernize-loop-convert)
|
|
|
|
|
dFlags[i][j] = file.NextLE<int8_t>(); |
|
|
|
|
} |
|
|
|
|
for (int j = 0; j < MAXDUNY; j++) { |
|
|
|
|
for (int i = 0; i < MAXDUNX; i++) // NOLINT(modernize-loop-convert)
|
|
|
|
|
dPlayer[i][j] = file.NextLE<int8_t>(); |
|
|
|
|
} |
|
|
|
|
for (int j = 0; j < MAXDUNY; j++) { |
|
|
|
|
for (int i = 0; i < MAXDUNX; i++) // NOLINT(modernize-loop-convert)
|
|
|
|
|
dItem[i][j] = file.NextLE<int8_t>(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (leveltype != DTYPE_TOWN) { |
|
|
|
|
for (int j = 0; j < MAXDUNY; j++) { |
|
|
|
|
for (int i = 0; i < MAXDUNX; i++) // NOLINT(modernize-loop-convert)
|
|
|
|
|
dMonster[i][j] = file.NextBE<int32_t>(); |
|
|
|
|
} |
|
|
|
|
for (int j = 0; j < MAXDUNY; j++) { |
|
|
|
|
for (int i = 0; i < MAXDUNX; i++) // NOLINT(modernize-loop-convert)
|
|
|
|
|
dDead[i][j] = file.NextLE<int8_t>(); |
|
|
|
|
} |
|
|
|
|
for (int j = 0; j < MAXDUNY; j++) { |
|
|
|
|
for (int i = 0; i < MAXDUNX; i++) // NOLINT(modernize-loop-convert)
|
|
|
|
|
dObject[i][j] = file.NextLE<int8_t>(); |
|
|
|
|
} |
|
|
|
|
for (int j = 0; j < MAXDUNY; j++) { |
|
|
|
|
for (int i = 0; i < MAXDUNX; i++) // NOLINT(modernize-loop-convert)
|
|
|
|
|
dLight[i][j] = file.NextLE<int8_t>(); |
|
|
|
|
} |
|
|
|
|
for (int j = 0; j < MAXDUNY; j++) { |
|
|
|
|
for (int i = 0; i < MAXDUNX; i++) // NOLINT(modernize-loop-convert)
|
|
|
|
|
dPreLight[i][j] = file.NextLE<int8_t>(); |
|
|
|
|
} |
|
|
|
|
for (int j = 0; j < DMAXY; j++) { |
|
|
|
|
for (int i = 0; i < DMAXX; i++) // NOLINT(modernize-loop-convert)
|
|
|
|
|
AutomapView[i][j] = file.NextBool8(); |
|
|
|
|
} |
|
|
|
|
for (int j = 0; j < MAXDUNY; j++) { |
|
|
|
|
for (int i = 0; i < MAXDUNX; i++) // NOLINT(modernize-loop-convert)
|
|
|
|
|
dMissile[i][j] = file.NextLE<int8_t>(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
numpremium = file.NextBE<int32_t>(); |
|
|
|
|
premiumlevel = file.NextBE<int32_t>(); |
|
|
|
|
|
|
|
|
|
for (int i = 0; i < giNumberOfSmithPremiumItems; i++) |
|
|
|
|
LoadPremium(&file, i); |
|
|
|
|
if (gbIsHellfire && !gbIsHellfireSaveGame) |
|
|
|
|
SpawnPremium(MyPlayerId); |
|
|
|
|
|
|
|
|
|
AutomapActive = file.NextBool8(); |
|
|
|
|
AutoMapScale = file.NextBE<int32_t>(); |
|
|
|
|
AutomapZoomReset(); |
|
|
|
|
ResyncQuests(); |
|
|
|
|
|
|
|
|
|
if (leveltype != DTYPE_TOWN) |
|
|
|
|
ProcessLightList(); |
|
|
|
|
|
|
|
|
|
RedoPlayerVision(); |
|
|
|
|
ProcessVisionList(); |
|
|
|
|
missiles_process_charge(); |
|
|
|
|
ResetPal(); |
|
|
|
|
NewCursor(CURSOR_HAND); |
|
|
|
|
gbProcessPlayers = true; |
|
|
|
|
|
|
|
|
|
if (gbIsHellfireSaveGame != gbIsHellfire) { |
|
|
|
|
RemoveEmptyLevelItems(); |
|
|
|
|
SaveGame(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
gbIsHellfireSaveGame = gbIsHellfire; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void SaveHeroItems(PlayerStruct &player) |
|
|
|
|
{ |
|
|
|
|
size_t itemCount = NUM_INVLOC + NUM_INV_GRID_ELEM + MAXBELTITEMS; |
|
|
|
|
|