feat: extend ContainerMenu
This commit is contained in:
1 parent
77e7fe9c97
commit
73448f18ae
1 file changed
+159
@@ -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();
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user