#include "vf/SlotAction.h" #include 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 collectRoles(const ContainerMenu& menu, bool wantTransferSection) { std::vector 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 transferTargetsFor(const ContainerMenu& menu, int index) { std::vector container = collectRoles(menu, true); MenuSlot::Role role = menu.slot(index).role; if (isTransferSection(role)) return menu.playerSectionSlots(); if (!container.empty()) return container; std::vector 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;