You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
698 lines
18 KiB
698 lines
18 KiB
/** |
|
* @file stash_test.cpp |
|
* |
|
* Tests for the player stash system. |
|
* |
|
* These tests verify the functional behaviour of the shared item stash: |
|
* item placement, removal, page navigation, gold storage, transfer |
|
* operations between stash and inventory, and the dirty flag. |
|
* |
|
* All assertions are on game state (stash contents, grid cells, gold |
|
* values, dirty flag, inventory contents). No assertions on rendering, |
|
* pixel positions, or widget layout. |
|
*/ |
|
|
|
#include <gtest/gtest.h> |
|
|
|
#include "ui_test.hpp" |
|
|
|
#include "inv.h" |
|
#include "items.h" |
|
#include "player.h" |
|
#include "qol/stash.h" |
|
|
|
namespace devilution { |
|
namespace { |
|
|
|
// --------------------------------------------------------------------------- |
|
// Test fixture |
|
// --------------------------------------------------------------------------- |
|
|
|
class StashTest : public UITest { |
|
protected: |
|
void SetUp() override |
|
{ |
|
UITest::SetUp(); |
|
|
|
// Start each test with a completely clean stash. |
|
Stash = {}; |
|
Stash.gold = 0; |
|
Stash.dirty = false; |
|
} |
|
|
|
// --- helpers --- |
|
|
|
/** @brief Create a simple 1×1 item (healing potion). */ |
|
static Item MakeSmallItem() |
|
{ |
|
Item item {}; |
|
InitializeItem(item, IDI_HEAL); |
|
return item; |
|
} |
|
|
|
/** @brief Create a sword (larger than 1×1). */ |
|
static Item MakeSword() |
|
{ |
|
Item item {}; |
|
InitializeItem(item, IDI_BARDSWORD); |
|
item._iIdentified = true; |
|
return item; |
|
} |
|
|
|
/** @brief Create a gold item with the given value. */ |
|
static Item MakeGold(int value) |
|
{ |
|
Item item {}; |
|
item._itype = ItemType::Gold; |
|
item._ivalue = value; |
|
item._iMiscId = IMISC_NONE; |
|
return item; |
|
} |
|
|
|
/** @brief Count the number of non-empty cells in a stash grid page. */ |
|
static int CountOccupiedCells(const StashStruct::StashGrid &grid) |
|
{ |
|
int count = 0; |
|
for (const auto &row : grid) { |
|
for (StashStruct::StashCell cell : row) { |
|
if (cell != 0) |
|
count++; |
|
} |
|
} |
|
return count; |
|
} |
|
|
|
/** @brief Fill a stash page completely with 1×1 items. */ |
|
void FillStashPage(unsigned page) |
|
{ |
|
for (int x = 0; x < 10; x++) { |
|
for (int y = 0; y < 10; y++) { |
|
Item item = MakeSmallItem(); |
|
Stash.SetPage(page); |
|
ASSERT_TRUE(AutoPlaceItemInStash(*MyPlayer, item, true)) |
|
<< "Failed to place item at logical position on page " << page; |
|
} |
|
} |
|
} |
|
}; |
|
|
|
// --------------------------------------------------------------------------- |
|
// AutoPlaceItemInStash |
|
// --------------------------------------------------------------------------- |
|
|
|
TEST_F(StashTest, PlaceItem_EmptyStash) |
|
{ |
|
Item item = MakeSmallItem(); |
|
|
|
bool placed = AutoPlaceItemInStash(*MyPlayer, item, true); |
|
|
|
EXPECT_TRUE(placed); |
|
EXPECT_FALSE(Stash.stashList.empty()); |
|
EXPECT_EQ(Stash.stashList.size(), 1u); |
|
EXPECT_TRUE(Stash.dirty); |
|
} |
|
|
|
TEST_F(StashTest, PlaceItem_DryRunDoesNotMutate) |
|
{ |
|
Item item = MakeSmallItem(); |
|
|
|
bool canPlace = AutoPlaceItemInStash(*MyPlayer, item, false); |
|
|
|
EXPECT_TRUE(canPlace); |
|
EXPECT_TRUE(Stash.stashList.empty()) << "Dry-run should not add item to stashList"; |
|
EXPECT_FALSE(Stash.dirty) << "Dry-run should not set dirty flag"; |
|
} |
|
|
|
TEST_F(StashTest, PlaceItem_GridCellOccupied) |
|
{ |
|
Item item = MakeSmallItem(); |
|
|
|
ASSERT_TRUE(AutoPlaceItemInStash(*MyPlayer, item, true)); |
|
|
|
// The item should occupy at least one cell on the current page. |
|
const auto &grid = Stash.stashGrids[Stash.GetPage()]; |
|
EXPECT_GT(CountOccupiedCells(grid), 0); |
|
} |
|
|
|
TEST_F(StashTest, PlaceItem_MultipleItemsOnSamePage) |
|
{ |
|
Item item1 = MakeSmallItem(); |
|
Item item2 = MakeSmallItem(); |
|
Item item3 = MakeSword(); |
|
|
|
EXPECT_TRUE(AutoPlaceItemInStash(*MyPlayer, item1, true)); |
|
EXPECT_TRUE(AutoPlaceItemInStash(*MyPlayer, item2, true)); |
|
EXPECT_TRUE(AutoPlaceItemInStash(*MyPlayer, item3, true)); |
|
|
|
EXPECT_EQ(Stash.stashList.size(), 3u); |
|
} |
|
|
|
TEST_F(StashTest, PlaceItem_FullPageOverflowsToNextPage) |
|
{ |
|
Stash.SetPage(0); |
|
Stash.dirty = false; |
|
|
|
FillStashPage(0); |
|
|
|
size_t itemsAfterPage0 = Stash.stashList.size(); |
|
|
|
// Page 0 should be completely full now. Placing another item should go to page 1. |
|
Item overflow = MakeSmallItem(); |
|
Stash.SetPage(0); // Reset to page 0 so AutoPlace starts searching from page 0. |
|
EXPECT_TRUE(AutoPlaceItemInStash(*MyPlayer, overflow, true)); |
|
|
|
EXPECT_EQ(Stash.stashList.size(), itemsAfterPage0 + 1); |
|
|
|
// The overflow item should be on page 1 (or later), not page 0. |
|
// Page 0 should still have only the original cells occupied. |
|
const auto &grid0 = Stash.stashGrids[0]; |
|
EXPECT_EQ(CountOccupiedCells(grid0), 100) << "Page 0 should remain fully occupied"; |
|
|
|
// Page 1 should have the overflow item. |
|
EXPECT_TRUE(Stash.stashGrids.count(1) > 0) << "Page 1 should have been created"; |
|
EXPECT_GT(CountOccupiedCells(Stash.stashGrids[1]), 0) << "Overflow item should be on page 1"; |
|
} |
|
|
|
TEST_F(StashTest, PlaceItem_SwordOccupiesCorrectArea) |
|
{ |
|
Item sword = MakeSword(); |
|
const Size swordSize = GetInventorySize(sword); |
|
|
|
ASSERT_TRUE(AutoPlaceItemInStash(*MyPlayer, sword, true)); |
|
|
|
const auto &grid = Stash.stashGrids[Stash.GetPage()]; |
|
int occupiedCells = CountOccupiedCells(grid); |
|
EXPECT_EQ(occupiedCells, swordSize.width * swordSize.height) |
|
<< "Sword should occupy exactly " << swordSize.width << "×" << swordSize.height << " cells"; |
|
} |
|
|
|
// --------------------------------------------------------------------------- |
|
// Gold in stash |
|
// --------------------------------------------------------------------------- |
|
|
|
TEST_F(StashTest, PlaceGold_AddsToStashGold) |
|
{ |
|
Item gold = MakeGold(5000); |
|
|
|
EXPECT_TRUE(AutoPlaceItemInStash(*MyPlayer, gold, true)); |
|
|
|
EXPECT_EQ(Stash.gold, 5000); |
|
EXPECT_TRUE(Stash.stashList.empty()) << "Gold should not be added to stashList"; |
|
EXPECT_TRUE(Stash.dirty); |
|
} |
|
|
|
TEST_F(StashTest, PlaceGold_DryRunDoesNotMutate) |
|
{ |
|
Item gold = MakeGold(3000); |
|
|
|
EXPECT_TRUE(AutoPlaceItemInStash(*MyPlayer, gold, false)); |
|
|
|
EXPECT_EQ(Stash.gold, 0) << "Dry-run should not change stash gold"; |
|
EXPECT_FALSE(Stash.dirty); |
|
} |
|
|
|
TEST_F(StashTest, PlaceGold_AccumulatesMultipleDeposits) |
|
{ |
|
Item gold1 = MakeGold(1000); |
|
Item gold2 = MakeGold(2500); |
|
|
|
ASSERT_TRUE(AutoPlaceItemInStash(*MyPlayer, gold1, true)); |
|
ASSERT_TRUE(AutoPlaceItemInStash(*MyPlayer, gold2, true)); |
|
|
|
EXPECT_EQ(Stash.gold, 3500); |
|
} |
|
|
|
TEST_F(StashTest, PlaceGold_RejectsOverflow) |
|
{ |
|
Stash.gold = std::numeric_limits<int>::max() - 100; |
|
|
|
Item gold = MakeGold(200); |
|
|
|
EXPECT_FALSE(AutoPlaceItemInStash(*MyPlayer, gold, true)) |
|
<< "Should reject gold that would cause integer overflow"; |
|
EXPECT_EQ(Stash.gold, std::numeric_limits<int>::max() - 100) |
|
<< "Stash gold should be unchanged after rejected deposit"; |
|
} |
|
|
|
// --------------------------------------------------------------------------- |
|
// RemoveStashItem |
|
// --------------------------------------------------------------------------- |
|
|
|
TEST_F(StashTest, RemoveItem_ClearsGridAndList) |
|
{ |
|
Item item = MakeSmallItem(); |
|
ASSERT_TRUE(AutoPlaceItemInStash(*MyPlayer, item, true)); |
|
ASSERT_EQ(Stash.stashList.size(), 1u); |
|
|
|
Stash.dirty = false; |
|
Stash.RemoveStashItem(0); |
|
|
|
EXPECT_TRUE(Stash.stashList.empty()); |
|
EXPECT_EQ(CountOccupiedCells(Stash.stashGrids[Stash.GetPage()]), 0) |
|
<< "Grid cells should be cleared after removing item"; |
|
EXPECT_TRUE(Stash.dirty); |
|
} |
|
|
|
TEST_F(StashTest, RemoveItem_LastItemSwap) |
|
{ |
|
// Place two items, then remove the first. The second item should be |
|
// moved to index 0 in stashList, and grid references updated. |
|
Item item1 = MakeSmallItem(); |
|
Item item2 = MakeSword(); |
|
|
|
ASSERT_TRUE(AutoPlaceItemInStash(*MyPlayer, item1, true)); |
|
ASSERT_TRUE(AutoPlaceItemInStash(*MyPlayer, item2, true)); |
|
ASSERT_EQ(Stash.stashList.size(), 2u); |
|
|
|
// Remember the type of the second item. |
|
const ItemType secondItemType = Stash.stashList[1]._itype; |
|
|
|
Stash.RemoveStashItem(0); |
|
|
|
ASSERT_EQ(Stash.stashList.size(), 1u); |
|
// The former item at index 1 should now be at index 0. |
|
EXPECT_EQ(Stash.stashList[0]._itype, secondItemType); |
|
|
|
// Grid should reference the moved item correctly (cell value = index + 1 = 1). |
|
const auto &grid = Stash.stashGrids[Stash.GetPage()]; |
|
bool foundReference = false; |
|
for (const auto &row : grid) { |
|
for (StashStruct::StashCell cell : row) { |
|
if (cell == 1) { // index 0 + 1 |
|
foundReference = true; |
|
} |
|
} |
|
} |
|
EXPECT_TRUE(foundReference) |
|
<< "Grid should have updated references to the swapped item"; |
|
} |
|
|
|
TEST_F(StashTest, RemoveItem_MiddleOfThree) |
|
{ |
|
// Place three items, remove the middle one. The last item should be |
|
// swapped into slot 1, and stashList should have size 2. |
|
Item item1 = MakeSmallItem(); |
|
Item item2 = MakeSmallItem(); |
|
Item item3 = MakeSmallItem(); |
|
|
|
ASSERT_TRUE(AutoPlaceItemInStash(*MyPlayer, item1, true)); |
|
ASSERT_TRUE(AutoPlaceItemInStash(*MyPlayer, item2, true)); |
|
ASSERT_TRUE(AutoPlaceItemInStash(*MyPlayer, item3, true)); |
|
ASSERT_EQ(Stash.stashList.size(), 3u); |
|
|
|
Stash.RemoveStashItem(1); |
|
|
|
EXPECT_EQ(Stash.stashList.size(), 2u); |
|
} |
|
|
|
// --------------------------------------------------------------------------- |
|
// Page navigation |
|
// --------------------------------------------------------------------------- |
|
|
|
TEST_F(StashTest, SetPage_SetsCorrectPage) |
|
{ |
|
Stash.SetPage(5); |
|
EXPECT_EQ(Stash.GetPage(), 5u); |
|
|
|
Stash.SetPage(42); |
|
EXPECT_EQ(Stash.GetPage(), 42u); |
|
} |
|
|
|
TEST_F(StashTest, SetPage_ClampsToLastPage) |
|
{ |
|
// LastStashPage = 99 (CountStashPages - 1). |
|
Stash.SetPage(200); |
|
EXPECT_EQ(Stash.GetPage(), 99u); |
|
} |
|
|
|
TEST_F(StashTest, SetPage_SetsDirtyFlag) |
|
{ |
|
Stash.dirty = false; |
|
Stash.SetPage(3); |
|
EXPECT_TRUE(Stash.dirty); |
|
} |
|
|
|
TEST_F(StashTest, NextPage_AdvancesByOne) |
|
{ |
|
Stash.SetPage(0); |
|
Stash.dirty = false; |
|
|
|
Stash.NextPage(); |
|
EXPECT_EQ(Stash.GetPage(), 1u); |
|
EXPECT_TRUE(Stash.dirty); |
|
} |
|
|
|
TEST_F(StashTest, NextPage_AdvancesByOffset) |
|
{ |
|
Stash.SetPage(5); |
|
|
|
Stash.NextPage(10); |
|
EXPECT_EQ(Stash.GetPage(), 15u); |
|
} |
|
|
|
TEST_F(StashTest, NextPage_ClampsAtLastPage) |
|
{ |
|
Stash.SetPage(98); |
|
|
|
Stash.NextPage(5); |
|
EXPECT_EQ(Stash.GetPage(), 99u) << "Should clamp to last page, not wrap around"; |
|
} |
|
|
|
TEST_F(StashTest, NextPage_AlreadyAtLastPage) |
|
{ |
|
Stash.SetPage(99); |
|
|
|
Stash.NextPage(); |
|
EXPECT_EQ(Stash.GetPage(), 99u) << "Should stay at last page"; |
|
} |
|
|
|
TEST_F(StashTest, PreviousPage_GoesBackByOne) |
|
{ |
|
Stash.SetPage(5); |
|
Stash.dirty = false; |
|
|
|
Stash.PreviousPage(); |
|
EXPECT_EQ(Stash.GetPage(), 4u); |
|
EXPECT_TRUE(Stash.dirty); |
|
} |
|
|
|
TEST_F(StashTest, PreviousPage_GoesBackByOffset) |
|
{ |
|
Stash.SetPage(20); |
|
|
|
Stash.PreviousPage(10); |
|
EXPECT_EQ(Stash.GetPage(), 10u); |
|
} |
|
|
|
TEST_F(StashTest, PreviousPage_ClampsAtPageZero) |
|
{ |
|
Stash.SetPage(2); |
|
|
|
Stash.PreviousPage(5); |
|
EXPECT_EQ(Stash.GetPage(), 0u) << "Should clamp to page 0, not underflow"; |
|
} |
|
|
|
TEST_F(StashTest, PreviousPage_AlreadyAtPageZero) |
|
{ |
|
Stash.SetPage(0); |
|
|
|
Stash.PreviousPage(); |
|
EXPECT_EQ(Stash.GetPage(), 0u) << "Should stay at page 0"; |
|
} |
|
|
|
// --------------------------------------------------------------------------- |
|
// Grid query helpers |
|
// --------------------------------------------------------------------------- |
|
|
|
TEST_F(StashTest, GetItemIdAtPosition_EmptyCell) |
|
{ |
|
Stash.SetPage(0); |
|
|
|
StashStruct::StashCell id = Stash.GetItemIdAtPosition({ 0, 0 }); |
|
EXPECT_EQ(id, StashStruct::EmptyCell); |
|
} |
|
|
|
TEST_F(StashTest, IsItemAtPosition_EmptyCell) |
|
{ |
|
Stash.SetPage(0); |
|
|
|
EXPECT_FALSE(Stash.IsItemAtPosition({ 0, 0 })); |
|
} |
|
|
|
TEST_F(StashTest, GetItemIdAtPosition_OccupiedCell) |
|
{ |
|
Item item = MakeSmallItem(); |
|
Stash.SetPage(0); |
|
ASSERT_TRUE(AutoPlaceItemInStash(*MyPlayer, item, true)); |
|
|
|
// The first item should be placed at (0,0) in an empty stash. |
|
StashStruct::StashCell id = Stash.GetItemIdAtPosition({ 0, 0 }); |
|
EXPECT_NE(id, StashStruct::EmptyCell); |
|
EXPECT_EQ(id, 0u) << "First item should have stashList index 0"; |
|
} |
|
|
|
TEST_F(StashTest, IsItemAtPosition_OccupiedCell) |
|
{ |
|
Item item = MakeSmallItem(); |
|
Stash.SetPage(0); |
|
ASSERT_TRUE(AutoPlaceItemInStash(*MyPlayer, item, true)); |
|
|
|
EXPECT_TRUE(Stash.IsItemAtPosition({ 0, 0 })); |
|
} |
|
|
|
// --------------------------------------------------------------------------- |
|
// TransferItemToInventory |
|
// --------------------------------------------------------------------------- |
|
|
|
TEST_F(StashTest, TransferToInventory_Success) |
|
{ |
|
// Clear inventory so there is room. |
|
StripPlayer(); |
|
|
|
Item item = MakeSmallItem(); |
|
Stash.SetPage(0); |
|
ASSERT_TRUE(AutoPlaceItemInStash(*MyPlayer, item, true)); |
|
ASSERT_EQ(Stash.stashList.size(), 1u); |
|
|
|
TransferItemToInventory(*MyPlayer, 0); |
|
|
|
EXPECT_TRUE(Stash.stashList.empty()) << "Item should be removed from stash"; |
|
EXPECT_EQ(CountOccupiedCells(Stash.stashGrids[0]), 0) |
|
<< "Grid should be cleared"; |
|
|
|
// Item should now be in the player's inventory. |
|
bool foundInInventory = false; |
|
for (int i = 0; i < MyPlayer->_pNumInv; i++) { |
|
if (!MyPlayer->InvList[i].isEmpty()) { |
|
foundInInventory = true; |
|
break; |
|
} |
|
} |
|
EXPECT_TRUE(foundInInventory) << "Item should appear in player inventory"; |
|
} |
|
|
|
TEST_F(StashTest, TransferToInventory_EmptyCell) |
|
{ |
|
// Transferring EmptyCell should be a no-op. |
|
TransferItemToInventory(*MyPlayer, StashStruct::EmptyCell); |
|
|
|
// Nothing should crash and stash should remain unchanged. |
|
EXPECT_TRUE(Stash.stashList.empty()); |
|
} |
|
|
|
TEST_F(StashTest, TransferToInventory_InventoryFull) |
|
{ |
|
// Fill inventory completely so there's no room. |
|
ClearInventory(); |
|
for (int i = 0; i < InventoryGridCells; i++) { |
|
Item filler = MakeSmallItem(); |
|
MyPlayer->InvList[i] = filler; |
|
MyPlayer->InvGrid[i] = static_cast<int8_t>(i + 1); |
|
} |
|
MyPlayer->_pNumInv = InventoryGridCells; |
|
|
|
Item item = MakeSmallItem(); |
|
Stash.SetPage(0); |
|
ASSERT_TRUE(AutoPlaceItemInStash(*MyPlayer, item, true)); |
|
ASSERT_EQ(Stash.stashList.size(), 1u); |
|
|
|
TransferItemToInventory(*MyPlayer, 0); |
|
|
|
// Item should remain in stash because inventory is full. |
|
EXPECT_EQ(Stash.stashList.size(), 1u) |
|
<< "Item should remain in stash when inventory is full"; |
|
} |
|
|
|
// --------------------------------------------------------------------------- |
|
// TransferItemToStash |
|
// --------------------------------------------------------------------------- |
|
|
|
TEST_F(StashTest, TransferToStash_Success) |
|
{ |
|
StripPlayer(); |
|
IsStashOpen = true; |
|
|
|
// Place an item in inventory slot 0. |
|
Item sword = MakeSword(); |
|
int idx = PlaceItemInInventory(sword); |
|
ASSERT_GE(idx, 0); |
|
|
|
int invLocation = INVITEM_INV_FIRST + idx; |
|
|
|
TransferItemToStash(*MyPlayer, invLocation); |
|
|
|
// Item should now be in stash. |
|
EXPECT_FALSE(Stash.stashList.empty()) << "Item should appear in stash"; |
|
|
|
// Item should be removed from inventory. |
|
EXPECT_TRUE(MyPlayer->InvList[idx].isEmpty() || MyPlayer->_pNumInv == 0) |
|
<< "Item should be removed from inventory"; |
|
} |
|
|
|
TEST_F(StashTest, TransferToStash_InvalidLocation) |
|
{ |
|
// Transferring from location -1 should be a no-op. |
|
TransferItemToStash(*MyPlayer, -1); |
|
|
|
EXPECT_TRUE(Stash.stashList.empty()); |
|
} |
|
|
|
// --------------------------------------------------------------------------- |
|
// Dirty flag |
|
// --------------------------------------------------------------------------- |
|
|
|
TEST_F(StashTest, DirtyFlag_SetOnPlaceItem) |
|
{ |
|
Stash.dirty = false; |
|
|
|
Item item = MakeSmallItem(); |
|
ASSERT_TRUE(AutoPlaceItemInStash(*MyPlayer, item, true)); |
|
|
|
EXPECT_TRUE(Stash.dirty); |
|
} |
|
|
|
TEST_F(StashTest, DirtyFlag_SetOnPlaceGold) |
|
{ |
|
Stash.dirty = false; |
|
|
|
Item gold = MakeGold(100); |
|
ASSERT_TRUE(AutoPlaceItemInStash(*MyPlayer, gold, true)); |
|
|
|
EXPECT_TRUE(Stash.dirty); |
|
} |
|
|
|
TEST_F(StashTest, DirtyFlag_SetOnRemoveItem) |
|
{ |
|
Item item = MakeSmallItem(); |
|
ASSERT_TRUE(AutoPlaceItemInStash(*MyPlayer, item, true)); |
|
Stash.dirty = false; |
|
|
|
Stash.RemoveStashItem(0); |
|
|
|
EXPECT_TRUE(Stash.dirty); |
|
} |
|
|
|
TEST_F(StashTest, DirtyFlag_SetOnPageChange) |
|
{ |
|
Stash.dirty = false; |
|
|
|
Stash.SetPage(1); |
|
|
|
EXPECT_TRUE(Stash.dirty); |
|
} |
|
|
|
TEST_F(StashTest, DirtyFlag_NotSetOnDryRun) |
|
{ |
|
Stash.dirty = false; |
|
|
|
Item item = MakeSmallItem(); |
|
AutoPlaceItemInStash(*MyPlayer, item, false); |
|
|
|
EXPECT_FALSE(Stash.dirty); |
|
} |
|
|
|
// --------------------------------------------------------------------------- |
|
// IsStashOpen flag |
|
// --------------------------------------------------------------------------- |
|
|
|
TEST_F(StashTest, IsStashOpen_InitiallyClosed) |
|
{ |
|
EXPECT_FALSE(IsStashOpen); |
|
} |
|
|
|
TEST_F(StashTest, IsStashOpen_CanBeToggled) |
|
{ |
|
IsStashOpen = true; |
|
EXPECT_TRUE(IsStashOpen); |
|
|
|
IsStashOpen = false; |
|
EXPECT_FALSE(IsStashOpen); |
|
} |
|
|
|
// --------------------------------------------------------------------------- |
|
// Edge cases |
|
// --------------------------------------------------------------------------- |
|
|
|
TEST_F(StashTest, PlaceItem_CurrentPagePreferred) |
|
{ |
|
// When the stash is empty, the item should be placed on the current page. |
|
Stash.SetPage(5); |
|
Stash.dirty = false; |
|
|
|
Item item = MakeSmallItem(); |
|
ASSERT_TRUE(AutoPlaceItemInStash(*MyPlayer, item, true)); |
|
|
|
EXPECT_TRUE(Stash.stashGrids.count(5) > 0) |
|
<< "Item should be placed on the current page (5)"; |
|
EXPECT_GT(CountOccupiedCells(Stash.stashGrids[5]), 0); |
|
} |
|
|
|
TEST_F(StashTest, PlaceItem_WrapsAroundPages) |
|
{ |
|
// Set page to 99 (last page), fill it, then place another item. |
|
// It should wrap around to page 0. |
|
Stash.SetPage(99); |
|
FillStashPage(99); |
|
|
|
Item overflow = MakeSmallItem(); |
|
Stash.SetPage(99); // Reset to page 99 so search starts there. |
|
EXPECT_TRUE(AutoPlaceItemInStash(*MyPlayer, overflow, true)); |
|
|
|
// The item should have been placed on page 0 (wrapped around). |
|
EXPECT_TRUE(Stash.stashGrids.count(0) > 0) |
|
<< "Item should wrap around to page 0"; |
|
EXPECT_GT(CountOccupiedCells(Stash.stashGrids[0]), 0); |
|
} |
|
|
|
TEST_F(StashTest, MultipleItemTypes_CoexistOnSamePage) |
|
{ |
|
Stash.SetPage(0); |
|
|
|
Item potion = MakeSmallItem(); |
|
Item sword = MakeSword(); |
|
|
|
ASSERT_TRUE(AutoPlaceItemInStash(*MyPlayer, potion, true)); |
|
ASSERT_TRUE(AutoPlaceItemInStash(*MyPlayer, sword, true)); |
|
|
|
EXPECT_EQ(Stash.stashList.size(), 2u); |
|
|
|
// Both items should be on page 0. |
|
const auto &grid = Stash.stashGrids[0]; |
|
const Size swordSize = GetInventorySize(sword); |
|
int expectedCells = 1 + (swordSize.width * swordSize.height); |
|
EXPECT_EQ(CountOccupiedCells(grid), expectedCells); |
|
} |
|
|
|
TEST_F(StashTest, RemoveItem_ThenPlaceNew) |
|
{ |
|
// Place an item, remove it, then place a new one. The stash should |
|
// reuse the slot correctly. |
|
Item item1 = MakeSmallItem(); |
|
ASSERT_TRUE(AutoPlaceItemInStash(*MyPlayer, item1, true)); |
|
ASSERT_EQ(Stash.stashList.size(), 1u); |
|
|
|
Stash.RemoveStashItem(0); |
|
ASSERT_TRUE(Stash.stashList.empty()); |
|
|
|
Item item2 = MakeSword(); |
|
ASSERT_TRUE(AutoPlaceItemInStash(*MyPlayer, item2, true)); |
|
EXPECT_EQ(Stash.stashList.size(), 1u); |
|
} |
|
|
|
TEST_F(StashTest, GoldStorageIndependentOfItems) |
|
{ |
|
// Gold and items use separate storage. Verify they don't interfere. |
|
Stash.SetPage(0); |
|
|
|
Item gold = MakeGold(5000); |
|
ASSERT_TRUE(AutoPlaceItemInStash(*MyPlayer, gold, true)); |
|
|
|
Item item = MakeSmallItem(); |
|
ASSERT_TRUE(AutoPlaceItemInStash(*MyPlayer, item, true)); |
|
|
|
EXPECT_EQ(Stash.gold, 5000) << "Gold should be tracked separately"; |
|
EXPECT_EQ(Stash.stashList.size(), 1u) << "Only the non-gold item should be in stashList"; |
|
} |
|
|
|
} // namespace |
|
} // namespace devilution
|