diff --git a/core/src/ContainerMenu.cpp b/core/src/ContainerMenu.cpp index aa898ce..620df81 100644 --- a/core/src/ContainerMenu.cpp +++ b/core/src/ContainerMenu.cpp @@ -309,3 +309,162 @@ const MetaStack& CraftingView::result() const { bool CraftingView::hasResult() const { return !result().empty(); } +int CraftingView::maxCrafts() const { + if (!hasResult()) return 0; + int rounds = -1; + for (int cell = 0; cell < cellCount(); ++cell) { + const MetaStack& stack = inputAt(cell); + if (stack.empty()) continue; + int available = stack.meta.durability.damageable() + ? stack.meta.durability.remaining() + : stack.count; + if (rounds < 0 || available < rounds) rounds = available; + } + return rounds < 0 ? 0 : rounds; +} + +void CraftingView::consumeOneRound() { + for (int cell = 0; cell < cellCount(); ++cell) { + MetaStack& stack = inputAt(cell); + if (stack.empty()) continue; + MetaStack leftover = craftingRemainder(stack); + if (!leftover.empty()) { + stack = leftover; + continue; + } + stack.count -= 1; + if (stack.count <= 0) stack.clear(); + } +} + +MetaStack CraftingView::takeResult() { + if (!hasResult()) return MetaStack{}; + MetaStack crafted = menu->stackAt(menu->layout().resultSlot); + consumeOneRound(); + refresh(); + return crafted; +} + +int CraftingView::craftAll(int limit) { + int crafted = 0; + std::vector targets = menu->playerSectionSlots(); + while (crafted < limit && hasResult()) { + MetaStack batch = takeResult(); + if (batch.empty()) break; + ++crafted; + menu->distributeInto(targets, batch); + if (!batch.empty()) { + // No room left: the unplaced remainder goes back to the result slot + // and crafting stops, exactly as a full inventory does in game. + menu->stackAt(menu->layout().resultSlot) = batch; + break; + } + } + return crafted; +} + +ContainerMenu::ContainerMenu(MenuKind kind, const CraftingRegistry* registry) + : menuKind(kind), craftingRegistry(registry) { + buildSlots(); +} + +void ContainerMenu::buildSlots() { slots = buildLayout(menuKind, menuLayout); } + +bool ContainerMenu::canPlace(int index, const MetaStack& incoming) const { + if (!validIndex(index) || incoming.empty()) return false; + const MenuSlot& target = slot(index); + if (target.takeOnly) return false; + return target.filter.accepts(incoming, craftingRegistry); +} + +bool ContainerMenu::canTake(int index) const { + return validIndex(index) && !stackAt(index).empty(); +} + +int ContainerMenu::insertAt(int index, MetaStack& incoming) { + if (!canPlace(index, incoming)) return 0; + return slot(index).cell.insert(incoming); +} + +MetaStack ContainerMenu::takeFrom(int index, int amount) { + if (!validIndex(index)) return MetaStack{}; + return slot(index).cell.take(amount); +} + +std::vector ContainerMenu::slotsWithRole(MenuSlot::Role role) const { + std::vector found; + for (int i = 0; i < slotCount(); ++i) { + if (slot(i).role == role) found.push_back(i); + } + return found; +} + +bool ContainerMenu::isPlayerSection(int index) const { + MenuSlot::Role role = slot(index).role; + return role == MenuSlot::Role::Inventory || role == MenuSlot::Role::Hotbar || + role == MenuSlot::Role::OffHand; +} + +std::vector ContainerMenu::playerSectionSlots() const { + std::vector found; + for (int i = 0; i < slotCount(); ++i) { + if (isPlayerSection(i)) found.push_back(i); + } + return found; +} + +std::vector ContainerMenu::containerSectionSlots() const { + std::vector found; + for (int i = 0; i < slotCount(); ++i) { + if (!isPlayerSection(i)) found.push_back(i); + } + return found; +} + +int ContainerMenu::distributeInto(const std::vector& targets, MetaStack& incoming) { + int moved = 0; + for (int index : targets) { + if (incoming.empty()) break; + if (stackAt(index).empty()) continue; + moved += insertAt(index, incoming); + } + for (int index : targets) { + if (incoming.empty()) break; + if (!stackAt(index).empty()) continue; + moved += insertAt(index, incoming); + } + return moved; +} + +int ContainerMenu::totalOf(ItemId item) const { + int total = 0; + for (const MenuSlot& menuSlot : slots) { + if (!menuSlot.cell.stack.empty() && menuSlot.cell.stack.item == item) { + total += menuSlot.cell.stack.count; + } + } + return total; +} + +int ContainerMenu::totalItems() const { + int total = 0; + for (const MenuSlot& menuSlot : slots) total += menuSlot.cell.stack.count; + return total; +} + +int ContainerMenu::usedSlots() const { + int used = 0; + for (const MenuSlot& menuSlot : slots) { + if (!menuSlot.cell.stack.empty()) ++used; + } + return used; +} + +void ContainerMenu::bindContainer(Container& container) { + bound = &container; + std::vector section = containerSectionSlots(); + int limit = std::min(static_cast(section.size()), container.size()); + for (int i = 0; i < limit; ++i) slot(section[static_cast(i)]).backingIndex = i; + syncFromContainer(); +} +