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(); }
|
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