#include "testHarness.h" #include "vf/ContainerMenu.h" #include #include using namespace vf; namespace { const CraftingRegistry& sharedRegistry() { static CraftingRegistry registry; return registry; } // A recipe nothing in the default set uses: a tool sits in the grid as a proper // ingredient, which is the only way to exercise the remainder rule. CraftingRegistry registryWithToolRecipe() { CraftingRegistry registry; ShapedRecipe carving; carving.width = 1; carving.height = 2; carving.pattern = {ItemId::IronShovel, ItemId::Planks}; carving.result = {ItemId::Stick, 4}; carving.allowMirror = false; registry.addShaped(carving); return registry; } int countSlotsWithRole(const ContainerMenu& menu, MenuSlot::Role role) { return static_cast(menu.slotsWithRole(role).size()); } } // namespace VF_TEST(everyMenuKindLaysOutTheSectionsItsScreenNeeds) { for (int i = 0; i < static_cast(MenuKind::Count); ++i) { MenuKind kind = static_cast(i); ContainerMenu menu(kind, &sharedRegistry()); CHECK(menu.slotCount() > 0); CHECK_EQ(menu.slotCount(), menu.layout().totalSlots); CHECK_EQ(menu.layout().inventoryCount, playerInventorySlots); CHECK_EQ(menu.layout().hotbarCount, playerHotbarSlots); CHECK(std::string(menuKindName(kind)) != std::string("unknown")); } CHECK_EQ(ContainerMenu(MenuKind::Chest).slotCount(), 27 + 27 + 9); CHECK_EQ(ContainerMenu(MenuKind::LargeChest).slotCount(), 54 + 27 + 9); CHECK_EQ(ContainerMenu(MenuKind::Hopper).slotCount(), 5 + 27 + 9); CHECK_EQ(countSlotsWithRole(ContainerMenu(MenuKind::Hopper), MenuSlot::Role::Storage), 5); } VF_TEST(thePlayerInventoryKeepsItsTwoByTwoGridAndAnOffHandSlot) { ContainerMenu menu(MenuKind::PlayerInventory, &sharedRegistry()); const MenuLayout& layout = menu.layout(); CHECK_EQ(layout.craftWidth, 2); CHECK_EQ(layout.craftHeight, 2); CHECK_EQ(layout.craftCells(), 4); CHECK(layout.hasCrafting()); CHECK(layout.resultSlot >= 0); CHECK(layout.offHandSlot >= 0); CHECK_EQ(countSlotsWithRole(menu, MenuSlot::Role::CraftInput), 4); CHECK_EQ(countSlotsWithRole(menu, MenuSlot::Role::OffHand), 1); CHECK(menu.isPlayerSection(layout.offHandSlot)); CHECK(!menu.isPlayerSection(layout.resultSlot)); } VF_TEST(theCraftingTableGivesTheFullThreeByThreeGrid) { ContainerMenu menu(MenuKind::CraftingTable, &sharedRegistry()); CHECK_EQ(menu.layout().craftWidth, 3); CHECK_EQ(menu.layout().craftHeight, 3); CHECK_EQ(countSlotsWithRole(menu, MenuSlot::Role::CraftInput), 9); CHECK(menu.hasCrafting()); CHECK_EQ(menu.layout().offHandSlot, -1); } VF_TEST(theFurnaceFuelSlotRefusesAnythingThatWillNotBurn) { ContainerMenu menu(MenuKind::Furnace, &sharedRegistry()); std::vector fuelSlots = menu.slotsWithRole(MenuSlot::Role::FuelInput); CHECK_EQ(fuelSlots.size(), 1u); int fuel = fuelSlots[0]; CHECK(menu.canPlace(fuel, makeStack(ItemId::CoalLump, 8))); CHECK(menu.canPlace(fuel, makeStack(ItemId::Planks, 8))); CHECK(!menu.canPlace(fuel, makeStack(ItemId::Stone, 8))); CHECK(!menu.canPlace(fuel, makeStack(ItemId::IronIngot, 1))); MetaStack stone = makeStack(ItemId::Stone, 8); CHECK_EQ(menu.insertAt(fuel, stone), 0); CHECK_EQ(stone.count, 8); MetaStack coal = makeStack(ItemId::CoalLump, 8); CHECK_EQ(menu.insertAt(fuel, coal), 8); CHECK(coal.empty()); CHECK_EQ(menu.stackAt(fuel).count, 8); } VF_TEST(theFurnaceInputSlotOnlyTakesItemsWithASmeltingRecipe) { ContainerMenu menu(MenuKind::Furnace, &sharedRegistry()); int input = menu.slotsWithRole(MenuSlot::Role::SmeltInput)[0]; CHECK(menu.canPlace(input, makeStack(ItemId::IronOre, 4))); CHECK(menu.canPlace(input, makeStack(ItemId::RawBeef, 4))); CHECK(!menu.canPlace(input, makeStack(ItemId::Stick, 4))); CHECK(!menu.canPlace(input, makeStack(ItemId::Glowstone, 4))); // Without a registry there is nothing to consult, so nothing is smeltable. ContainerMenu blind(MenuKind::Furnace, nullptr); CHECK(!blind.canPlace(input, makeStack(ItemId::IronOre, 4))); } VF_TEST(theEnchantingTableTakesOnlyLapisForPaymentAndOnlyGearForTheItem) { ContainerMenu menu(MenuKind::EnchantingTable, &sharedRegistry()); int item = menu.slotsWithRole(MenuSlot::Role::EnchantItem)[0]; int lapis = menu.slotsWithRole(MenuSlot::Role::EnchantLapis)[0]; CHECK(menu.canPlace(lapis, makeStack(ItemId::LapisLazuli, 3))); CHECK(!menu.canPlace(lapis, makeStack(ItemId::DiamondGem, 3))); CHECK(!menu.canPlace(lapis, makeStack(ItemId::LapisOre, 3))); CHECK(menu.canPlace(item, makeTool(ItemId::DiamondSword))); CHECK(!menu.canPlace(item, makeStack(ItemId::Stone, 1))); CHECK(!menu.canPlace(item, makeStack(ItemId::LapisLazuli, 1))); } VF_TEST(theAnvilOnlyAcceptsItemsThatCarryDurability) { ContainerMenu menu(MenuKind::Anvil, &sharedRegistry()); std::vector inputs = menu.slotsWithRole(MenuSlot::Role::AnvilInput); CHECK_EQ(inputs.size(), 2u); CHECK(menu.canPlace(inputs[0], makeTool(ItemId::GoldHoe))); CHECK(menu.canPlace(inputs[1], makeTool(ItemId::DiamondAxe))); CHECK(!menu.canPlace(inputs[0], makeStack(ItemId::IronIngot, 4))); } VF_TEST(theBrewingStandFiltersIngredientsAndHoldsOneItemPerBottleSlot) { ContainerMenu menu(MenuKind::BrewingStand, &sharedRegistry()); int ingredient = menu.slotsWithRole(MenuSlot::Role::BrewIngredient)[0]; std::vector bottles = menu.slotsWithRole(MenuSlot::Role::BrewBottle); CHECK_EQ(bottles.size(), 3u); CHECK(menu.canPlace(ingredient, makeStack(ItemId::Gunpowder, 2))); CHECK(menu.canPlace(ingredient, makeStack(ItemId::RedstoneDust, 2))); CHECK(!menu.canPlace(ingredient, makeStack(ItemId::Stick, 2))); MetaStack pile = makeStack(ItemId::Glowstone, 20); int moved = menu.insertAt(bottles[0], pile); CHECK_EQ(moved, 1); CHECK_EQ(menu.stackAt(bottles[0]).count, 1); CHECK_EQ(pile.count, 19); CHECK_EQ(menu.insertAt(bottles[0], pile), 0); } VF_TEST(aResultSlotCanBeEmptiedButNeverFilledByHand) { ContainerMenu menu(MenuKind::CraftingTable, &sharedRegistry()); int result = menu.layout().resultSlot;