feat: extend ContainerMenu

This commit is contained in:
dualfroz committed 2026-09-06 22:35:42 +02:00
1 parent 77e7fe9c97
commit 73448f18ae
1 file changed
+159
+159
View File
@@ -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<int> 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<int> ContainerMenu::slotsWithRole(MenuSlot::Role role) const {
std::vector<int> 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<int> ContainerMenu::playerSectionSlots() const {
std::vector<int> found;
for (int i = 0; i < slotCount(); ++i) {
if (isPlayerSection(i)) found.push_back(i);
}
return found;
}
std::vector<int> ContainerMenu::containerSectionSlots() const {
std::vector<int> found;
for (int i = 0; i < slotCount(); ++i) {
if (!isPlayerSection(i)) found.push_back(i);
}
return found;
}
int ContainerMenu::distributeInto(const std::vector<int>& 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<int> section = containerSectionSlots();
int limit = std::min(static_cast<int>(section.size()), container.size());
for (int i = 0; i < limit; ++i) slot(section[static_cast<std::size_t>(i)]).backingIndex = i;
syncFromContainer();
}