Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 115 additions & 0 deletions src/main/java/net/tfminecraft/cooking/manager/ConversionManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
import org.bukkit.event.entity.EntityPickupItemEvent;
import org.bukkit.event.player.PlayerAttemptPickupItemEvent;
import org.bukkit.event.inventory.InventoryOpenEvent;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryAction;
import org.bukkit.event.inventory.ClickType;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
Expand All @@ -32,9 +36,120 @@
import net.tfminecraft.cooking.utils.InventoryAdder;
import net.tfminecraft.cooking.utils.ItemBuilder;
import net.tfminecraft.cooking.utils.Keys;
import net.tfminecraft.cooking.utils.PlayerActionContext;

public class ConversionManager implements Listener {

@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onClick(InventoryClickEvent event) {
if (!(event.getWhoClicked() instanceof Player player)
|| PlayerActionContext.capture(player) == null) return;
Inventory top = event.getView().getTopInventory();
if (!stackingInventory(top)) return;
if (event.getSlotType() != InventoryType.SlotType.CONTAINER
&& event.getSlotType() != InventoryType.SlotType.QUICKBAR) return;
Inventory clicked = event.getClickedInventory();
if (clicked == null) return;

if (event.getAction() == InventoryAction.MOVE_TO_OTHER_INVENTORY && event.isShiftClick()) {
shiftFood(event, top, clicked);
return;
}
if (event.getClick() != ClickType.LEFT && event.getClick() != ClickType.RIGHT) return;
ItemStack cursor = event.getCursor();
ItemStack current = event.getCurrentItem();
if (!stackableFood(cursor, current)) return;

// Cancel even when full: differing clocks must not turn a stack attempt into a swap.
event.setCancelled(true);
int room = Math.min(current.getMaxStackSize(), clicked.getMaxStackSize()) - current.getAmount();
int amount = Math.min(room, event.isRightClick() ? 1 : cursor.getAmount());
if (amount <= 0) return;
event.setCurrentItem(mergedFood(current, cursor, amount));
event.getView().setCursor(remainder(cursor, cursor.getAmount() - amount));
}

private static boolean stackingInventory(Inventory inventory) {
// Only unrestricted storage slots; machines, equipment and plugin menus own their rules.
boolean storage = switch (inventory.getType()) {
case CRAFTING, CHEST, BARREL, ENDER_CHEST, SHULKER_BOX -> true;
default -> false;
};
InventoryHolder holder = inventory.getHolder();
return storage && (holder == null || holder instanceof org.bukkit.block.BlockState
|| holder instanceof org.bukkit.block.DoubleChest || holder instanceof org.bukkit.entity.Entity);
}

private static void shiftFood(InventoryClickEvent event, Inventory top, Inventory source) {
ItemStack item = event.getCurrentItem();
if (FoodItem.fromItem(item) == null) return;
Inventory target;
List<Integer> slots = new ArrayList<>();
if (top.getType() == InventoryType.CRAFTING) {
if (source == top || event.getSlot() >= 36) return;
target = source;
int start = event.getSlot() < 9 ? 9 : 0;
int end = event.getSlot() < 9 ? 36 : 9;
for (int i = start; i < end; i++) slots.add(i);
} else if (source == top) {
target = event.getView().getBottomInventory();
// Vanilla container quick-move visits the hotbar, then storage, in reverse order.
for (int i = 8; i >= 0; i--) slots.add(i);
for (int i = 35; i >= 9; i--) slots.add(i);
} else {
target = top;
for (int i = 0; i < top.getSize(); i++) slots.add(i);
}

int remaining = item.getAmount();
for (int slot : slots) {
ItemStack current = target.getItem(slot);
if (!stackableFood(item, current)) continue;
int room = Math.min(current.getMaxStackSize(), target.getMaxStackSize()) - current.getAmount();
int amount = Math.min(room, remaining);
if (amount <= 0) continue;
event.setCancelled(true);
target.setItem(slot, mergedFood(current, item, amount));
remaining -= amount;
if (remaining == 0) break;
}
if (!event.isCancelled()) return;

// Once a merge is handled here, finish the same move into empty slots without losing excess.
for (int slot : slots) {
if (remaining == 0) break;
ItemStack current = target.getItem(slot);
if (current != null && !current.isEmpty()) continue;
int amount = Math.min(remaining, Math.min(item.getMaxStackSize(), target.getMaxStackSize()));
target.setItem(slot, remainder(item, amount));
remaining -= amount;
}
event.setCurrentItem(remainder(item, remaining));
}

private static boolean stackableFood(ItemStack a, ItemStack b) {
if (a == null || b == null || a.isEmpty() || b.isEmpty() || a.getType() != b.getType()) return false;
FoodItem foodA = FoodItem.fromItem(a);
FoodItem foodB = FoodItem.fromItem(b);
if (foodA == null || foodB == null) return false;
foodA.updateAge();
foodB.updateAge();
return InventoryAdder.equalsFood(foodA, foodB) && sameApartFromAging(a, b);
}

private static ItemStack mergedFood(ItemStack current, ItemStack incoming, int amount) {
ItemStack merged = BundleFoodStacker.moreAged(current, incoming).clone();
merged.setAmount(current.getAmount() + amount);
return merged;
}

private static ItemStack remainder(ItemStack item, int amount) {
if (amount == 0) return null;
ItemStack rest = item.clone();
rest.setAmount(amount);
return rest;
}

@EventHandler(ignoreCancelled = true)
public void pickup(EntityPickupItemEvent e) {
ItemStack item = e.getItem().getItemStack();
Expand Down
Loading
Loading