diff --git a/Source/items.cpp b/Source/items.cpp index 96f046ed4..228105545 100644 --- a/Source/items.cpp +++ b/Source/items.cpp @@ -1901,13 +1901,13 @@ _item_indexes RndSmithItem(const Player &player, int lvl) return RndVendorItem(player, 0, lvl); } -void SortVendor(Item *itemList, size_t count) +void SortVendor(std::span itemList, size_t PinnedItemCount) { auto cmp = [](const Item &a, const Item &b) { return a.IDidx < b.IDidx; }; - std::sort(itemList, itemList + count, cmp); + std::sort(itemList.begin() + PinnedItemCount, itemList.end(), cmp); } bool PremiumItemOk(const Player & /*player*/, const ItemData &item) @@ -4388,10 +4388,11 @@ void SpawnSmith(int lvl) maxItems = NumSmithBasicItemsHf; } - const int iCnt = RandomIntBetween(10, maxItems); - for (int i = 0; i < iCnt; i++) { - Item &newItem = SmithItems[i]; + const size_t iCnt = RandomIntBetween(10, maxItems); + SmithItems.clear(); + while (SmithItems.size() < iCnt) { + Item newItem; do { newItem = {}; newItem._iSeed = AdvanceRndSeed(); @@ -4402,41 +4403,46 @@ void SpawnSmith(int lvl) newItem._iCreateInfo = lvl | CF_SMITH; newItem._iIdentified = true; + + SmithItems.push_back(newItem); } - for (int i = iCnt; i < NumSmithBasicItemsHf; i++) - SmithItems[i].clear(); - SortVendor(SmithItems + PinnedItemCount, iCnt - PinnedItemCount); + SortVendor(SmithItems, PinnedItemCount); +} + +void ReplacePremium(const Player &player, int idx) +{ + int plvl = gbIsHellfire ? itemLevelAddHf[idx] : itemLevelAdd[idx]; + plvl += player.getCharacterLevel(); + SpawnOnePremium(PremiumItems[idx], plvl, player); } void SpawnPremium(const Player &player) { const int lvl = player.getCharacterLevel(); - const int maxItems = gbIsHellfire ? NumSmithItemsHf : NumSmithItems; - if (PremiumItemCount < maxItems) { - for (int i = 0; i < maxItems; i++) { - if (PremiumItems[i].isEmpty()) { - const int plvl = PremiumItemLevel + (gbIsHellfire ? itemLevelAddHf[i] : itemLevelAdd[i]); - SpawnOnePremium(PremiumItems[i], plvl, player); - } - } - PremiumItemCount = maxItems; + const size_t maxItems = gbIsHellfire ? NumSmithItemsHf : NumSmithItems; + + while (PremiumItems.size() < maxItems) { + int plvl = PremiumItemLevel + (gbIsHellfire ? itemLevelAddHf[PremiumItems.size()] : itemLevelAdd[PremiumItems.size()]); + Item item = {}; + SpawnOnePremium(item, plvl, player); + PremiumItems.push_back(item); } + while (PremiumItemLevel < lvl) { PremiumItemLevel++; + Item *ptr = PremiumItems.begin(); if (gbIsHellfire) { - // Discard first 3 items and shift next 10 - std::move(&PremiumItems[3], &PremiumItems[12] + 1, &PremiumItems[0]); - SpawnOnePremium(PremiumItems[10], PremiumItemLevel + itemLevelAddHf[10], player); + std::move(ptr + 3, ptr + 13, ptr); PremiumItems[11] = PremiumItems[13]; - SpawnOnePremium(PremiumItems[12], PremiumItemLevel + itemLevelAddHf[12], player); PremiumItems[13] = PremiumItems[14]; + SpawnOnePremium(PremiumItems[10], PremiumItemLevel + itemLevelAddHf[10], player); + SpawnOnePremium(PremiumItems[12], PremiumItemLevel + itemLevelAddHf[12], player); SpawnOnePremium(PremiumItems[14], PremiumItemLevel + itemLevelAddHf[14], player); } else { - // Discard first 2 items and shift next 3 - std::move(&PremiumItems[2], &PremiumItems[4] + 1, &PremiumItems[0]); - SpawnOnePremium(PremiumItems[3], PremiumItemLevel + itemLevelAdd[3], player); + std::move(ptr + 2, ptr + 5, ptr); PremiumItems[4] = PremiumItems[5]; + SpawnOnePremium(PremiumItems[3], PremiumItemLevel + itemLevelAdd[3], player); SpawnOnePremium(PremiumItems[5], PremiumItemLevel + itemLevelAdd[5], player); } } @@ -4453,16 +4459,17 @@ void SpawnWitch(int lvl) const int pinnedBookCount = gbIsHellfire ? RandomIntLessThan(MaxPinnedBookCount) : 0; const int itemCount = RandomIntBetween(10, gbIsHellfire ? NumWitchItemsHf : NumWitchItems); const int maxValue = gbIsHellfire ? MaxVendorValueHf : MaxVendorValue; + WitchItems.clear(); - for (int i = 0; i < NumWitchItemsHf; i++) { - Item &item = WitchItems[i]; - item = {}; + for (int i = 0; i < itemCount; i++) { + Item item = {}; if (i < PinnedItemCount) { item._iSeed = AdvanceRndSeed(); GetItemAttrs(item, PinnedItemTypes[i], 1); item._iCreateInfo = lvl; item._iStatFlag = true; + WitchItems.push_back(item); continue; } @@ -4477,16 +4484,12 @@ void SpawnWitch(int lvl) item._iCreateInfo = lvl | CF_WITCH; item._iIdentified = true; bookCount++; + WitchItems.push_back(item); continue; } } } - if (i >= itemCount) { - item.clear(); - continue; - } - do { item = {}; item._iSeed = AdvanceRndSeed(); @@ -4504,9 +4507,11 @@ void SpawnWitch(int lvl) item._iCreateInfo = lvl | CF_WITCH; item._iIdentified = true; + + WitchItems.push_back(item); } - SortVendor(WitchItems + PinnedItemCount, itemCount - PinnedItemCount); + SortVendor(WitchItems, PinnedItemCount); } void SpawnBoy(int lvl) @@ -4629,34 +4634,30 @@ void SpawnHealer(int lvl) { constexpr size_t PinnedItemCount = NumHealerPinnedItems; constexpr std::array<_item_indexes, PinnedItemCount + 1> PinnedItemTypes = { IDI_HEAL, IDI_FULLHEAL, IDI_RESURRECT }; - const auto itemCount = static_cast(RandomIntBetween(10, gbIsHellfire ? NumHealerItemsHf : NumHealerItems)); + const size_t itemCount = static_cast(RandomIntBetween(10, gbIsHellfire ? NumHealerItemsHf : NumHealerItems)); + HealerItems.clear(); - for (size_t i = 0; i < sizeof(HealerItems) / sizeof(HealerItems[0]); ++i) { - Item &item = HealerItems[i]; - item = {}; + for (size_t i = 0; i < itemCount; i++) { + Item item = {}; if (i < PinnedItemCount || (gbIsMultiplayer && i < NumHealerPinnedItemsMp)) { item._iSeed = AdvanceRndSeed(); GetItemAttrs(item, PinnedItemTypes[i], 1); item._iCreateInfo = lvl; item._iStatFlag = true; - continue; - } - - if (i >= itemCount) { - item.clear(); - continue; + } else { + item._iSeed = AdvanceRndSeed(); + SetRndSeed(item._iSeed); + const _item_indexes itype = RndHealerItem(*MyPlayer, lvl); + GetItemAttrs(item, itype, lvl); + item._iCreateInfo = lvl | CF_HEALER; + item._iIdentified = true; } - item._iSeed = AdvanceRndSeed(); - SetRndSeed(item._iSeed); - const _item_indexes itype = RndHealerItem(*MyPlayer, lvl); - GetItemAttrs(item, itype, lvl); - item._iCreateInfo = lvl | CF_HEALER; - item._iIdentified = true; + HealerItems.push_back(item); } - SortVendor(HealerItems + PinnedItemCount, itemCount - PinnedItemCount); + SortVendor(HealerItems, PinnedItemCount); } void MakeGoldStack(Item &goldItem, int value) diff --git a/Source/items.h b/Source/items.h index 70bf4e0e0..a208e75d2 100644 --- a/Source/items.h +++ b/Source/items.h @@ -557,6 +557,7 @@ void UseItem(Player &player, item_misc_id Mid, SpellID spellID, int spellFrom); bool UseItemOpensHive(const Item &item, Point position); bool UseItemOpensGrave(const Item &item, Point position); void SpawnSmith(int lvl); +void ReplacePremium(const Player &player, int idx); void SpawnPremium(const Player &player); void SpawnWitch(int lvl); void SpawnBoy(int lvl); diff --git a/Source/stores.cpp b/Source/stores.cpp index 2392e498e..e8d255d72 100644 --- a/Source/stores.cpp +++ b/Source/stores.cpp @@ -40,14 +40,14 @@ int CurrentItemIndex; int8_t PlayerItemIndexes[48]; Item PlayerItems[48]; -Item SmithItems[NumSmithBasicItemsHf]; +StaticVector SmithItems; int PremiumItemCount; int PremiumItemLevel; -Item PremiumItems[NumSmithItemsHf]; +StaticVector PremiumItems; -Item HealerItems[20]; +StaticVector HealerItems; -Item WitchItems[NumWitchItemsHf]; +StaticVector WitchItems; int BoyItemLevel; Item BoyItem; @@ -354,22 +354,18 @@ bool StoreAutoPlace(Item &item, bool persistItem) return CanFitItemInInventory(player, item); } -void ScrollVendorStore(Item *itemData, int storeLimit, int idx, int selling = true) +void ScrollVendorStore(std::span itemData, int storeLimit, int idx, int selling = true) { ClearSText(5, 21); PreviousScrollPos = 5; for (int l = 5; l < 20 && idx < storeLimit; l += 4) { const Item &item = itemData[idx]; - if (!item.isEmpty()) { - const UiFlags itemColor = item.getTextColorWithStatCheck(); - AddSText(20, l, item.getName(), itemColor, true, item._iCurs, true); - AddSTextVal(l, item._iIdentified ? item._iIvalue : item._ivalue); - PrintStoreItem(item, l + 1, itemColor, true); - NextScrollPos = l; - } else { - l -= 4; - } + const UiFlags itemColor = item.getTextColorWithStatCheck(); + AddSText(20, l, item.getName(), itemColor, true, item._iCurs, true); + AddSTextVal(l, item._iIdentified ? item._iIvalue : item._ivalue); + PrintStoreItem(item, l + 1, itemColor, true); + NextScrollPos = l; idx++; } if (selling) { @@ -399,7 +395,7 @@ void StartSmith() void ScrollSmithBuy(int idx) { - ScrollVendorStore(SmithItems, static_cast(std::size(SmithItems)), idx); + ScrollVendorStore(SmithItems, static_cast(SmithItems.size()), idx); } uint32_t TotalPlayerGold() @@ -427,9 +423,6 @@ void StartSmithBuy() CurrentItemIndex = 0; for (Item &item : SmithItems) { - if (item.isEmpty()) - continue; - item._iStatFlag = MyPlayer->CanUseItem(item); CurrentItemIndex++; } @@ -445,16 +438,13 @@ void ScrollSmithPremiumBuy(int boughtitems) boughtitems--; } - ScrollVendorStore(PremiumItems, static_cast(std::size(PremiumItems)), idx); + ScrollVendorStore(PremiumItems, static_cast(PremiumItems.size()), idx); } bool StartSmithPremiumBuy() { CurrentItemIndex = 0; for (Item &item : PremiumItems) { - if (item.isEmpty()) - continue; - item._iStatFlag = MyPlayer->CanUseItem(item); CurrentItemIndex++; } @@ -695,7 +685,7 @@ void StartWitch() void ScrollWitchBuy(int idx) { - ScrollVendorStore(WitchItems, static_cast(std::size(WitchItems)), idx); + ScrollVendorStore(WitchItems, static_cast(WitchItems.size()), idx); } void WitchBookLevel(Item &bookItem) @@ -729,9 +719,6 @@ void StartWitchBuy() CurrentItemIndex = 0; for (Item &item : WitchItems) { - if (item.isEmpty()) - continue; - WitchBookLevel(item); item._iStatFlag = MyPlayer->CanUseItem(item); CurrentItemIndex++; @@ -1044,7 +1031,7 @@ void StartHealer() void ScrollHealerBuy(int idx) { - ScrollVendorStore(HealerItems, static_cast(std::size(HealerItems)), idx); + ScrollVendorStore(HealerItems, static_cast(HealerItems.size()), idx); } void StartHealerBuy() @@ -1062,9 +1049,6 @@ void StartHealerBuy() CurrentItemIndex = 0; for (Item &item : HealerItems) { - if (item.isEmpty()) - continue; - item._iStatFlag = MyPlayer->CanUseItem(item); CurrentItemIndex++; } @@ -1322,14 +1306,7 @@ void SmithBuyItem(Item &item) item._iIdentified = false; StoreAutoPlace(item, true); int idx = OldScrollPos + ((OldTextLine - PreviousScrollPos) / 4); - if (idx == NumSmithBasicItemsHf - 1) { - SmithItems[NumSmithBasicItemsHf - 1].clear(); - } else { - for (; !SmithItems[idx + 1].isEmpty(); idx++) { - SmithItems[idx] = std::move(SmithItems[idx + 1]); - } - SmithItems[idx].clear(); - } + SmithItems.erase(SmithItems.begin() + idx); CalcPlrInv(*MyPlayer, true); } @@ -1371,17 +1348,7 @@ void SmithBuyPItem(Item &item) StoreAutoPlace(item, true); int idx = OldScrollPos + ((OldTextLine - PreviousScrollPos) / 4); - int xx = 0; - for (int i = 0; idx >= 0; i++) { - if (!PremiumItems[i].isEmpty()) { - idx--; - xx = i; - } - } - - PremiumItems[xx].clear(); - PremiumItemCount--; - SpawnPremium(*MyPlayer); + ReplacePremium(*MyPlayer, idx); } void SmithPremiumBuyEnter() @@ -1396,14 +1363,7 @@ void SmithPremiumBuyEnter() OldTextLine = CurrentTextLine; OldScrollPos = ScrollPos; - int xx = ScrollPos + ((CurrentTextLine - PreviousScrollPos) / 4); - int idx = 0; - for (int i = 0; xx >= 0; i++) { - if (!PremiumItems[i].isEmpty()) { - xx--; - idx = i; - } - } + int idx = ScrollPos + ((CurrentTextLine - PreviousScrollPos) / 4); if (!PlayerCanAfford(PremiumItems[idx]._iIvalue)) { StartStore(TalkID::NoMoney); @@ -1574,14 +1534,7 @@ void WitchBuyItem(Item &item) StoreAutoPlace(item, true); if (idx >= 3) { - if (idx == NumWitchItemsHf - 1) { - WitchItems[NumWitchItemsHf - 1].clear(); - } else { - for (; !WitchItems[idx + 1].isEmpty(); idx++) { - WitchItems[idx] = std::move(WitchItems[idx + 1]); - } - WitchItems[idx].clear(); - } + WitchItems.erase(WitchItems.begin() + idx); } CalcPlrInv(*MyPlayer, true); @@ -1747,14 +1700,7 @@ void HealerBuyItem(Item &item) return; } idx = OldScrollPos + ((OldTextLine - PreviousScrollPos) / 4); - if (idx == 19) { - HealerItems[19].clear(); - } else { - for (; !HealerItems[idx + 1].isEmpty(); idx++) { - HealerItems[idx] = std::move(HealerItems[idx + 1]); - } - HealerItems[idx].clear(); - } + HealerItems.erase(HealerItems.begin() + idx); CalcPlrInv(*MyPlayer, true); } @@ -2121,8 +2067,10 @@ void InitStores() PremiumItemCount = 0; PremiumItemLevel = 1; - for (auto &premiumitem : PremiumItems) - premiumitem.clear(); + SmithItems.clear(); + WitchItems.clear(); + HealerItems.clear(); + PremiumItems.clear(); BoyItem.clear(); BoyItemLevel = 0; @@ -2271,12 +2219,7 @@ void StartStore(TalkID s) StartSmith(); break; case TalkID::SmithBuy: { - bool hasAnyItems = false; - for (int i = 0; !SmithItems[i].isEmpty(); i++) { - hasAnyItems = true; - break; - } - if (hasAnyItems) + if (!SmithItems.empty()) StartSmithBuy(); else { ActiveStore = TalkID::SmithBuy; diff --git a/Source/stores.h b/Source/stores.h index f9eb0be75..a6471f4b0 100644 --- a/Source/stores.h +++ b/Source/stores.h @@ -14,6 +14,7 @@ #include "engine/surface.hpp" #include "game_mode.hpp" #include "utils/attributes.h" +#include "utils/static_vector.hpp" namespace devilution { @@ -72,19 +73,19 @@ extern int8_t PlayerItemIndexes[48]; extern DVL_API_FOR_TEST Item PlayerItems[48]; /** Items sold by Griswold */ -extern DVL_API_FOR_TEST Item SmithItems[NumSmithBasicItemsHf]; +extern DVL_API_FOR_TEST StaticVector SmithItems; /** Number of premium items for sale by Griswold */ extern DVL_API_FOR_TEST int PremiumItemCount; /** Base level of current premium items sold by Griswold */ extern DVL_API_FOR_TEST int PremiumItemLevel; /** Premium items sold by Griswold */ -extern DVL_API_FOR_TEST Item PremiumItems[NumSmithItemsHf]; +extern DVL_API_FOR_TEST StaticVector PremiumItems; /** Items sold by Pepin */ -extern DVL_API_FOR_TEST Item HealerItems[20]; +extern DVL_API_FOR_TEST StaticVector HealerItems; /** Items sold by Adria */ -extern DVL_API_FOR_TEST Item WitchItems[NumWitchItemsHf]; +extern DVL_API_FOR_TEST StaticVector WitchItems; /** Current level of the item sold by Wirt */ extern int BoyItemLevel; diff --git a/test/vendor_test.cpp b/test/vendor_test.cpp index 88c5a858a..b6d067d9b 100644 --- a/test/vendor_test.cpp +++ b/test/vendor_test.cpp @@ -81,6 +81,7 @@ public: Players.resize(1); MyPlayer = &Players[0]; gbIsHellfire = false; + PremiumItemLevel = 1; CreatePlayer(*MyPlayer, HeroClass::Warrior); SetRndSeed(SEED); } @@ -149,62 +150,45 @@ std::string misctype_str(item_misc_id type) TEST_F(VendorTest, SmithGen) { MyPlayer->setCharacterLevel(25); - - // Clear global state for test, and force Diablo game mode - for (int i = 0; i < NumSmithBasicItemsHf; i++) { - SmithItems[i].clear(); - } - gbIsHellfire = false; - + SmithItems.clear(); SpawnSmith(16); SetRndSeed(SEED); const int N_ITEMS = RandomIntBetween(10, NumSmithBasicItems); - int n_items = 0; + EXPECT_EQ(SmithItems.size(), N_ITEMS); + EXPECT_LE(SmithItems.size(), NumSmithBasicItems); - for (int i = 0; i < NumSmithBasicItems; i++) { - if (SmithItems[i].isEmpty()) break; + for (size_t i = 0; i < SmithItems.size(); i++) { EXPECT_THAT(SmithItems[i]._itype, SmithTypeMatch(i)); - n_items++; } - EXPECT_EQ(n_items, N_ITEMS); } TEST_F(VendorTest, SmithGenHf) { MyPlayer->setCharacterLevel(25); - - // Clear global state for test, and force Hellfire game mode - for (int i = 0; i < NumSmithBasicItemsHf; i++) { - SmithItems[i].clear(); - } + SmithItems.clear(); gbIsHellfire = true; - SpawnSmith(16); SetRndSeed(SEED); const int N_ITEMS = RandomIntBetween(10, NumSmithBasicItemsHf); - int n_items = 0; + EXPECT_EQ(SmithItems.size(), N_ITEMS); + EXPECT_LE(SmithItems.size(), NumSmithBasicItemsHf); - for (int i = 0; i < NumSmithBasicItemsHf; i++) { - if (SmithItems[i].isEmpty()) break; + for (size_t i = 0; i < SmithItems.size(); i++) { EXPECT_THAT(SmithItems[i]._itype, SmithTypeMatchHf(i)); - n_items++; } - EXPECT_EQ(n_items, N_ITEMS); } TEST_F(VendorTest, PremiumQlvl1to5) { - for (int i = 0; i < NumSmithItems; i++) { - PremiumItems[i].clear(); - } - PremiumItemLevel = 1; - - // Test level 1 character item qlvl + // Test starting the game as a level 1 character MyPlayer->setCharacterLevel(1); + PremiumItems.clear(); SpawnPremium(*MyPlayer); - for (int i = 0; i < NumSmithItems; i++) { + EXPECT_EQ(PremiumItems.size(), NumSmithItems); + + for (size_t i = 0; i < PremiumItems.size(); i++) { constexpr int QLVLS[] = { 1, 1, 1, 1, 2, 3 }; EXPECT_EQ(PremiumItems[i]._iCreateInfo & CF_LEVEL, QLVLS[i]) << "Index: " << i; EXPECT_THAT(PremiumItems[i]._itype, AnyOf(SmithTypeMatch(i), PremiumTypeMatch(i))); @@ -213,7 +197,9 @@ TEST_F(VendorTest, PremiumQlvl1to5) // Test level ups MyPlayer->setCharacterLevel(5); SpawnPremium(*MyPlayer); - for (int i = 0; i < NumSmithItems; i++) { + EXPECT_EQ(PremiumItems.size(), NumSmithItems); + + for (size_t i = 0; i < PremiumItems.size(); i++) { constexpr int QLVLS[] = { 4, 4, 5, 5, 6, 7 }; EXPECT_EQ(PremiumItems[i]._iCreateInfo & CF_LEVEL, QLVLS[i]) << "Index: " << i; EXPECT_THAT(PremiumItems[i]._itype, AnyOf(SmithTypeMatch(i), PremiumTypeMatch(i))); @@ -224,26 +210,24 @@ TEST_F(VendorTest, PremiumQlvl25) { constexpr int QLVLS[] = { 24, 24, 25, 25, 26, 27 }; - for (int i = 0; i < NumSmithItems; i++) { - PremiumItems[i].clear(); - } - PremiumItemLevel = 1; - - // Test starting game as a level 25 character + // Test starting the game as a level 25 character MyPlayer->setCharacterLevel(25); + PremiumItems.clear(); SpawnPremium(*MyPlayer); - for (int i = 0; i < NumSmithItems; i++) { + EXPECT_EQ(PremiumItems.size(), NumSmithItems); + + for (size_t i = 0; i < PremiumItems.size(); i++) { EXPECT_EQ(PremiumItems[i]._iCreateInfo & CF_LEVEL, QLVLS[i]) << "Index: " << i; EXPECT_THAT(PremiumItems[i]._itype, AnyOf(SmithTypeMatch(i), PremiumTypeMatch(i))); } // Test buying select items - PremiumItems[0].clear(); - PremiumItems[3].clear(); - PremiumItems[5].clear(); - PremiumItemCount -= 3; - SpawnPremium(*MyPlayer); - for (int i = 0; i < NumSmithItems; i++) { + ReplacePremium(*MyPlayer, 0); + ReplacePremium(*MyPlayer, 3); + ReplacePremium(*MyPlayer, 5); + EXPECT_EQ(PremiumItems.size(), NumSmithItems); + + for (size_t i = 0; i < PremiumItems.size(); i++) { EXPECT_EQ(PremiumItems[i]._iCreateInfo & CF_LEVEL, QLVLS[i]) << "Index: " << i; EXPECT_THAT(PremiumItems[i]._itype, AnyOf(SmithTypeMatch(i), PremiumTypeMatch(i))); } @@ -253,26 +237,24 @@ TEST_F(VendorTest, PremiumQlvl30Plus) { constexpr int QLVLS[] = { 30, 30, 30, 30, 30, 30 }; - for (int i = 0; i < NumSmithItems; i++) { - PremiumItems[i].clear(); - } - PremiumItemLevel = 1; - // Finally test level 30+ characters MyPlayer->setCharacterLevel(31); + PremiumItems.clear(); SpawnPremium(*MyPlayer); - for (int i = 0; i < NumSmithItems; i++) { + EXPECT_EQ(PremiumItems.size(), NumSmithItems); + + for (size_t i = 0; i < PremiumItems.size(); i++) { EXPECT_EQ(PremiumItems[i]._iCreateInfo & CF_LEVEL, QLVLS[i]) << "Index: " << i; EXPECT_THAT(PremiumItems[i]._itype, AnyOf(SmithTypeMatch(i), PremiumTypeMatch(i))); } // Test buying select items - PremiumItems[0].clear(); - PremiumItems[3].clear(); - PremiumItems[5].clear(); - PremiumItemCount -= 3; - SpawnPremium(*MyPlayer); - for (int i = 0; i < NumSmithItems; i++) { + ReplacePremium(*MyPlayer, 0); + ReplacePremium(*MyPlayer, 3); + ReplacePremium(*MyPlayer, 5); + EXPECT_EQ(PremiumItems.size(), NumSmithItems); + + for (size_t i = 0; i < PremiumItems.size(); i++) { EXPECT_EQ(PremiumItems[i]._iCreateInfo & CF_LEVEL, QLVLS[i]) << "Index: " << i; EXPECT_THAT(PremiumItems[i]._itype, AnyOf(SmithTypeMatch(i), PremiumTypeMatch(i))); } @@ -280,18 +262,20 @@ TEST_F(VendorTest, PremiumQlvl30Plus) // Test 30+ levelling MyPlayer->setCharacterLevel(35); SpawnPremium(*MyPlayer); - for (int i = 0; i < NumSmithItems; i++) { + EXPECT_EQ(PremiumItems.size(), NumSmithItems); + + for (size_t i = 0; i < PremiumItems.size(); i++) { EXPECT_EQ(PremiumItems[i]._iCreateInfo & CF_LEVEL, QLVLS[i]) << "Index: " << i; EXPECT_THAT(PremiumItems[i]._itype, AnyOf(SmithTypeMatch(i), PremiumTypeMatch(i))); } // Test buying select items - PremiumItems[0].clear(); - PremiumItems[3].clear(); - PremiumItems[5].clear(); - PremiumItemCount -= 3; - SpawnPremium(*MyPlayer); - for (int i = 0; i < NumSmithItems; i++) { + ReplacePremium(*MyPlayer, 0); + ReplacePremium(*MyPlayer, 3); + ReplacePremium(*MyPlayer, 5); + EXPECT_EQ(PremiumItems.size(), NumSmithItems); + + for (size_t i = 0; i < PremiumItems.size(); i++) { EXPECT_EQ(PremiumItems[i]._iCreateInfo & CF_LEVEL, QLVLS[i]) << "Index: " << i; EXPECT_THAT(PremiumItems[i]._itype, AnyOf(SmithTypeMatch(i), PremiumTypeMatch(i))); } @@ -299,16 +283,14 @@ TEST_F(VendorTest, PremiumQlvl30Plus) TEST_F(VendorTest, HfPremiumQlvl1to5) { - for (int i = 0; i < NumSmithItemsHf; i++) { - PremiumItems[i].clear(); - } - PremiumItemLevel = 1; - gbIsHellfire = true; - // Test level 1 character item qlvl MyPlayer->setCharacterLevel(1); + PremiumItems.clear(); + gbIsHellfire = true; SpawnPremium(*MyPlayer); - for (int i = 0; i < NumSmithItemsHf; i++) { + EXPECT_EQ(PremiumItems.size(), NumSmithItemsHf); + + for (size_t i = 0; i < PremiumItems.size(); i++) { constexpr int QLVLS[] = { 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 4, 4 }; EXPECT_EQ(PremiumItems[i]._iCreateInfo & CF_LEVEL, QLVLS[i]) << "Index: " << i; EXPECT_THAT(PremiumItems[i]._itype, AnyOf(SmithTypeMatchHf(i), PremiumTypeMatch(i))); @@ -317,7 +299,9 @@ TEST_F(VendorTest, HfPremiumQlvl1to5) // Test level ups MyPlayer->setCharacterLevel(5); SpawnPremium(*MyPlayer); - for (int i = 0; i < NumSmithItemsHf; i++) { + EXPECT_EQ(PremiumItems.size(), NumSmithItemsHf); + + for (size_t i = 0; i < PremiumItems.size(); i++) { constexpr int QLVLS[] = { 3, 3, 4, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 8 }; EXPECT_EQ(PremiumItems[i]._iCreateInfo & CF_LEVEL, QLVLS[i]) << "Index: " << i; EXPECT_THAT(PremiumItems[i]._itype, AnyOf(SmithTypeMatchHf(i), PremiumTypeMatch(i))); @@ -326,28 +310,26 @@ TEST_F(VendorTest, HfPremiumQlvl1to5) TEST_F(VendorTest, HfPremiumQlvl25) { - for (int i = 0; i < NumSmithItemsHf; i++) { - PremiumItems[i].clear(); - } - PremiumItemLevel = 1; - gbIsHellfire = true; - // Test starting game as a level 25 character MyPlayer->setCharacterLevel(25); + PremiumItems.clear(); + gbIsHellfire = true; SpawnPremium(*MyPlayer); - for (int i = 0; i < NumSmithItemsHf; i++) { + EXPECT_EQ(PremiumItems.size(), NumSmithItemsHf); + + for (size_t i = 0; i < PremiumItems.size(); i++) { constexpr int QLVLS[] = { 23, 23, 23, 24, 24, 24, 25, 25, 25, 26, 26, 26, 27, 27, 28 }; EXPECT_EQ(PremiumItems[i]._iCreateInfo & CF_LEVEL, QLVLS[i]) << "Index: " << i; EXPECT_THAT(PremiumItems[i]._itype, AnyOf(SmithTypeMatchHf(i), PremiumTypeMatch(i))); } // Test buying select items - PremiumItems[0].clear(); - PremiumItems[7].clear(); - PremiumItems[14].clear(); - PremiumItemCount -= 3; - SpawnPremium(*MyPlayer); - for (int i = 0; i < NumSmithItemsHf; i++) { + ReplacePremium(*MyPlayer, 0); + ReplacePremium(*MyPlayer, 7); + ReplacePremium(*MyPlayer, 14); + EXPECT_EQ(PremiumItems.size(), NumSmithItemsHf); + + for (size_t i = 0; i < PremiumItems.size(); i++) { constexpr int QLVLS[] = { 24, 23, 23, 24, 24, 24, 25, 26, 25, 26, 26, 26, 27, 27, 28 }; EXPECT_EQ(PremiumItems[i]._iCreateInfo & CF_LEVEL, QLVLS[i]) << "Index: " << i; EXPECT_THAT(PremiumItems[i]._itype, AnyOf(SmithTypeMatchHf(i), PremiumTypeMatch(i))); @@ -356,28 +338,26 @@ TEST_F(VendorTest, HfPremiumQlvl25) TEST_F(VendorTest, HfPremiumQlvl30Plus) { - for (int i = 0; i < NumSmithItemsHf; i++) { - PremiumItems[i].clear(); - } - PremiumItemLevel = 1; - gbIsHellfire = true; - // Finally test level 30+ characters MyPlayer->setCharacterLevel(31); + PremiumItems.clear(); + gbIsHellfire = true; SpawnPremium(*MyPlayer); - for (int i = 0; i < NumSmithItemsHf; i++) { + EXPECT_EQ(PremiumItems.size(), NumSmithItemsHf); + + for (size_t i = 0; i < PremiumItems.size(); i++) { constexpr int QLVLS[] = { 29, 29, 29, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30 }; EXPECT_EQ(PremiumItems[i]._iCreateInfo & CF_LEVEL, QLVLS[i]) << "Index: " << i; EXPECT_THAT(PremiumItems[i]._itype, AnyOf(SmithTypeMatchHf(i), PremiumTypeMatch(i))); } // Test buying select items - PremiumItems[0].clear(); - PremiumItems[7].clear(); - PremiumItems[14].clear(); - PremiumItemCount -= 3; - SpawnPremium(*MyPlayer); - for (int i = 0; i < NumSmithItemsHf; i++) { + ReplacePremium(*MyPlayer, 0); + ReplacePremium(*MyPlayer, 7); + ReplacePremium(*MyPlayer, 14); + EXPECT_EQ(PremiumItems.size(), NumSmithItemsHf); + + for (size_t i = 0; i < PremiumItems.size(); i++) { constexpr int QLVLS[] = { 30, 29, 29, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30 }; EXPECT_EQ(PremiumItems[i]._iCreateInfo & CF_LEVEL, QLVLS[i]) << "Index: " << i; EXPECT_THAT(PremiumItems[i]._itype, AnyOf(SmithTypeMatchHf(i), PremiumTypeMatch(i))); @@ -388,18 +368,20 @@ TEST_F(VendorTest, HfPremiumQlvl30Plus) // Test 30+ levelling MyPlayer->setCharacterLevel(35); SpawnPremium(*MyPlayer); - for (int i = 0; i < NumSmithItems; i++) { + EXPECT_EQ(PremiumItems.size(), NumSmithItemsHf); + + for (size_t i = 0; i < PremiumItems.size(); i++) { EXPECT_EQ(PremiumItems[i]._iCreateInfo & CF_LEVEL, QLVLS[i]) << "Index: " << i; EXPECT_THAT(PremiumItems[i]._itype, AnyOf(SmithTypeMatchHf(i), PremiumTypeMatch(i))); } // Test buying select items - PremiumItems[0].clear(); - PremiumItems[7].clear(); - PremiumItems[14].clear(); - PremiumItemCount -= 3; - SpawnPremium(*MyPlayer); - for (int i = 0; i < NumSmithItems; i++) { + ReplacePremium(*MyPlayer, 0); + ReplacePremium(*MyPlayer, 7); + ReplacePremium(*MyPlayer, 14); + EXPECT_EQ(PremiumItems.size(), NumSmithItemsHf); + + for (size_t i = 0; i < PremiumItems.size(); i++) { EXPECT_EQ(PremiumItems[i]._iCreateInfo & CF_LEVEL, QLVLS[i]) << "Index: " << i; EXPECT_THAT(PremiumItems[i]._itype, AnyOf(SmithTypeMatchHf(i), PremiumTypeMatch(i))); } @@ -410,34 +392,24 @@ TEST_F(VendorTest, WitchGen) constexpr _item_indexes PINNED_ITEMS[] = { IDI_MANA, IDI_FULLMANA, IDI_PORTAL }; MyPlayer->setCharacterLevel(25); - - // Clear global state for test, and force Diablo game mode - for (int i = 0; i < NumWitchItemsHf; i++) { - WitchItems[i].clear(); - } - gbIsHellfire = false; - + WitchItems.clear(); SpawnWitch(16); SetRndSeed(SEED); const int N_ITEMS = RandomIntBetween(10, NumWitchItems); - - int n_items = NumWitchPinnedItems; - - for (int i = 0; i < NumWitchPinnedItems; i++) { - EXPECT_EQ(WitchItems[i].IDidx, PINNED_ITEMS[i]) << "Index: " << i; - } - - for (int i = NumWitchPinnedItems; i < NumWitchItems; i++) { - if (WitchItems[i].isEmpty()) break; - EXPECT_THAT(WitchItems[i]._itype, WitchTypeMatch(i)); - - if (WitchItems[i]._itype == ItemType::Misc) { - EXPECT_THAT(WitchItems[i]._iMiscId, WitchMiscMatch(i)); + EXPECT_EQ(WitchItems.size(), N_ITEMS); + EXPECT_LE(WitchItems.size(), NumWitchItems); + + for (size_t i = 0; i < WitchItems.size(); i++) { + if (i < NumWitchPinnedItems) { + EXPECT_EQ(WitchItems[i].IDidx, PINNED_ITEMS[i]) << "Index: " << i; + } else { + EXPECT_THAT(WitchItems[i]._itype, WitchTypeMatch(i)); + if (WitchItems[i]._itype == ItemType::Misc) { + EXPECT_THAT(WitchItems[i]._iMiscId, WitchMiscMatch(i)); + } } - n_items++; } - EXPECT_EQ(n_items, N_ITEMS); } TEST_F(VendorTest, WitchGenHf) @@ -446,38 +418,29 @@ TEST_F(VendorTest, WitchGenHf) constexpr int MAX_PINNED_BOOKS = 4; MyPlayer->setCharacterLevel(25); + WitchItems.clear(); gbIsHellfire = true; - - // Clear global state for test, and force Hellfire game mode - for (int i = 0; i < NumWitchItemsHf; i++) { - WitchItems[i].clear(); - } - SpawnWitch(16); SetRndSeed(SEED); const int N_PINNED_BOOKS = RandomIntLessThan(MAX_PINNED_BOOKS); const int N_ITEMS = RandomIntBetween(10, NumWitchItemsHf); + EXPECT_EQ(WitchItems.size(), N_ITEMS); + EXPECT_LE(WitchItems.size(), NumWitchItemsHf); int n_books = 0; - int n_items = NumWitchPinnedItems; - - for (int i = 0; i < NumWitchPinnedItems; i++) { - EXPECT_EQ(WitchItems[i].IDidx, PINNED_ITEMS[i]) << "Index: " << i; - } - - for (int i = NumWitchPinnedItems; i < NumWitchItemsHf; i++) { - if (WitchItems[i].isEmpty()) break; - EXPECT_THAT(WitchItems[i]._itype, WitchTypeMatch(i)); - - if (WitchItems[i]._itype == ItemType::Misc) { - EXPECT_THAT(WitchItems[i]._iMiscId, WitchMiscMatch(i)); + for (size_t i = 0; i < WitchItems.size(); i++) { + if (i < NumWitchPinnedItems) { + EXPECT_EQ(WitchItems[i].IDidx, PINNED_ITEMS[i]) << "Index: " << i; + } else { + EXPECT_THAT(WitchItems[i]._itype, WitchTypeMatch(i)); + if (WitchItems[i]._itype == ItemType::Misc) { + EXPECT_THAT(WitchItems[i]._iMiscId, WitchMiscMatch(i)); + } + if (WitchItems[i]._iMiscId == IMISC_BOOK) n_books++; } - if (WitchItems[i]._iMiscId == IMISC_BOOK) n_books++; - n_items++; } EXPECT_GE(n_books, N_PINNED_BOOKS); - EXPECT_EQ(n_items, N_ITEMS); } TEST_F(VendorTest, HealerGen) @@ -485,30 +448,22 @@ TEST_F(VendorTest, HealerGen) constexpr _item_indexes PINNED_ITEMS[] = { IDI_HEAL, IDI_FULLHEAL, IDI_RESURRECT }; MyPlayer->setCharacterLevel(25); - - // Clear global state for test, and force Diablo game mode - for (int i = 0; i < NumHealerItemsHf; i++) { - HealerItems[i].clear(); - } - gbIsHellfire = false; - + HealerItems.clear(); SpawnHealer(16); SetRndSeed(SEED); const int N_ITEMS = RandomIntBetween(10, NumHealerItems); - int n_items = NumHealerPinnedItems; - - for (int i = 0; i < NumHealerPinnedItems; i++) { - EXPECT_EQ(HealerItems[i].IDidx, PINNED_ITEMS[i]) << "Index: " << i; - } - - for (int i = NumHealerPinnedItems; i < NumHealerItems; i++) { - if (HealerItems[i].isEmpty()) break; - EXPECT_THAT(HealerItems[i]._itype, Eq(ItemType::Misc)); - EXPECT_THAT(HealerItems[i]._iMiscId, HealerMiscMatch(i)); - n_items++; + EXPECT_EQ(HealerItems.size(), N_ITEMS); + EXPECT_LE(HealerItems.size(), NumHealerItems); + + for (size_t i = 0; i < HealerItems.size(); i++) { + if (i < NumHealerPinnedItems) { + EXPECT_EQ(HealerItems[i].IDidx, PINNED_ITEMS[i]) << "Index: " << i; + } else { + EXPECT_THAT(HealerItems[i]._itype, Eq(ItemType::Misc)); + EXPECT_THAT(HealerItems[i]._iMiscId, HealerMiscMatch(i)); + } } - EXPECT_EQ(n_items, N_ITEMS); } TEST_F(VendorTest, HealerGenHf) @@ -516,30 +471,23 @@ TEST_F(VendorTest, HealerGenHf) constexpr _item_indexes PINNED_ITEMS[] = { IDI_HEAL, IDI_FULLHEAL, IDI_RESURRECT }; MyPlayer->setCharacterLevel(25); - - // Clear global state for test, and force Hellfire game mode - for (int i = 0; i < NumHealerItemsHf; i++) { - HealerItems[i].clear(); - } + HealerItems.clear(); gbIsHellfire = true; - SpawnHealer(16); SetRndSeed(SEED); const int N_ITEMS = RandomIntBetween(10, NumHealerItemsHf); - int n_items = NumHealerPinnedItems; - - for (int i = 0; i < NumHealerPinnedItems; i++) { - EXPECT_EQ(HealerItems[i].IDidx, PINNED_ITEMS[i]) << "Index: " << i; - } - - for (int i = NumHealerPinnedItems; i < NumHealerItemsHf; i++) { - if (HealerItems[i].isEmpty()) break; - EXPECT_THAT(HealerItems[i]._itype, Eq(ItemType::Misc)); - EXPECT_THAT(HealerItems[i]._iMiscId, HealerMiscMatch(i)); - n_items++; + EXPECT_EQ(HealerItems.size(), N_ITEMS); + EXPECT_LE(HealerItems.size(), NumHealerItemsHf); + + for (size_t i = 0; i < HealerItems.size(); i++) { + if (i < NumHealerPinnedItems) { + EXPECT_EQ(HealerItems[i].IDidx, PINNED_ITEMS[i]) << "Index: " << i; + } else { + EXPECT_THAT(HealerItems[i]._itype, Eq(ItemType::Misc)); + EXPECT_THAT(HealerItems[i]._iMiscId, HealerMiscMatch(i)); + } } - EXPECT_EQ(n_items, N_ITEMS); } } // namespace