#pragma once #include #include #include "Container.h" #include "Crafting.h" #include "ItemStack.h" namespace vf { // A menu is the screen an open container puts in front of the player. It does // not replace Container: a Container is still the storage that lives in the // world and that hoppers, droppers and comparators read. The menu binds one for // the duration of the screen, adds the player's own sections next to it, and // gives every cell a role and an admission rule. // What a slot is willing to accept. The result of a craft, a smelt or a trade // takes Nothing, which is what makes it take-only. struct SlotFilter { enum class Kind : uint8_t { Any, Nothing, Fuel, // furnace fuel, per Crafting.h isFuel Smeltable, // has a smelting recipe in the bound registry Lapis, // enchanting table payment Enchantable, // enchantabilityOf > 0 Repairable, // anvil input: tools carry durability, so they repair BrewIngredient, // the brewing ingredients that exist as items here Count }; Kind kind = Kind::Any; // `registry` is only consulted by Smeltable; every other kind answers from // the item tables alone and accepts a null registry. bool accepts(const MetaStack& stack, const CraftingRegistry* registry) const; }; const char* slotFilterKindName(SlotFilter::Kind kind); // One storage cell: a stack plus a cap. `capacity` of zero defers to the item's // own limit, anything else clamps below it (a brewing bottle holds exactly one). struct StackSlot { MetaStack stack; int capacity = 0; bool empty() const { return stack.empty(); } int limitFor(const MetaStack& candidate) const; int room(const MetaStack& candidate) const; // Merges what fits from `incoming`, leaving the rest in it. Returns moved. int insert(MetaStack& incoming); MetaStack take(int amount); MetaStack takeAll(); MetaStack swapWith(const MetaStack& incoming); }; enum class MenuKind : uint8_t { PlayerInventory, CraftingTable, Chest, LargeChest, Furnace, BrewingStand, EnchantingTable, Anvil, Villager, Hopper, Count }; const char* menuKindName(MenuKind kind); struct MenuSlot { // Which part of the screen the slot belongs to. Shift-clicking moves items // between the container half and the player half, so the roles have to be // distinguishable at runtime, not just visually. enum class Role : uint8_t { Storage, // the bound container, or the menu's own storage Inventory, // the player's three rows Hotbar, // the player's bottom row CraftInput, CraftResult, FuelInput, SmeltInput, SmeltOutput, BrewBottle, BrewIngredient, EnchantItem, EnchantLapis, AnvilInput, AnvilResult, TradeInput, TradeResult, OffHand, Count }; StackSlot cell; Role role = Role::Storage; SlotFilter filter; // A take-only slot never accepts a placement, however empty it is. bool takeOnly = false; // Index into the bound Container, or -1 when the menu owns the stack. int backingIndex = -1; }; const char* menuSlotRoleName(MenuSlot::Role role); // Where each section starts and how long it is, so callers can iterate a // section without knowing the slot order by heart. struct MenuLayout { MenuKind kind = MenuKind::Chest; int storageFirst = -1; int storageCount = 0; int inventoryFirst = -1; int inventoryCount = 0; int hotbarFirst = -1; int hotbarCount = 0; int craftFirst = -1; int craftWidth = 0; int craftHeight = 0; int resultSlot = -1; int offHandSlot = -1; int totalSlots = 0; int craftCells() const { return craftWidth * craftHeight; } bool hasCrafting() const { return craftFirst >= 0 && craftCells() > 0; } }; MenuLayout layoutFor(MenuKind kind); constexpr int playerInventoryRows = 3; constexpr int playerInventoryColumns = 9; constexpr int playerInventorySlots = playerInventoryRows * playerInventoryColumns; constexpr int playerHotbarSlots = 9; class ContainerMenu; // The crafting half of a menu, wired to the existing CraftingRegistry. It owns // no stacks and no lifetime: it reads and writes the menu's craft slots in // place, so the grid the player sees and the grid the recipe matcher sees are // the same cells. A menu without a crafting section hands out a view whose // cellCount is zero and whose operations therefore do nothing. class CraftingView { public: explicit CraftingView(ContainerMenu& menu); int width() const; int height() const; int cellCount() const; MetaStack& input(int x, int y); const MetaStack& input(int x, int y) const; MetaStack& inputAt(int cell); const MetaStack& inputAt(int cell) const;