From c9149916831bcded92a15950eda992397e5b5b86 Mon Sep 17 00:00:00 2001 From: DualFroz Date: Sun, 6 Sep 2026 22:18:54 +0200 Subject: [PATCH] feat: add isBrewIngredient and appendSection --- core/src/ContainerMenu.cpp | 156 +++++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 core/src/ContainerMenu.cpp diff --git a/core/src/ContainerMenu.cpp b/core/src/ContainerMenu.cpp new file mode 100644 index 0000000..c929d20 --- /dev/null +++ b/core/src/ContainerMenu.cpp @@ -0,0 +1,156 @@ +#include "vf/ContainerMenu.h" + +#include + +namespace vf { + +namespace { + +const MetaStack& emptyStack() { + static const MetaStack nothing; + return nothing; +} + +bool isBrewIngredient(ItemId item) { + // Brewing.h models potions with PotionItem, not ItemId, so only the + // ingredients that also exist as items can be filtered here. + return item == ItemId::RedstoneDust || item == ItemId::Glowstone || + item == ItemId::Gunpowder; +} + +void appendSection(std::vector& slots, MenuSlot::Role role, int count, + SlotFilter::Kind filter, bool takeOnly, int capacity) { + for (int i = 0; i < count; ++i) { + MenuSlot menuSlot; + menuSlot.role = role; + menuSlot.filter.kind = filter; + menuSlot.takeOnly = takeOnly; + menuSlot.cell.capacity = capacity; + slots.push_back(menuSlot); + } +} + +void appendPlayerSections(std::vector& slots, MenuLayout& layout, bool withOffHand) { + layout.inventoryFirst = static_cast(slots.size()); + layout.inventoryCount = playerInventorySlots; + appendSection(slots, MenuSlot::Role::Inventory, playerInventorySlots, SlotFilter::Kind::Any, + false, 0); + + layout.hotbarFirst = static_cast(slots.size()); + layout.hotbarCount = playerHotbarSlots; + appendSection(slots, MenuSlot::Role::Hotbar, playerHotbarSlots, SlotFilter::Kind::Any, false, 0); + + if (withOffHand) { + layout.offHandSlot = static_cast(slots.size()); + appendSection(slots, MenuSlot::Role::OffHand, 1, SlotFilter::Kind::Any, false, 0); + } +} + +} // namespace + +const char* slotFilterKindName(SlotFilter::Kind kind) { + switch (kind) { + case SlotFilter::Kind::Any: return "any"; + case SlotFilter::Kind::Nothing: return "nothing"; + case SlotFilter::Kind::Fuel: return "fuel"; + case SlotFilter::Kind::Smeltable: return "smeltable"; + case SlotFilter::Kind::Lapis: return "lapis"; + case SlotFilter::Kind::Enchantable: return "enchantable"; + case SlotFilter::Kind::Repairable: return "repairable"; + case SlotFilter::Kind::BrewIngredient: return "brewIngredient"; + case SlotFilter::Kind::Count: break; + } + return "unknown"; +} + +bool SlotFilter::accepts(const MetaStack& stack, const CraftingRegistry* registry) const { + if (stack.empty()) return false; + switch (kind) { + case Kind::Any: return true; + case Kind::Nothing: return false; + case Kind::Fuel: return isFuel(stack.item); + case Kind::Smeltable: + return registry != nullptr && registry->smeltingFor(stack.item) != nullptr; + case Kind::Lapis: return stack.item == ItemId::LapisLazuli; + case Kind::Enchantable: return enchantabilityOf(stack.item) > 0; + case Kind::Repairable: return stack.meta.durability.damageable(); + case Kind::BrewIngredient: return isBrewIngredient(stack.item); + case Kind::Count: break; + } + return false; +} + +int StackSlot::limitFor(const MetaStack& candidate) const { + if (candidate.empty()) return 0; + int limit = candidate.maxCount(); + if (capacity > 0 && capacity < limit) limit = capacity; + return limit; +} + +int StackSlot::room(const MetaStack& candidate) const { + if (candidate.empty()) return 0; + if (stack.empty()) return limitFor(candidate); + if (!stacksMergeable(stack, candidate)) return 0; + int left = limitFor(stack) - stack.count; + return left > 0 ? left : 0; +} + +int StackSlot::insert(MetaStack& incoming) { + int allowed = room(incoming); + if (allowed <= 0) return 0; + return transferStack(incoming, stack, allowed).moved; +} + +MetaStack StackSlot::take(int amount) { return splitOff(stack, amount); } + +MetaStack StackSlot::takeAll() { return splitOff(stack, stack.count); } + +MetaStack StackSlot::swapWith(const MetaStack& incoming) { + MetaStack previous = stack; + stack = incoming; + return previous; +} + +const char* menuKindName(MenuKind kind) { + switch (kind) { + case MenuKind::PlayerInventory: return "playerInventory"; + case MenuKind::CraftingTable: return "craftingTable"; + case MenuKind::Chest: return "chest"; + case MenuKind::LargeChest: return "largeChest"; + case MenuKind::Furnace: return "furnace"; + case MenuKind::BrewingStand: return "brewingStand"; + case MenuKind::EnchantingTable: return "enchantingTable"; + case MenuKind::Anvil: return "anvil"; + case MenuKind::Villager: return "villager"; + case MenuKind::Hopper: return "hopper"; + case MenuKind::Count: break; + } + return "unknown"; +} + +const char* menuSlotRoleName(MenuSlot::Role role) { + switch (role) { + case MenuSlot::Role::Storage: return "storage"; + case MenuSlot::Role::Inventory: return "inventory"; + case MenuSlot::Role::Hotbar: return "hotbar"; + case MenuSlot::Role::CraftInput: return "craftInput"; + case MenuSlot::Role::CraftResult: return "craftResult"; + case MenuSlot::Role::FuelInput: return "fuelInput"; + case MenuSlot::Role::SmeltInput: return "smeltInput"; + case MenuSlot::Role::SmeltOutput: return "smeltOutput"; + case MenuSlot::Role::BrewBottle: return "brewBottle"; + case MenuSlot::Role::BrewIngredient: return "brewIngredient"; + case MenuSlot::Role::EnchantItem: return "enchantItem"; + case MenuSlot::Role::EnchantLapis: return "enchantLapis"; + case MenuSlot::Role::AnvilInput: return "anvilInput"; + case MenuSlot::Role::AnvilResult: return "anvilResult"; + case MenuSlot::Role::TradeInput: return "tradeInput"; + case MenuSlot::Role::TradeResult: return "tradeResult"; + case MenuSlot::Role::OffHand: return "offHand"; + case MenuSlot::Role::Count: break; + } + return "unknown"; +} + +namespace { +