#include #include #include #include "engine/random.hpp" #include "game_mode.hpp" #include "items.h" #include "player.h" #include "spells.h" #include "utils/str_cat.hpp" namespace devilution { namespace { class ItemsTest : public ::testing::Test { public: void SetUp() override { Players.resize(1); MyPlayer = &Players[0]; for (auto &flag : UniqueItemFlags) flag = false; } static void SetUpTestSuite() { LoadItemData(); LoadSpellData(); gbIsSpawn = false; gbIsMultiplayer = false; // to also test UniqueItemFlags } }; void GenerateAllUniques(bool hellfire, const size_t expectedUniques) { gbIsHellfire = hellfire; std::mt19937 betterRng; std::uniform_int_distribution dist(0, std::numeric_limits::max()); // Get seed for test run from time. If a test run fails, remember the seed and hardcode it here. uint32_t testRunSeed = static_cast(time(nullptr)); betterRng.seed(testRunSeed); std::set foundUniques; constexpr int max_iterations = 1000000; int iteration = 0; for (int uniqueIndex = 0, n = static_cast(UniqueItems.size()); uniqueIndex < n; ++uniqueIndex) { if (!IsUniqueAvailable(uniqueIndex)) continue; if (foundUniques.contains(uniqueIndex)) continue; auto &uniqueItem = UniqueItems[uniqueIndex]; _item_indexes uniqueBaseIndex = IDI_GOLD; for (std::underlying_type_t<_item_indexes> j = IDI_GOLD; j <= IDI_LAST; j++) { if (!IsItemAvailable(j)) continue; if (AllItemsList[j].iItemId != uniqueItem.UIItemId) continue; if (AllItemsList[j].dropRate > 0) uniqueBaseIndex = static_cast<_item_indexes>(j); break; } if (uniqueBaseIndex == IDI_GOLD) continue; // Unique not dropable (Quest-Unique) auto &baseItemData = AllItemsList[static_cast(uniqueBaseIndex)]; while (true) { iteration += 1; if (iteration > max_iterations) FAIL() << StrCat("Item ", uniqueItem.UIName, " not found in ", max_iterations, " tries with test run seed ", testRunSeed); Item testItem = {}; testItem._iMiscId = baseItemData.iMiscId; std::uniform_int_distribution dist(0, INT_MAX); SetRndSeed(dist(betterRng)); int targetLvl = uniqueItem.UIMinLvl; int uper = 1; if (uniqueItem.UIMinLvl - 4 > 0) { uper = 15; targetLvl = uniqueItem.UIMinLvl - 4; } SetupAllItems(*MyPlayer, testItem, uniqueBaseIndex, AdvanceRndSeed(), targetLvl, uper, true, false); TryRandomUniqueItem(testItem, uniqueBaseIndex, targetLvl, uper, true, false); SetupItem(testItem); if (testItem._iMagical != ITEM_QUALITY_UNIQUE) continue; foundUniques.insert(testItem._iUid); if (testItem._iUid == uniqueIndex) break; } } EXPECT_EQ(foundUniques.size(), expectedUniques) << StrCat("test run seed ", testRunSeed); } TEST_F(ItemsTest, AllDiabloUniquesCanDrop) { GenerateAllUniques(false, 79); } TEST_F(ItemsTest, AllHellfireUniquesCanDrop) { GenerateAllUniques(true, 99); } } // namespace } // namespace devilution