From 7d1aebd91be94ff20cbdeedaa3614548ab4dd33f Mon Sep 17 00:00:00 2001 From: Andrettin <6322423+Andrettin@users.noreply.github.com> Date: Thu, 14 Aug 2025 09:44:55 +0200 Subject: [PATCH] Added support for mapping IDs for item types --- Source/itemdat.cpp | 17 +++++++++++++++-- Source/itemdat.h | 6 ++++-- Source/loadsave.cpp | 28 ++++++++++++++++++---------- Source/lua/modules/items.cpp | 6 +++--- 4 files changed, 40 insertions(+), 17 deletions(-) diff --git a/Source/itemdat.cpp b/Source/itemdat.cpp index 1f6a811f4..621189864 100644 --- a/Source/itemdat.cpp +++ b/Source/itemdat.cpp @@ -24,6 +24,9 @@ namespace devilution { /** Contains the data related to each item ID. */ std::vector AllItemsList; +/** Contains item mapping IDs, with item indices assigned to them. This is used for loading saved games. */ +ankerl::unordered_dense::map ItemMappingIdsToIndices; + /** Contains the mapping between unique base item ID strings and indices, used for parsing additional item data. */ ankerl::unordered_dense::map AdditionalUniqueBaseItemStringsToIndices; @@ -548,10 +551,11 @@ tl::expected ParseAffixAlignment(std::string_view value } // namespace -void LoadItemDatFromFile(DataFile &dataFile, std::string_view filename) +void LoadItemDatFromFile(DataFile &dataFile, std::string_view filename, int32_t baseMappingId) { dataFile.skipHeaderOrDie(filename); + int32_t currentMappingId = baseMappingId; AllItemsList.reserve(AllItemsList.size() + dataFile.numRecords()); for (DataFileRecord record : dataFile) { RecordReader reader { record, filename }; @@ -579,6 +583,14 @@ void LoadItemDatFromFile(DataFile &dataFile, std::string_view filename) reader.read("spell", item.iSpell, ParseSpellId); reader.readBool("usable", item.iUsable); reader.readInt("value", item.iValue); + + item.iMappingId = currentMappingId; + const auto [it, inserted] = ItemMappingIdsToIndices.emplace(item.iMappingId, static_cast(AllItemsList.size()) - 1); + if (!inserted) { + DisplayFatalErrorAndExit("Adding Item Failed", fmt::format("An item already exists for mapping ID {}.", item.iMappingId)); + } + + ++currentMappingId; } AllItemsList.shrink_to_fit(); } @@ -592,7 +604,8 @@ void LoadItemDat() AllItemsList.clear(); AdditionalUniqueBaseItemStringsToIndices.clear(); - LoadItemDatFromFile(dataFile, filename); + ItemMappingIdsToIndices.clear(); + LoadItemDatFromFile(dataFile, filename, 0); LuaEvent("ItemDataLoaded"); } diff --git a/Source/itemdat.h b/Source/itemdat.h index 6ca33d8ed..914db668c 100644 --- a/Source/itemdat.h +++ b/Source/itemdat.h @@ -80,7 +80,7 @@ enum _item_indexes : int16_t { // TODO defines all indexes in AllItemsList IDI_SORCERER_DIABLO, IDI_ARENAPOT, - IDI_LAST = IDI_ARENAPOT, + IDI_NUM_DEFAULT_ITEMS, IDI_NONE = -1, }; @@ -511,6 +511,7 @@ struct ItemData { SpellID iSpell; bool iUsable; uint16_t iValue; + int32_t iMappingId; }; enum item_effect_type : int8_t { @@ -647,12 +648,13 @@ struct UniqueItem { }; extern DVL_API_FOR_TEST std::vector AllItemsList; +extern ankerl::unordered_dense::map ItemMappingIdsToIndices; extern std::vector ItemPrefixes; extern std::vector ItemSuffixes; extern DVL_API_FOR_TEST std::vector UniqueItems; extern ankerl::unordered_dense::map UniqueItemMappingIdsToIndices; -void LoadItemDatFromFile(DataFile &dataFile, std::string_view filename); +void LoadItemDatFromFile(DataFile &dataFile, std::string_view filename, int32_t baseMappingId); void LoadUniqueItemDatFromFile(DataFile &dataFile, std::string_view filename, int32_t baseMappingId); void LoadItemData(); diff --git a/Source/loadsave.cpp b/Source/loadsave.cpp index 9092e7b27..6f9126c91 100644 --- a/Source/loadsave.cpp +++ b/Source/loadsave.cpp @@ -352,13 +352,21 @@ struct LevelConversionData { item._iMinDex = file.NextLE(); file.Skip(1); // Alignment item._iStatFlag = file.NextBool32(); - item.IDidx = static_cast<_item_indexes>(file.NextLE()); - if (gbIsSpawn) { - item.IDidx = RemapItemIdxFromSpawn(item.IDidx); + + int32_t itemMappingId = file.NextLE(); + if (gbIsSpawn && itemMappingId < IDI_NUM_DEFAULT_ITEMS) { + itemMappingId = RemapItemIdxFromSpawn(static_cast<_item_indexes>(itemMappingId)); + } + if (!gbIsHellfireSaveGame && itemMappingId < IDI_NUM_DEFAULT_ITEMS) { + itemMappingId = RemapItemIdxFromDiablo(static_cast<_item_indexes>(itemMappingId)); } - if (!gbIsHellfireSaveGame) { - item.IDidx = RemapItemIdxFromDiablo(item.IDidx); + const auto findIt = ItemMappingIdsToIndices.find(itemMappingId); + if (findIt == ItemMappingIdsToIndices.end()) { + return false; } + const _item_indexes itemIndex = static_cast<_item_indexes>(findIt->second); + item.IDidx = itemIndex; + item.dwBuff = file.NextLE(); if (gbIsHellfireSaveGame) item._iDamAcFlags = static_cast(file.NextLE()); @@ -1147,11 +1155,11 @@ int getHellfireLevelType(int type) void SaveItem(SaveHelper &file, const Item &item) { - auto idx = item.IDidx; - if (!gbIsHellfire) - idx = RemapItemIdxToDiablo(idx); - if (gbIsSpawn) - idx = RemapItemIdxToSpawn(idx); + int32_t idx = item.IDidx != IDI_NONE ? AllItemsList[item.IDidx].iMappingId : -1; + if (!gbIsHellfire && idx < IDI_NUM_DEFAULT_ITEMS) + idx = RemapItemIdxToDiablo(static_cast<_item_indexes>(idx)); + if (gbIsSpawn && idx < IDI_NUM_DEFAULT_ITEMS) + idx = RemapItemIdxToSpawn(static_cast<_item_indexes>(idx)); ItemType iType = item._itype; if (idx == -1) { idx = _item_indexes::IDI_GOLD; diff --git a/Source/lua/modules/items.cpp b/Source/lua/modules/items.cpp index 7486beabd..b84dcd80a 100644 --- a/Source/lua/modules/items.cpp +++ b/Source/lua/modules/items.cpp @@ -453,10 +453,10 @@ void RegisterItemSpecialEffectHfEnum(sol::state_view &lua) }); } -void AddItemDataFromTsv(const std::string_view path) +void AddItemDataFromTsv(const std::string_view path, const int32_t baseMappingId) { DataFile dataFile = DataFile::loadOrDie(path); - LoadItemDatFromFile(dataFile, path); + LoadItemDatFromFile(dataFile, path, baseMappingId); } void AddUniqueItemDataFromTsv(const std::string_view path, const int32_t baseMappingId) @@ -482,7 +482,7 @@ sol::table LuaItemModule(sol::state_view &lua) sol::table table = lua.create_table(); - LuaSetDocFn(table, "addItemDataFromTsv", "(path: string)", AddItemDataFromTsv); + LuaSetDocFn(table, "addItemDataFromTsv", "(path: string, baseMappingId: number)", AddItemDataFromTsv); LuaSetDocFn(table, "addUniqueItemDataFromTsv", "(path: string, baseMappingId: number)", AddUniqueItemDataFromTsv); return table;