diff --git a/core/include/vf/ContainerMenu.h b/core/include/vf/ContainerMenu.h index d047bcf..91798e2 100644 --- a/core/include/vf/ContainerMenu.h +++ b/core/include/vf/ContainerMenu.h @@ -158,3 +158,107 @@ public: MetaStack& inputAt(int cell); const MetaStack& inputAt(int cell) const; + // Recomputes the result slot from the current grid. Cheap enough to call + // after every single change, which is exactly what the menu does. + void refresh(); + + const MetaStack& result() const; + bool hasResult() const; + + // How many times the current grid could be crafted before running dry. + int maxCrafts() const; + + // Takes the result once. Each input loses one item; a damageable ingredient + // is not eaten but comes back one durability point worse, and a broken one + // vanishes. Returns the crafted stack, empty when there was no recipe. + MetaStack takeResult(); + + // Shift-clicking the result: crafts until the grid runs out or the player + // has no room, pushing every batch into the player's sections. + int craftAll(int limit); + +private: + void consumeOneRound(); + CraftGrid gridOfIds() const; + + ContainerMenu* menu; +}; + +class ContainerMenu { +public: + explicit ContainerMenu(MenuKind kind, const CraftingRegistry* registry = nullptr); + + MenuKind kind() const { return menuKind; } + const MenuLayout& layout() const { return menuLayout; } + const CraftingRegistry* registry() const { return craftingRegistry; } + + int slotCount() const { return static_cast(slots.size()); } + MenuSlot& slot(int index) { return slots[static_cast(index)]; } + const MenuSlot& slot(int index) const { return slots[static_cast(index)]; } + MetaStack& stackAt(int index) { return slot(index).cell.stack; } + const MetaStack& stackAt(int index) const { return slot(index).cell.stack; } + + bool validIndex(int index) const { return index >= 0 && index < slotCount(); } + bool canPlace(int index, const MetaStack& incoming) const; + // True when the slot's contents may leave it. Every slot allows this; the + // asymmetry is deliberate, a result slot gives but never takes. + bool canTake(int index) const; + + // Puts what fits into the slot, honouring the filter and the cap. + int insertAt(int index, MetaStack& incoming); + MetaStack takeFrom(int index, int amount); + + // Section walkers used by shift-click and by the double-click gather. + std::vector slotsWithRole(MenuSlot::Role role) const; + std::vector playerSectionSlots() const; + std::vector containerSectionSlots() const; + bool isPlayerSection(int index) const; + + // Fills matching stacks first and only then empty slots, which is the order + // a shift-click has to follow to feel right. + int distributeInto(const std::vector& targets, MetaStack& incoming); + + int totalOf(ItemId item) const; + int totalItems() const; + int usedSlots() const; + + // Binds a world Container as the storage section. The menu copies the + // container in, and syncToContainer writes it back. ItemSlot has no room + // for enchantments or names, so anything richer than durability survives + // only inside the menu (or through stackListToNbt). + void bindContainer(Container& container); + void syncFromContainer(); + void syncToContainer(); + bool hasContainer() const { return bound != nullptr; } + void unbind(); + + // Creative mode is the one thing allowed to conjure items: a middle click + // clones a stack instead of doing nothing. + void setCreativeMode(bool enabled) { creative = enabled; } + bool creativeMode() const { return creative; } + + bool hasCrafting() const { return menuLayout.hasCrafting() && craftingRegistry != nullptr; } + // A two-pointer handle, cheap to hand out by value and never stale. + CraftingView crafting(); + const MetaStack& resultStack() const; + + // Called by CraftingView and by every slot action that touched a craft + // input, so the result slot is never stale. + void refreshResult(); + + // Persists every slot losslessly, unlike the Container projection. + NbtTag toNbt() const; + bool fromNbt(const NbtTag& tag); + +private: + void buildSlots(); + + MenuKind menuKind; + MenuLayout menuLayout; + const CraftingRegistry* craftingRegistry = nullptr; + std::vector slots; + Container* bound = nullptr; + bool creative = false; +}; + +} // namespace vf