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
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package net.swofty.type.skyblockgeneric.event.actions.player.gui;

import net.minestom.server.event.inventory.InventoryCloseEvent;
import net.minestom.server.inventory.PlayerInventory;
import net.swofty.type.generic.event.EventNodes;
import net.swofty.type.generic.event.HypixelEvent;
import net.swofty.type.generic.event.HypixelEventClass;
import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer;
import net.swofty.type.skyblockgeneric.utility.PlayerInventoryCrafting;

public class ActionPlayerCloseVanillaCrafting implements HypixelEventClass {
@HypixelEvent(node = EventNodes.PLAYER, requireDataLoaded = true)
public void run(InventoryCloseEvent event) {
if (!(event.getInventory() instanceof PlayerInventory)) {
return;
}

PlayerInventoryCrafting.returnCraftingGrid((SkyBlockPlayer) event.getPlayer());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,59 @@

import net.minestom.server.event.inventory.InventoryPreClickEvent;
import net.minestom.server.inventory.PlayerInventory;
import net.minestom.server.item.ItemStack;
import net.minestom.server.inventory.click.Click;
import net.swofty.type.generic.event.EventNodes;
import net.swofty.type.generic.event.HypixelEvent;
import net.swofty.type.generic.event.HypixelEventClass;
import net.swofty.type.skyblockgeneric.gui.inventories.sbmenu.GUICrafting;
import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer;
import net.swofty.type.skyblockgeneric.utility.PlayerInventoryCrafting;

import java.util.List;

public class ActionPlayerInteractWithCrafting implements HypixelEventClass {
@HypixelEvent(node = EventNodes.PLAYER, requireDataLoaded = true)
public void run(InventoryPreClickEvent event) {
SkyBlockPlayer player = (SkyBlockPlayer) event.getPlayer();

if (!(event.getInventory() instanceof PlayerInventory)) return;
if (event.getSlot() < 37 || event.getSlot() > 40) return;

event.setCancelled(true);
player.addAndUpdateItem(player.getInventory().getCursorItem());
player.getInventory().setCursorItem(ItemStack.AIR);
player.getInventory().update();
if (event.getClick() instanceof Click.LeftDrag(List<Integer> slots)) {
handleDrag(event, player, slots);
return;
}

if (event.getClick() instanceof Click.RightDrag(List<Integer> slots)) {
handleDrag(event, player, slots);
return;
}

if (PlayerInventoryCrafting.isResultSlot(event.getSlot())) {
event.setCancelled(true);
PlayerInventoryCrafting.craft(player, event.getClick());
return;
}

if (PlayerInventoryCrafting.isCraftingSlot(event.getSlot())
|| event.getClick() instanceof Click.LeftShift
|| event.getClick() instanceof Click.RightShift) {
PlayerInventoryCrafting.refreshNextTick(player);
}
}

private void handleDrag(InventoryPreClickEvent event, SkyBlockPlayer player, Iterable<Integer> slots) {
boolean refresh = false;
for (int slot : slots) {
if (PlayerInventoryCrafting.isResultSlot(slot)) {
event.setCancelled(true);
return;
}
if (PlayerInventoryCrafting.isCraftingSlot(slot)) {
refresh = true;
}
}

player.openView(new GUICrafting());
if (refresh) {
PlayerInventoryCrafting.refreshNextTick(player);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,291 @@
package net.swofty.type.skyblockgeneric.utility;

import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import net.kyori.adventure.text.format.TextDecoration;
import net.minestom.server.MinecraftServer;
import net.minestom.server.component.DataComponents;
import net.minestom.server.inventory.PlayerInventory;
import net.minestom.server.inventory.click.Click;
import net.minestom.server.item.ItemStack;
import net.minestom.server.item.Material;
import net.swofty.commons.StringUtility;
import net.swofty.commons.skyblock.item.ItemType;
import net.swofty.type.generic.event.HypixelEventHandler;
import net.swofty.type.generic.gui.inventory.ItemStackCreator;
import net.swofty.type.skyblockgeneric.event.custom.ItemCraftEvent;
import net.swofty.type.skyblockgeneric.item.SkyBlockItem;
import net.swofty.type.skyblockgeneric.item.crafting.SkyBlockRecipe;
import net.swofty.type.skyblockgeneric.item.updater.PlayerItemUpdater;
import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.stream.Collectors;

@AllArgsConstructor(access = AccessLevel.PRIVATE)
public final class PlayerInventoryCrafting {
private static final String[] DEFAULT_CRAFT_ERROR = new String[]{"§cYou cannot craft this item right now."};
private static final int RESULT_SLOT = 36;
private static final int[] CRAFT_SLOTS = new int[]{37, 38, 39, 40};
private static final int[] RECIPE_GRID_INDEXES = new int[]{0, 1, 3, 4};
private static final int RECIPE_GRID_SIZE = 9;

public static boolean isCraftingSlot(int slot) {
return Arrays.stream(CRAFT_SLOTS).anyMatch(craftSlot -> craftSlot == slot);
}

public static boolean isResultSlot(int slot) {
return slot == RESULT_SLOT;
}

public static void refreshNextTick(SkyBlockPlayer player) {
MinecraftServer.getSchedulerManager().scheduleNextTick(() -> refresh(player));
}

public static void refresh(SkyBlockPlayer player) {
PlayerInventory inventory = player.getInventory();
SkyBlockRecipe<?> recipe = parseRecipe(inventory);
inventory.setItemStack(RESULT_SLOT, renderResult(player, recipe));
inventory.update();
}

public static void returnCraftingGrid(SkyBlockPlayer player) {
PlayerInventory inventory = player.getInventory();
ItemStack[] itemsToReturn = Arrays.stream(CRAFT_SLOTS)
.mapToObj(inventory::getItemStack)
.filter(item -> !item.isAir())
.toArray(ItemStack[]::new);

inventory.setItemStack(RESULT_SLOT, ItemStack.AIR);
for (int slot : CRAFT_SLOTS) {
inventory.setItemStack(slot, ItemStack.AIR);
}

for (ItemStack item : itemsToReturn) {
returnToMainInventory(player, item);
}

inventory.update();
}

public static void craft(SkyBlockPlayer player, Click click) {
PlayerInventory inventory = player.getInventory();
SkyBlockRecipe<?> recipe = parseRecipe(inventory);
if (recipe == null) {
refresh(player);
return;
}

SkyBlockRecipe.CraftingResult permissionResult = recipe.getCanCraft().apply(player);
if (permissionResult == null || !permissionResult.allowed()) {
refresh(player);
return;
}

int amount = recipe.getAmount();
ItemStack craftedItem = PlayerItemUpdater.playerUpdate(player, recipe.getResult().getItemStack())
.amount(amount)
.build();

if (click instanceof Click.LeftShift || click instanceof Click.RightShift) {
craftToInventory(player, recipe, craftedItem, amount);
} else {
craftToCursor(player, recipe, craftedItem, amount);
}

refresh(player);
player.getInventory().update();
}

private static ItemStack renderResult(SkyBlockPlayer player, SkyBlockRecipe<?> recipe) {
if (recipe == null) {
return ItemStack.AIR;
}

SkyBlockRecipe.CraftingResult result = recipe.getCanCraft().apply(player);
if (result == null || !result.allowed()) {
String[] craftErrorMessages = getCraftErrorMessages(result);
return ItemStackCreator.getStack(
craftErrorMessages[0],
Material.BEDROCK,
1,
Arrays.copyOfRange(craftErrorMessages, 1, craftErrorMessages.length)
).build();
}

ItemStack.Builder builder = PlayerItemUpdater.playerUpdate(player, recipe.getResult().getItemStack())
.amount(recipe.getAmount());

ArrayList<Object> lore = new ArrayList<>();
var existingLore = builder.build().get(DataComponents.LORE);
if (existingLore != null) {
existingLore.stream().map(line -> "§7" + StringUtility.getTextFromComponent(line)).forEach(lore::add);
}
builder.set(DataComponents.LORE, ItemStackCreator.literalLoreComponents(lore).stream()
.map(line -> line.decoration(TextDecoration.ITALIC, false))
.collect(Collectors.toList()));

return builder.build();
}

private static void craftToInventory(SkyBlockPlayer player, SkyBlockRecipe<?> recipe, ItemStack craftedItem, int amountPerCraft) {
int maxCraftsByInventory = getMaxCraftsByInventory(player, craftedItem, amountPerCraft);
SkyBlockItem[] simulatedItems = getCurrentRecipeAsItems(player.getInventory());
int craftedCount = 0;

while (craftedCount < maxCraftsByInventory) {
SkyBlockRecipe.CraftingResult craftResult = recipe.getCanCraft().apply(player);
if (craftResult == null || !craftResult.allowed()) {
break;
}

try {
simulatedItems = recipe.consume(simulatedItems);
} catch (Exception e) {
break;
}

craftedCount++;
}

int totalCraftedAmount = craftedCount * amountPerCraft;
if (totalCraftedAmount <= 0) {
return;
}

applyConsumedGrid(player, simulatedItems);
player.addAndUpdateItem(craftedItem.withAmount(totalCraftedAmount));
HypixelEventHandler.callCustomEvent(
new ItemCraftEvent(player, new SkyBlockItem(craftedItem.withAmount(totalCraftedAmount)), recipe)
);
}

private static void craftToCursor(SkyBlockPlayer player, SkyBlockRecipe<?> recipe, ItemStack craftedItem, int amount) {
PlayerInventory inventory = player.getInventory();
ItemStack cursorItemStack = inventory.getCursorItem();
SkyBlockItem cursorItem = new SkyBlockItem(cursorItemStack);
ItemType cursorItemType = cursorItem.getAttributeHandler().getPotentialType();
ItemType resultItemType = recipe.getResult().getAttributeHandler().getPotentialType();

if (!cursorItemStack.isAir() && (cursorItemType == null || !cursorItemType.equals(resultItemType))) {
return;
}

int currentCursorAmount = cursorItemStack.isAir() ? 0 : cursorItemStack.amount();
int newAmount = currentCursorAmount + amount;
if (newAmount > craftedItem.material().maxStackSize()) {
return;
}

SkyBlockItem[] toReplace;
try {
toReplace = recipe.consume(getCurrentRecipeAsItems(inventory));
} catch (Exception e) {
return;
}

applyConsumedGrid(player, toReplace);
inventory.setCursorItem(craftedItem.withAmount(newAmount));
HypixelEventHandler.callCustomEvent(new ItemCraftEvent(player, new SkyBlockItem(craftedItem), recipe));
}

private static int getMaxCraftsByInventory(SkyBlockPlayer player, ItemStack craftedItem, int amountPerCraft) {
if (amountPerCraft <= 0) {
return 0;
}

int maxStackSize = craftedItem.material().maxStackSize();
int availableSpace = 0;
for (int slot = 0; slot < 36; slot++) {
ItemStack stack = player.getInventory().getItemStack(slot);
if (stack.isAir()) {
availableSpace += maxStackSize;
continue;
}
if (stack.isSimilar(craftedItem) && stack.amount() < maxStackSize) {
availableSpace += maxStackSize - stack.amount();
}
}

return availableSpace / amountPerCraft;
}

private static void returnToMainInventory(SkyBlockPlayer player, ItemStack item) {
ItemStack updatedItem = PlayerItemUpdater.playerUpdate(player, item, true).build();
int remaining = updatedItem.amount();
int maxStackSize = updatedItem.material().maxStackSize();
PlayerInventory inventory = player.getInventory();

for (int slot = 0; slot < 36 && remaining > 0; slot++) {
ItemStack stack = inventory.getItemStack(slot);
if (!stack.isSimilar(updatedItem) || stack.amount() >= maxStackSize) {
continue;
}

int moved = Math.min(remaining, maxStackSize - stack.amount());
inventory.setItemStack(slot, stack.withAmount(stack.amount() + moved));
remaining -= moved;
}

for (int slot = 0; slot < 36 && remaining > 0; slot++) {
ItemStack stack = inventory.getItemStack(slot);
if (!stack.isAir()) {
continue;
}

int moved = Math.min(remaining, maxStackSize);
inventory.setItemStack(slot, updatedItem.withAmount(moved));
remaining -= moved;
}

if (remaining > 0) {
player.addToStash(new SkyBlockItem(updatedItem.withAmount(remaining)));
}
}

private static SkyBlockRecipe<?> parseRecipe(PlayerInventory inventory) {
return SkyBlockRecipe.parseRecipe(getCurrentRecipeStacks(inventory));
}

private static ItemStack[] getCurrentRecipeStacks(PlayerInventory inventory) {
ItemStack[] stacks = new ItemStack[RECIPE_GRID_SIZE];
Arrays.fill(stacks, ItemStack.AIR);

for (int i = 0; i < CRAFT_SLOTS.length; i++) {
stacks[RECIPE_GRID_INDEXES[i]] = inventory.getItemStack(CRAFT_SLOTS[i]);
}
return stacks;
}

private static SkyBlockItem[] getCurrentRecipeAsItems(PlayerInventory inventory) {
ItemStack[] rawStacks = getCurrentRecipeStacks(inventory);
SkyBlockItem[] stacks = new SkyBlockItem[RECIPE_GRID_SIZE];
for (int i = 0; i < rawStacks.length; i++) {
stacks[i] = new SkyBlockItem(rawStacks[i]);
}
return stacks;
}

private static void applyConsumedGrid(SkyBlockPlayer player, SkyBlockItem[] toReplace) {
PlayerInventory inventory = player.getInventory();
for (int i = 0; i < CRAFT_SLOTS.length; i++) {
SkyBlockItem replacement = toReplace[RECIPE_GRID_INDEXES[i]];
if (replacement == null || replacement.getItemStack().material() == Material.BEDROCK) {
inventory.setItemStack(CRAFT_SLOTS[i], ItemStack.AIR);
} else {
inventory.setItemStack(
CRAFT_SLOTS[i],
PlayerItemUpdater.playerUpdate(player, replacement.getItemStack()).build()
);
}
}
}

private static String[] getCraftErrorMessages(SkyBlockRecipe.CraftingResult result) {
if (result == null || result.errorMessage() == null || result.errorMessage().length == 0) {
return DEFAULT_CRAFT_ERROR;
}
return result.errorMessage();
}
}
Loading