feat: add isTransferSection and isResultSlot

This commit is contained in:
dualfroz committed 2026-09-09 18:04:33 +02:00
1 parent f61d9eed00
commit 70ced743d5
1 file changed
+153
+153
View File
@@ -0,0 +1,153 @@
#include "vf/SlotAction.h"
#include <algorithm>
namespace vf {
namespace {
// The half of a menu a shift-click aims at. Craft inputs and result slots are
// deliberately not part of it: shift-clicking in a player inventory moves items
// between the rows and the hotbar, it does not stuff the 2x2 grid.
bool isTransferSection(MenuSlot::Role role) {
switch (role) {
case MenuSlot::Role::Storage:
case MenuSlot::Role::FuelInput:
case MenuSlot::Role::SmeltInput:
case MenuSlot::Role::SmeltOutput:
case MenuSlot::Role::BrewBottle:
case MenuSlot::Role::BrewIngredient:
case MenuSlot::Role::EnchantItem:
case MenuSlot::Role::EnchantLapis:
case MenuSlot::Role::AnvilInput:
case MenuSlot::Role::AnvilResult:
case MenuSlot::Role::TradeInput:
case MenuSlot::Role::TradeResult:
return true;
case MenuSlot::Role::Inventory:
case MenuSlot::Role::Hotbar:
case MenuSlot::Role::OffHand:
case MenuSlot::Role::CraftInput:
case MenuSlot::Role::CraftResult:
case MenuSlot::Role::Count:
return false;
}
return false;
}
std::vector<int> collectRoles(const ContainerMenu& menu, bool wantTransferSection) {
std::vector<int> found;
for (int i = 0; i < menu.slotCount(); ++i) {
if (isTransferSection(menu.slot(i).role) == wantTransferSection) found.push_back(i);
}
return found;
}
// Where a shift-click from `index` should land. Container half to player half
// and back; in a menu that has no container half, the player's rows and the
// hotbar swap roles instead.
std::vector<int> transferTargetsFor(const ContainerMenu& menu, int index) {
std::vector<int> container = collectRoles(menu, true);
MenuSlot::Role role = menu.slot(index).role;
if (isTransferSection(role)) return menu.playerSectionSlots();
if (!container.empty()) return container;
std::vector<int> targets;
MenuSlot::Role wanted =
role == MenuSlot::Role::Hotbar ? MenuSlot::Role::Inventory : MenuSlot::Role::Hotbar;
for (int i = 0; i < menu.slotCount(); ++i) {
if (menu.slot(i).role == wanted) targets.push_back(i);
}
return targets;
}
bool isResultSlot(const ContainerMenu& menu, int index) {
return menu.layout().resultSlot >= 0 && index == menu.layout().resultSlot;
}
void touchCraftInput(ContainerMenu& menu, int index) {
if (menu.slot(index).role == MenuSlot::Role::CraftInput) menu.refreshResult();
}
// Every removal from a slot goes through here so the crafting grid can never
// hand out a result without paying for it.
MetaStack takeFromSlot(ContainerMenu& menu, int index, int amount, SlotActionResult& out) {
if (isResultSlot(menu, index)) {
CraftingView view = menu.crafting();
MetaStack crafted = view.takeResult();
if (!crafted.empty()) {
out.craftedResult = true;
out.craftedCount += crafted.count;
}
return crafted;
}
MetaStack taken = menu.takeFrom(index, amount);
touchCraftInput(menu, index);
return taken;
}
int putIntoSlot(ContainerMenu& menu, int index, MetaStack& incoming) {
int moved = menu.insertAt(index, incoming);
if (moved > 0) touchCraftInput(menu, index);
return moved;
}
bool swapWithSlot(ContainerMenu& menu, int index, CursorStack& cursor) {
MenuSlot& target = menu.slot(index);
if (target.takeOnly) return false;
if (!menu.canPlace(index, cursor.held)) return false;
if (target.cell.limitFor(cursor.held) < cursor.held.count) return false;
cursor.held = target.cell.swapWith(cursor.held);
touchCraftInput(menu, index);
return true;
}
SlotActionResult leftClick(ContainerMenu& menu, CursorStack& cursor, const SlotAction& action) {
SlotActionResult out;
int index = action.slotIndex;
if (!menu.validIndex(index)) return out;
if (cursor.empty()) {
MetaStack taken = takeFromSlot(menu, index, menu.stackAt(index).count, out);
if (taken.empty()) return out;
out.moved = taken.count;
cursor.held = taken;
out.cursorChanged = true;
out.applied = true;
return out;
}
if (isResultSlot(menu, index)) {
const MetaStack& pending = menu.stackAt(index);
if (pending.empty() || !stacksMergeable(cursor.held, pending)) return out;
if (cursor.held.room() < pending.count) return out;
MetaStack crafted = takeFromSlot(menu, index, pending.count, out);
out.moved = crafted.count;
transferAll(crafted, cursor.held);
out.cursorChanged = true;
out.applied = true;
return out;
}
int moved = putIntoSlot(menu, index, cursor.held);
if (moved > 0) {
out.moved = moved;
out.cursorChanged = true;
out.applied = true;
return out;
}
if (menu.stackAt(index).empty() || stacksMergeable(menu.stackAt(index), cursor.held)) return out;
if (!swapWithSlot(menu, index, cursor)) return out;
out.moved = cursor.held.count + menu.stackAt(index).count;
out.cursorChanged = true;
out.applied = true;
return out;
}
SlotActionResult rightClick(ContainerMenu& menu, CursorStack& cursor, const SlotAction& action) {
SlotActionResult out;
int index = action.slotIndex;
if (!menu.validIndex(index)) return out;