From 2b29f3242f4bf8f04c50808441bacf72ab1f7504 Mon Sep 17 00:00:00 2001 From: TexTrue <65154269+TexBlock@users.noreply.github.com> Date: Fri, 8 Nov 2024 22:31:14 +0800 Subject: [PATCH] sync with `sakura-ryoko/itemscroller` 1.21-0.24.51 --- CHANGELOG.md | 2 +- gradle/libs.versions.toml | 6 +- gradle/wrapper/gradle-wrapper.properties | 2 +- .../fi/dy/masa/itemscroller/Reference.java | 6 + .../compat/modmenu/ModMenuImpl.java | 18 + .../dy/masa/itemscroller/config/Configs.java | 33 +- .../itemscroller/event/KeybindCallbacks.java | 23 +- .../itemscroller/gui/ItemScrollerIcons.java | 4 +- .../MixinClientPlayerInteractionManager.java | 3 - .../itemscroller/mixin/MixinItemStack.java | 9 +- .../itemscroller/recipes/RecipePattern.java | 1 + .../itemscroller/recipes/RecipeStorage.java | 23 +- .../itemscroller/util/InventoryUtils.java | 371 ++++++++++++++++-- .../itemscroller/util/SortingCategory.java | 172 ++++++++ .../masa/itemscroller/util/SortingMethod.java | 84 ++++ .../rocknroller/RocknRoller.java | 11 +- .../assets/rocknroller/lang/en_us.json | 46 ++- .../assets/rocknroller/lang/es_es.json | 40 ++ .../assets/rocknroller/lang/zh_cn.json | 42 +- .../assets/rocknroller/lang/zh_tw.json | 40 ++ 20 files changed, 874 insertions(+), 62 deletions(-) create mode 100644 src/main/java/fi/dy/masa/itemscroller/compat/modmenu/ModMenuImpl.java create mode 100644 src/main/java/fi/dy/masa/itemscroller/util/SortingCategory.java create mode 100644 src/main/java/fi/dy/masa/itemscroller/util/SortingMethod.java diff --git a/CHANGELOG.md b/CHANGELOG.md index fa6a3b8..c2c98a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,2 +1,2 @@ ## Change -- sync with `sakura-ryoko/itemscroller` 1.21-sakura.8 \ No newline at end of file +- sync with `sakura-ryoko/itemscroller` 1.21-0.24.51 \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6606f6f..43d8952 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -6,7 +6,7 @@ mappings_patch="1.21+build.4" neoforge="21.1.31" # Mod properties -version="0.1.8" +version="0.1.9" maven-group="org.thinkingstudio.rocknroller" archives-name="RocknRoller" @@ -15,14 +15,14 @@ id-modrinth="hYq29QmW" id-curseforge="916852" # Mod dependencies -mafglib="0.1.22-mc1.21.1" +mafglib="0.1.24-mc1.21.1" badpackets="neo-0.8.1" # Libraries jsr305="3.0.2" # Gradle plugin -architectury-loom = "1.6-SNAPSHOT" +architectury-loom = "1.7-SNAPSHOT" modpublisher = "2.+" [libraries] diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 48c0a02..19cfad9 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/main/java/fi/dy/masa/itemscroller/Reference.java b/src/main/java/fi/dy/masa/itemscroller/Reference.java index e26b8de..2f7eb2b 100644 --- a/src/main/java/fi/dy/masa/itemscroller/Reference.java +++ b/src/main/java/fi/dy/masa/itemscroller/Reference.java @@ -1,10 +1,16 @@ package fi.dy.masa.itemscroller; +import net.minecraft.MinecraftVersion; + import fi.dy.masa.malilib.util.StringUtils; public class Reference { + public static final String ID = "itemscroller"; public static final String MOD_ID = "rocknroller"; public static final String MOD_NAME = "Rock'n Roller"; public static final String MOD_VERSION = StringUtils.getModVersionString(MOD_ID); + public static final String MC_VERSION = MinecraftVersion.CURRENT.getName(); + public static final String MOD_TYPE = "neoforge"; + public static final String MOD_STRING = MOD_ID + "-" + MOD_TYPE + "-" + MC_VERSION + "-" + MOD_VERSION; } diff --git a/src/main/java/fi/dy/masa/itemscroller/compat/modmenu/ModMenuImpl.java b/src/main/java/fi/dy/masa/itemscroller/compat/modmenu/ModMenuImpl.java new file mode 100644 index 0000000..167f900 --- /dev/null +++ b/src/main/java/fi/dy/masa/itemscroller/compat/modmenu/ModMenuImpl.java @@ -0,0 +1,18 @@ +package fi.dy.masa.itemscroller.compat.modmenu; + +import fi.dy.masa.itemscroller.gui.GuiConfigs; +import org.thinkingstudio.mafglib.loader.gui.ModConfigScreenFactory; +import org.thinkingstudio.mafglib.loader.gui.ModConfigScreenInitializer; + +public class ModMenuImpl implements ModConfigScreenInitializer +{ + @Override + public ModConfigScreenFactory getModConfigScreenFactory() + { + return (modContainer, screen) -> { + GuiConfigs gui = new GuiConfigs(); + gui.setParent(screen); + return gui; + }; + } +} diff --git a/src/main/java/fi/dy/masa/itemscroller/config/Configs.java b/src/main/java/fi/dy/masa/itemscroller/config/Configs.java index 65b8784..ff291f4 100644 --- a/src/main/java/fi/dy/masa/itemscroller/config/Configs.java +++ b/src/main/java/fi/dy/masa/itemscroller/config/Configs.java @@ -11,6 +11,7 @@ import net.minecraft.client.gui.screen.ingame.InventoryScreen; import net.minecraft.screen.slot.CraftingResultSlot; import fi.dy.masa.malilib.config.ConfigUtils; +import fi.dy.masa.malilib.config.IConfigBase; import fi.dy.masa.malilib.config.IConfigHandler; import fi.dy.masa.malilib.config.IConfigValue; import fi.dy.masa.malilib.config.options.*; @@ -19,17 +20,22 @@ import fi.dy.masa.itemscroller.Reference; import fi.dy.masa.itemscroller.recipes.CraftingHandler; import fi.dy.masa.itemscroller.recipes.CraftingHandler.SlotRange; +import fi.dy.masa.itemscroller.util.SortingCategory; +import fi.dy.masa.itemscroller.util.SortingMethod; public class Configs implements IConfigHandler { private static final String CONFIG_FILE_NAME = Reference.MOD_ID + ".json"; + private static final ImmutableList DEFAULT_TOP_SORTING = ImmutableList.of("minecraft:diamond_sword","minecraft:diamond_pickaxe","minecraft:diamond_axe","minecraft:diamond_shovel","minecraft:diamond_hoe","minecraft:netherite_sword","minecraft:netherite_pickaxe","minecraft:netherite_axe","minecraft:netherite_shovel","minecraft:netherite_hoe"); + private static final ImmutableList DEFAULT_BOTTOM_SORTING = ImmutableList.of(); + public static class Generic { public static final ConfigBoolean CARPET_CTRL_Q_CRAFTING = new ConfigBoolean("carpetCtrlQCraftingEnabledOnServer", false, "itemscroller.config.generic.comment.carpetCtrlQCraftingEnabledOnServer").translatedName("itemscroller.config.generic.name.carpetCtrlQCraftingEnabledOnServer"); public static final ConfigBoolean CLIENT_CRAFTING_FIX = new ConfigBoolean("clientCraftingFixOn1.12", true, "itemscroller.config.generic.comment.clientCraftingFixOn1_12").translatedName("itemscroller.config.generic.name.clientCraftingFixOn1_12"); public static final ConfigBoolean CRAFTING_RENDER_RECIPE_ITEMS = new ConfigBoolean("craftingRenderRecipeItems", true, "itemscroller.config.generic.comment.craftingRenderRecipeItems").translatedName("itemscroller.config.generic.name.craftingRenderRecipeItems"); - //public static final ConfigBoolean DEBUG_MESSAGES = new ConfigBoolean("debugMessages", false, "itemscroller.config.generic.comment.debugMessages").translatedName("itemscroller.config.generic.name.debugMessages"); + public static final ConfigBoolean DEBUG_MESSAGES = new ConfigBoolean("debugMessages", false, "itemscroller.config.generic.comment.debugMessages").translatedName("itemscroller.config.generic.name.debugMessages"); public static final ConfigBoolean MOD_MAIN_TOGGLE = new ConfigBoolean("modMainToggle", true, "itemscroller.config.generic.comment.modMainToggle").translatedName("itemscroller.config.generic.name.modMainToggle"); public static final ConfigBoolean MASS_CRAFT_INHIBIT_MID_UPDATES = new ConfigBoolean("massCraftInhibitMidUpdates", true, "itemscroller.config.generic.comment.massCraftInhibitMidUpdates").translatedName("itemscroller.config.generic.name.massCraftInhibitMidUpdates"); public static final ConfigInteger MASS_CRAFT_INTERVAL = new ConfigInteger("massCraftInterval", 2, 1, 60, "itemscroller.config.generic.comment.massCraftInterval").translatedName("itemscroller.config.generic.name.massCraftInterval"); @@ -46,14 +52,23 @@ public static class Generic public static final ConfigBoolean SLOT_POSITION_AWARE_SCROLL_DIRECTION = new ConfigBoolean("useSlotPositionAwareScrollDirection", false, "itemscroller.config.generic.comment.useSlotPositionAwareScrollDirection").translatedName("itemscroller.config.generic.name.useSlotPositionAwareScrollDirection"); public static final ConfigBoolean VILLAGER_TRADE_USE_GLOBAL_FAVORITES = new ConfigBoolean("villagerTradeUseGlobalFavorites", true, "itemscroller.config.generic.comment.villagerTradeUseGlobalFavorites").translatedName("itemscroller.config.generic.name.villagerTradeUseGlobalFavorites"); public static final ConfigBoolean VILLAGER_TRADE_LIST_REMEMBER_SCROLL = new ConfigBoolean("villagerTradeListRememberScrollPosition", true, "itemscroller.config.generic.comment.villagerTradeListRememberScrollPosition").translatedName("itemscroller.config.generic.name.villagerTradeListRememberScrollPosition"); + + public static final ConfigBoolean SORT_INVENTORY_TOGGLE = new ConfigBoolean("sortInventoryToggle", false, "itemscroller.config.generic.comment.sortInventoryToggle").translatedName("itemscroller.config.generic.name.sortInventoryToggle"); public static final ConfigBoolean SORT_ASSUME_EMPTY_BOX_STACKS = new ConfigBoolean("sortAssumeEmptyBoxStacks", true, "itemscroller.config.generic.comment.sortAssumeEmptyBoxStacks").translatedName("itemscroller.config.generic.name.sortAssumeEmptyBoxStacks"); public static final ConfigBoolean SORT_SHULKER_BOXES_AT_END = new ConfigBoolean("sortShulkerBoxesAtEnd", true, "itemscroller.config.generic.comment.sortShulkerBoxesAtEnd").translatedName("itemscroller.config.generic.name.sortShulkerBoxesAtEnd"); - - public static final ImmutableList OPTIONS = ImmutableList.of( + public static final ConfigBoolean SORT_SHULKER_BOXES_INVERTED = new ConfigBoolean("sortShulkerBoxesInverted", false, "itemscroller.config.generic.comment.sortShulkerBoxesInverted").translatedName("itemscroller.config.generic.name.sortShulkerBoxesInverted"); + public static final ConfigBoolean SORT_BUNDLES_AT_END = new ConfigBoolean("sortBundlesAtEnd", true, "itemscroller.config.generic.comment.sortBundlesAtEnd").translatedName("itemscroller.config.generic.name.sortBundlesAtEnd"); + public static final ConfigBoolean SORT_BUNDLES_INVERTED = new ConfigBoolean("sortBundlesInverted", false, "itemscroller.config.generic.comment.sortBundlesInverted").translatedName("itemscroller.config.generic.name.sortBundlesInverted"); + public static final ConfigStringList SORT_TOP_PRIORITY_INVENTORY = new ConfigStringList("sortTopPriorityInventory", DEFAULT_TOP_SORTING, "itemscroller.config.generic.comment.sortTopPriorityInventory").translatedName("itemscroller.config.generic.name.sortTopPriorityInventory"); + public static final ConfigStringList SORT_BOTTOM_PRIORITY_INVENTORY = new ConfigStringList("sortBottomPriorityInventory", DEFAULT_BOTTOM_SORTING, "itemscroller.config.generic.comment.sortBottomPriorityInventory").translatedName("itemscroller.config.generic.name.sortBottomPriorityInventory"); + public static final ConfigOptionList SORT_METHOD_DEFAULT = new ConfigOptionList("sortMethodDefault", SortingMethod.CATEGORY_NAME, "itemscroller.config.generic.comment.sortMethodDefault").translatedName("itemscroller.config.generic.name.sortMethodDefault"); + public static final ConfigLockedList SORT_CATEGORY_ORDER = new ConfigLockedList("sortCategoryOrder", SortingCategory.INSTANCE, "itemscroller.config.generic.comment.sortCategoryOrder").translatedName("itemscroller.config.generic.name.sortCategoryOrder"); + + public static final ImmutableList OPTIONS = ImmutableList.of( CARPET_CTRL_Q_CRAFTING, CLIENT_CRAFTING_FIX, CRAFTING_RENDER_RECIPE_ITEMS, - //DEBUG_MESSAGES, + DEBUG_MESSAGES, MASS_CRAFT_INHIBIT_MID_UPDATES, MASS_CRAFT_INTERVAL, MASS_CRAFT_ITERATIONS, @@ -71,8 +86,16 @@ public static class Generic VILLAGER_TRADE_USE_GLOBAL_FAVORITES, VILLAGER_TRADE_LIST_REMEMBER_SCROLL, + SORT_INVENTORY_TOGGLE, SORT_ASSUME_EMPTY_BOX_STACKS, - SORT_SHULKER_BOXES_AT_END + SORT_SHULKER_BOXES_AT_END, + SORT_SHULKER_BOXES_INVERTED, + SORT_BUNDLES_AT_END, + SORT_BUNDLES_INVERTED, + SORT_TOP_PRIORITY_INVENTORY, + SORT_BOTTOM_PRIORITY_INVENTORY, + SORT_METHOD_DEFAULT, + SORT_CATEGORY_ORDER ); } diff --git a/src/main/java/fi/dy/masa/itemscroller/event/KeybindCallbacks.java b/src/main/java/fi/dy/masa/itemscroller/event/KeybindCallbacks.java index 8358a97..ffa3385 100644 --- a/src/main/java/fi/dy/masa/itemscroller/event/KeybindCallbacks.java +++ b/src/main/java/fi/dy/masa/itemscroller/event/KeybindCallbacks.java @@ -172,8 +172,11 @@ else if (key == Hotkeys.SLOT_DEBUG.getKeybind()) } else if (key == Hotkeys.SORT_INVENTORY.getKeybind()) { - InventoryUtils.sortInventory(gui); - return true; + if (Configs.Generic.SORT_INVENTORY_TOGGLE.getBooleanValue()) + { + InventoryUtils.sortInventory(gui); + return true; + } } return false; @@ -239,9 +242,9 @@ public void onClientTick(MinecraftClient mc) for (int i = 0; i < limit; ++i) { // todo -// InventoryUtils.tryClearCursor(gui); -// InventoryUtils.setInhibitCraftingOutputUpdate(true); - InventoryUtils.throwAllCraftingResultsToGround(recipe, gui); + //InventoryUtils.setInhibitCraftingOutputUpdate(true); + //InventoryUtils.tryClearCursor(gui); + //InventoryUtils.throwAllCraftingResultsToGround(recipe, gui); RecipeInputInventory craftingInv = ((IMixinCraftingResultSlot) outputSlot).itemscroller_getCraftingInventory(); if (!recipe.getVanillaRecipe().matches(craftingInv.createRecipeInput(), mc.world)) @@ -274,8 +277,18 @@ public void onClientTick(MinecraftClient mc) } InventoryUtils.shiftClickSlot(gui, outputSlot.id); + + // This isn't required after 1.21, it only needs a single dropStack + for (int k = 0; k < recipe.getResult().getMaxCount(); k++) + { + InventoryUtils.dropStack(gui, outputSlot.id); + } + recipeBookClicks = true; } + + InventoryUtils.tryClearCursor(gui); + InventoryUtils.throwAllCraftingResultsToGround(recipe, gui); } else if (Configs.Generic.MASS_CRAFT_SWAPS.getBooleanValue()) { diff --git a/src/main/java/fi/dy/masa/itemscroller/gui/ItemScrollerIcons.java b/src/main/java/fi/dy/masa/itemscroller/gui/ItemScrollerIcons.java index 0061599..c3b1158 100644 --- a/src/main/java/fi/dy/masa/itemscroller/gui/ItemScrollerIcons.java +++ b/src/main/java/fi/dy/masa/itemscroller/gui/ItemScrollerIcons.java @@ -1,9 +1,9 @@ package fi.dy.masa.itemscroller.gui; import net.minecraft.util.Identifier; -import fi.dy.masa.itemscroller.Reference; import fi.dy.masa.malilib.gui.interfaces.IGuiIcon; import fi.dy.masa.malilib.render.RenderUtils; +import fi.dy.masa.itemscroller.Reference; public enum ItemScrollerIcons implements IGuiIcon { @@ -13,7 +13,7 @@ public enum ItemScrollerIcons implements IGuiIcon STAR_5_YELLOW (112, 18, 5, 5), STAR_5_PURPLE (117, 18, 5, 5); - public static final Identifier TEXTURE = Identifier.splitOn(Reference.MOD_ID + ":textures/gui/gui_widgets.png", ':'); + public static final Identifier TEXTURE = Identifier.of(Reference.MOD_ID, "textures/gui/gui_widgets.png"); private final int u; private final int v; diff --git a/src/main/java/fi/dy/masa/itemscroller/mixin/MixinClientPlayerInteractionManager.java b/src/main/java/fi/dy/masa/itemscroller/mixin/MixinClientPlayerInteractionManager.java index 2328a64..1685110 100644 --- a/src/main/java/fi/dy/masa/itemscroller/mixin/MixinClientPlayerInteractionManager.java +++ b/src/main/java/fi/dy/masa/itemscroller/mixin/MixinClientPlayerInteractionManager.java @@ -1,8 +1,5 @@ package fi.dy.masa.itemscroller.mixin; -import net.minecraft.client.MinecraftClient; -import net.minecraft.network.packet.c2s.play.ClickSlotC2SPacket; -import net.minecraft.screen.ScreenHandler; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; diff --git a/src/main/java/fi/dy/masa/itemscroller/mixin/MixinItemStack.java b/src/main/java/fi/dy/masa/itemscroller/mixin/MixinItemStack.java index fca0f3c..e03816a 100644 --- a/src/main/java/fi/dy/masa/itemscroller/mixin/MixinItemStack.java +++ b/src/main/java/fi/dy/masa/itemscroller/mixin/MixinItemStack.java @@ -17,7 +17,9 @@ public class MixinItemStack private void dontCap(int maxCount, CallbackInfo ci) { // Client-side fx for empty shulker box stacking - if (MinecraftClient.getInstance().isOnThread() && Configs.Generic.SORT_ASSUME_EMPTY_BOX_STACKS.getBooleanValue()) + if (MinecraftClient.getInstance().isOnThread() && + Configs.Generic.SORT_INVENTORY_TOGGLE.getBooleanValue() && + Configs.Generic.SORT_ASSUME_EMPTY_BOX_STACKS.getBooleanValue()) { ci.cancel(); } @@ -27,7 +29,10 @@ private void dontCap(int maxCount, CallbackInfo ci) private void getMaxCount(CallbackInfoReturnable cir) { // Client-side fx for empty shulker box stacking - if (MinecraftClient.getInstance().isOnThread() && Configs.Generic.SORT_ASSUME_EMPTY_BOX_STACKS.getBooleanValue() && InventoryUtils.assumeEmptyShulkerStacking) + if (MinecraftClient.getInstance().isOnThread() && + Configs.Generic.SORT_INVENTORY_TOGGLE.getBooleanValue() && + Configs.Generic.SORT_ASSUME_EMPTY_BOX_STACKS.getBooleanValue() && + InventoryUtils.assumeEmptyShulkerStacking) { cir.setReturnValue(InventoryUtils.stackMaxSize((ItemStack) (Object) this, true)); } diff --git a/src/main/java/fi/dy/masa/itemscroller/recipes/RecipePattern.java b/src/main/java/fi/dy/masa/itemscroller/recipes/RecipePattern.java index 085cea3..ec862fc 100644 --- a/src/main/java/fi/dy/masa/itemscroller/recipes/RecipePattern.java +++ b/src/main/java/fi/dy/masa/itemscroller/recipes/RecipePattern.java @@ -55,6 +55,7 @@ public void ensureRecipeSizeAndClearRecipe(int size) this.clearRecipe(); } + @SuppressWarnings("unchecked") @Nullable public Recipe lookupVanillaRecipe(World world) { //Assume all recipes here are of type CraftingRecipe diff --git a/src/main/java/fi/dy/masa/itemscroller/recipes/RecipeStorage.java b/src/main/java/fi/dy/masa/itemscroller/recipes/RecipeStorage.java index 9a16101..e846f84 100644 --- a/src/main/java/fi/dy/masa/itemscroller/recipes/RecipeStorage.java +++ b/src/main/java/fi/dy/masa/itemscroller/recipes/RecipeStorage.java @@ -165,13 +165,21 @@ private void readFromNBT(NbtCompound nbt, @Nonnull DynamicRegistryManager regist if (index >= 0 && index < this.recipes.length) { this.recipes[index].readFromNBT(tag, registryManager); + + // TODO 1.21.2+ + /* + if (tag.contains("LastNetworkId")) + { + this.recipes[index].storeNetworkRecipeId(new NetworkRecipeId(tag.getInt("LastNetworkId"))); + } + */ } } this.changeSelectedRecipe(nbt.getByte("Selected")); } - private NbtCompound writeToNBT(DynamicRegistryManager registryManager) + private NbtCompound writeToNBT(@Nonnull DynamicRegistryManager registryManager) { NbtList tagRecipes = new NbtList(); NbtCompound nbt = new NbtCompound(); @@ -180,10 +188,17 @@ private NbtCompound writeToNBT(DynamicRegistryManager registryManager) { if (this.recipes[i].isValid()) { - - NbtCompound tag = this.recipes[i].writeToNBT(registryManager); + RecipePattern entry = this.recipes[i]; + NbtCompound tag = entry.writeToNBT(registryManager); tag.putByte("RecipeIndex", (byte) i); - tagRecipes.add(tag); + + // TODO 1.21.2+ + /* + if (entry.getNetworkRecipeId() != null) + { + tag.putInt("LastNetworkId", entry.getNetworkRecipeId().index()); + } + */ } } diff --git a/src/main/java/fi/dy/masa/itemscroller/util/InventoryUtils.java b/src/main/java/fi/dy/masa/itemscroller/util/InventoryUtils.java index 2aa6854..b485791 100644 --- a/src/main/java/fi/dy/masa/itemscroller/util/InventoryUtils.java +++ b/src/main/java/fi/dy/masa/itemscroller/util/InventoryUtils.java @@ -9,24 +9,25 @@ import it.unimi.dsi.fastutil.ints.IntComparator; import it.unimi.dsi.fastutil.ints.IntIntMutablePair; +import org.apache.commons.lang3.math.Fraction; + import net.minecraft.block.ShulkerBoxBlock; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen; -import net.minecraft.client.gui.screen.ingame.HandledScreen; -import net.minecraft.client.gui.screen.ingame.InventoryScreen; -import net.minecraft.client.gui.screen.ingame.MerchantScreen; +import net.minecraft.client.gui.screen.ingame.*; import net.minecraft.client.network.ClientPlayerEntity; import net.minecraft.client.world.ClientWorld; +import net.minecraft.component.ComponentChanges; +import net.minecraft.component.ComponentMap; import net.minecraft.component.DataComponentTypes; +import net.minecraft.component.type.BundleContentsComponent; import net.minecraft.component.type.ContainerComponent; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.inventory.CraftingResultInventory; import net.minecraft.inventory.Inventory; import net.minecraft.inventory.RecipeInputInventory; -import net.minecraft.item.BlockItem; -import net.minecraft.item.ItemStack; +import net.minecraft.item.*; import net.minecraft.network.listener.ClientPlayPacketListener; import net.minecraft.network.packet.Packet; import net.minecraft.network.packet.c2s.play.ClientStatusC2SPacket; @@ -43,6 +44,7 @@ import net.minecraft.screen.slot.SlotActionType; import net.minecraft.screen.slot.TradeOutputSlot; import net.minecraft.util.Identifier; +import net.minecraft.util.collection.DefaultedList; import net.minecraft.village.TradeOffer; import net.minecraft.village.TradeOfferList; import net.minecraft.world.GameRules; @@ -77,8 +79,14 @@ public class InventoryUtils private static boolean inhibitCraftResultUpdate; private static Runnable selectedSlotUpdateTask; public static boolean assumeEmptyShulkerStacking = false; + private static List topSortingPriorityList = Configs.Generic.SORT_TOP_PRIORITY_INVENTORY.getStrings(); + private static List bottomSortingPriorityList = Configs.Generic.SORT_BOTTOM_PRIORITY_INVENTORY.getStrings(); public static boolean bufferInvUpdates = false; public static List> invUpdatesBuffer = new ArrayList<>(); + private static ItemGroup.DisplayContext displayContext; + + private static Pair lastSwapTry = Pair.of(-1, -1); + private static List> hotbarSwaps = new ArrayList<>(); public static void setInhibitCraftingOutputUpdate(boolean inhibitUpdate) { @@ -1705,6 +1713,7 @@ public static void setCraftingGridContentsUsingSwaps(HandledScreen= 0) { Slot ingredientSlot = gui.getScreenHandler().getSlot(index); + if (ingredientSlot.inventory instanceof PlayerInventory && ingredientSlot.getIndex() < 9) { // hotbar @@ -2621,19 +2630,61 @@ public static void sortInventory(HandledScreen gui) { Pair range = new IntIntMutablePair(Integer.MAX_VALUE, 0); Slot focusedSlot = AccessorUtils.getSlotUnderMouse(gui); - if (focusedSlot == null) { + boolean shulkerBoxFix; + lastSwapTry = Pair.of(-1, -1); + hotbarSwaps.clear(); + + if (focusedSlot == null || focusedSlot.hasStack() == false) + { return; } + //System.out.printf("sort - focusedSlot[%d]: %s\n", focusedSlot.id, focusedSlot.hasStack() ? focusedSlot.getStack().getName().getString() : ""); ScreenHandler container = gui.getScreenHandler(); + int limit = container.slots.size(); + if (gui instanceof CreativeInventoryScreen creative && !creative.isInventoryTabSelected()) { return; } + if (gui instanceof InventoryScreen && (focusedSlot.id < 9 || focusedSlot.id > 44)) + { + return; + } + if (gui instanceof ShulkerBoxScreen) + { + //System.out.printf("sort - ShulkerBoxScreen %s\n", true); + + // Free Hotbar slots if sorting inside the Shulker Box + // Might cause an endless loop if hotbar is full. + if (focusedSlot.id < 27) + { + /* + if (tryFreeHotbarForShulkerSwaps(gui, container) == false) + { + ItemScroller.logger.warn("sortInventory: Free Hotbar slots are required in order to complete a Shulker box sorting task."); + return; + } + */ + + // Don't sort Shulkers into a shulker. + shulkerBoxFix = true; + } + else + { + shulkerBoxFix = false; + } + } + else + { + shulkerBoxFix = false; + } int focusedIndex = -1; - for (int i = 0; i < container.slots.size(); i++) + for (int i = 0; i < limit; i++) { Slot slot = container.slots.get(i); + + //System.out.printf("sort - slot[%d]: %s\n", i, slot.hasStack() ? slot.getStack().getName().getString() : ""); if (slot == focusedSlot) { focusedIndex = i; @@ -2682,8 +2733,8 @@ else if (range.right() - range.left() == 36) } } - //System.out.printf("Sorting [%d, %d)\n", range.first(), range.second()); - //ItemScroller.printDebug("Sorting [{}, {}]", range.first(), range.second()); + //System.out.printf("Sorting [%d, %d] (first, second)\n", range.first(), range.second()); + //System.out.printf("Sorting [%d, %d] (left, right)\n", range.left(), range.right()); tryClearCursor(gui); tryMergeItems(gui, range.left(), range.right() - 1); @@ -2691,40 +2742,126 @@ else if (range.right() - range.left() == 36) { ClientStatusC2SPacket packet = new ClientStatusC2SPacket(ClientStatusC2SPacket.Mode.REQUEST_STATS); MinecraftClient.getInstance().getNetworkHandler().send(packet); - selectedSlotUpdateTask = () -> quickSort(gui, range.first(), range.second() - 1); + selectedSlotUpdateTask = () -> quickSort(gui, range.first(), range.second() - 1, shulkerBoxFix); } else { - quickSort(gui, range.first(), range.second() - 1); + quickSort(gui, range.first(), range.second() - 1, shulkerBoxFix); + } + + if (hotbarSwaps.isEmpty() == false) + { + restoreHotbarForShulkerSwaps(gui, container); + } + } + + /** + * Free Hotbar slots for sorting operation. Restore to Original locations when done using restoreHotbarForShulkerSwaps() + * + * @param gui + * @param container + */ + private static boolean tryFreeHotbarForShulkerSwaps(HandledScreen gui, ScreenHandler container) + { + final int lastSlot = container.slots.size() - 1; + final int firstSlot = lastSlot - 9; + final int freeTarget = 2; + int emptySlot = -1; + int freeCount = 0; + + //System.out.printf("tryFreeHotbarForShulkerSwaps first %d, last %d, target %d\n", firstSlot, lastSlot, freeTarget); + + for (int i = lastSlot; i > firstSlot; i--) + { + Slot slot = container.slots.get(i); + + if (slot.hasStack() == false && freeCount < freeTarget) + { + freeCount++; + } + else if (freeCount > freeTarget) + { + break; + } + else if (slot.hasStack()) + { + emptySlot = fi.dy.masa.malilib.util.InventoryUtils.findEmptySlotInPlayerInventory(container, false, true); + + if (emptySlot >= 0) + { + //System.out.printf("tryFreeHotbarForShulkerSwaps SWAP [%d -> %d]\n", i, emptySlot); + + hotbarSwaps.add(Pair.of(i, emptySlot)); + swapSlots(gui, i, emptySlot); + emptySlot = -1; + freeCount++; + } + } + } + + return freeCount > 0; + } + + /** + * Restore Temporary Hotbar Swaps to their original locations. + * + * @param gui + * @param container + */ + private static void restoreHotbarForShulkerSwaps(HandledScreen gui, ScreenHandler container) + { + //System.out.printf("sort - restoring hotbar slots\n"); + + hotbarSwaps.reversed().forEach((pair) -> + { + swapSlots(gui, pair.right(), pair.left()); + }); + + // Seem that we need to swap the first entry a second time inverted, for it to be correct + if (hotbarSwaps.isEmpty() == false) + { + swapSlots(gui, hotbarSwaps.getFirst().left(), hotbarSwaps.getFirst().right()); } + + hotbarSwaps.clear(); } /** * sort inventory */ - private static void quickSort(HandledScreen gui, int start, int end) + private static void quickSort(HandledScreen gui, int start, int end, boolean shulkerBoxFix) { if (start >= end) return; + //System.out.printf("quickSort - start %d, end %d\n", start, end); ItemStack mid = gui.getScreenHandler().getSlot(end).getStack(); int l = start; int r = end - 1; + while (l < r) { - while (l < r && compareStacks(gui.getScreenHandler().getSlot(l).getStack(), mid) < 0) + while (l < r && compareStacks(gui.getScreenHandler().getSlot(l).getStack(), mid, shulkerBoxFix) < 0) { l++; } - while (l < r && compareStacks(gui.getScreenHandler().getSlot(r).getStack(), mid) >= 0) + while (l < r && compareStacks(gui.getScreenHandler().getSlot(r).getStack(), mid, shulkerBoxFix) >= 0) { r--; } - if (l != r) + + // This is ugly, but it stops infinite loops. + if (l != r && lastSwapTry.left() != l && lastSwapTry.right() != r) { swapSlots(gui, l, r); + lastSwapTry = Pair.of(l, r); + } + else if (l != r) + { + ItemScroller.logger.warn("quickSort: Item swap failure. Duplicate pair of [{}, {}], cancelling sort task", l, r); + return; } } - if (compareStacks(gui.getScreenHandler().getSlot(l).getStack(), gui.getScreenHandler().getSlot(end).getStack()) >= 0) + if (compareStacks(gui.getScreenHandler().getSlot(l).getStack(), gui.getScreenHandler().getSlot(end).getStack(), shulkerBoxFix) >= 0) { swapSlots(gui, l, end); } @@ -2733,12 +2870,30 @@ private static void quickSort(HandledScreen gui, int start, int end) l++; } - quickSort(gui, start, l - 1); - quickSort(gui, l + 1, end); + quickSort(gui, start, l - 1, shulkerBoxFix); + quickSort(gui, l + 1, end, shulkerBoxFix); } - private static int compareStacks(ItemStack stack1, ItemStack stack2) + private static int compareStacks(ItemStack stack1, ItemStack stack2, boolean shulkerBoxFix) { + // Check if they have a custom priority + int priority1 = getCustomPriority(stack1); + int priority2 = getCustomPriority(stack2); + + if (priority1 != -1 || priority2 != -1) + { + // Any of both has a priority, compare them using that + return Integer.compare(priority2, priority1); + } + + // Don't sort shulker boxes if 'fix' is enabled; such as if we are in the ShulkerBox Screen, + // since we can't insert shulkers into a Shulker Box. + if (shulkerBoxFix && + (isShulkerBox(stack1) || isShulkerBox(stack2))) + { + return 0; + } + if (Configs.Generic.SORT_SHULKER_BOXES_AT_END.getBooleanValue()) { if (isShulkerBox(stack1) && !isShulkerBox(stack2)) @@ -2750,6 +2905,18 @@ private static int compareStacks(ItemStack stack1, ItemStack stack2) return -1; } } + if (Configs.Generic.SORT_BUNDLES_AT_END.getBooleanValue()) + { + if (isBundle(stack1) && !isBundle(stack2)) + { + return 1; + } + if (!isBundle(stack1) && isBundle(stack2)) + { + return -1; + } + } + if (stack1.isEmpty() && stack2.isEmpty()) return 0; else if (stack1.isEmpty()) @@ -2757,26 +2924,174 @@ else if (stack1.isEmpty()) else if (stack2.isEmpty()) return -1; + //System.out.printf("sort - compareStacks stack1 [%s], stack2 [%s]\n", stack1.toString(), stack2.toString()); + if (isShulkerBox(stack1) && isShulkerBox(stack2)) { List contents1 = stack1.getOrDefault(DataComponentTypes.CONTAINER, ContainerComponent.DEFAULT).streamNonEmpty().toList(); List contents2 = stack2.getOrDefault(DataComponentTypes.CONTAINER, ContainerComponent.DEFAULT).streamNonEmpty().toList(); + if (contents1.size() != contents2.size()) { - return Integer.compare(contents1.size(), contents2.size()); + if (Configs.Generic.SORT_SHULKER_BOXES_INVERTED.getBooleanValue()) + { + return Integer.compare(contents1.size(), contents2.size()); + } + + return Integer.compare(contents2.size(), contents1.size()); + } + } + if (isBundle(stack1) && isBundle(stack2)) + { + if (isEmptyBundle(stack1) && isEmptyBundle(stack2)) + { + return 0; + } + else if (isEmptyBundle(stack1)) + { + return 1; + } + else if (isEmptyBundle(stack2)) + { + return -1; + } + + BundleContentsComponent bundle1 = stack1.getOrDefault(DataComponentTypes.BUNDLE_CONTENTS, BundleContentsComponent.DEFAULT); + BundleContentsComponent bundle2 = stack2.getOrDefault(DataComponentTypes.BUNDLE_CONTENTS, BundleContentsComponent.DEFAULT); + Fraction occupancy1 = bundle1.getOccupancy(); + Fraction occupancy2 = bundle2.getOccupancy(); + + if (Configs.Generic.SORT_BUNDLES_INVERTED.getBooleanValue()) + { + return occupancy1.compareTo(occupancy2); } + + return occupancy2.compareTo(occupancy1); } + + SortingMethod method = (SortingMethod) Configs.Generic.SORT_METHOD_DEFAULT.getOptionListValue(); + + if (method.equals(SortingMethod.CATEGORY_NAME) || + method.equals(SortingMethod.CATEGORY_COUNT) || + method.equals(SortingMethod.CATEGORY_RARITY) || + method.equals(SortingMethod.CATEGORY_RAWID)) + { + // Sort by Catagory + MinecraftClient mc = MinecraftClient.getInstance(); + + if (mc.world != null) + { + if (displayContext == null) + { + displayContext = SortingCategory.INSTANCE.buildDisplayContext(mc); + // This isn't used here, but it is required to build the list of items, as if we are opening the Creative Inventory Screen. + } + + SortingCategory.Entry cat1 = SortingCategory.INSTANCE.fromItemStack(stack1); + SortingCategory.Entry cat2 = SortingCategory.INSTANCE.fromItemStack(stack2); + + if (cat1.getStringValue().equals(cat2.getStringValue()) == false) + { + int index1 = Configs.Generic.SORT_CATEGORY_ORDER.getEntryIndex(cat1); + int index2 = Configs.Generic.SORT_CATEGORY_ORDER.getEntryIndex(cat2); + + //ItemScroller.logger.error("SORT - Category1[{}]: [{}] // Category2[{}]: [{}]", index1, cat1.getDisplayName(), index2, cat2.getDisplayName()); + + if (index1 < 0) + { + return 1; + } + else if (index2 < 0) + { + return -1; + } + + return Integer.compare(index1, index2); + } + } + } + if (stack1.getItem() != stack2.getItem()) { - return Integer.compare(Registries.ITEM.getRawId(stack1.getItem()), Registries.ITEM.getRawId(stack2.getItem())); + if (method.equals(SortingMethod.CATEGORY_NAME) || method.equals(SortingMethod.ITEM_NAME)) + { + // Sort by Item Name + int result = stack1.getName().getString().compareTo(stack2.getName().getString()) >= 0 ? 1 : -1; + return result == 0 ? Integer.compare(Registries.ITEM.getRawId(stack1.getItem()), Registries.ITEM.getRawId(stack2.getItem())) : result; + } + else if (method.equals(SortingMethod.CATEGORY_COUNT) || method.equals(SortingMethod.ITEM_COUNT)) + { + // Sort by Item Count + int result = Integer.compare(-stack1.getCount(), -stack2.getCount()); + return result == 0 ? Integer.compare(Registries.ITEM.getRawId(stack1.getItem()), Registries.ITEM.getRawId(stack2.getItem())) : result; + } + else if (method.equals(SortingMethod.CATEGORY_RARITY) || method.equals(SortingMethod.ITEM_RARITY)) + { + // Sort by Item Rarity + int result = stack1.getRarity().compareTo(stack2.getRarity()); + return result == 0 ? Integer.compare(Registries.ITEM.getRawId(stack1.getItem()), Registries.ITEM.getRawId(stack2.getItem())) : result; + } + else + { + // Sort by Item RawID + return Integer.compare(Registries.ITEM.getRawId(stack1.getItem()), Registries.ITEM.getRawId(stack2.getItem())); + } } if (!areStacksEqual(stack1, stack2)) { + // Sort's Data Components by Hash Code return Integer.compare(stack1.getComponents().hashCode(), stack2.getComponents().hashCode()); } + return Integer.compare(-stack1.getCount(), -stack2.getCount()); } + private static int getCustomPriority(ItemStack stack) { + if (stack == null || stack.isEmpty()) { + return -1; // No priority for empty stacks + } + + // Get item ID and name to check against custom priority lists + String itemID = Registries.ITEM.getId(stack.getItem()).toString(); + String itemName = stack.getName().getString(); + + if (itemID.equals(itemName)) + { + itemName = null; + } + + // Top priority check + int idTopPriority = topSortingPriorityList.indexOf(itemID); + int nameTopPriority = itemName != null ? topSortingPriorityList.indexOf(itemName) : -1; + + // Bottom priority check + int idBottomPriority = bottomSortingPriorityList.indexOf(itemID); + int nameBottomPriority = itemName != null ? bottomSortingPriorityList.indexOf(itemName) : -1; + + // Sort at the top: Prefer name priority if it exists + if (nameTopPriority != -1) + { + return topSortingPriorityList.size() - nameTopPriority; + } + if (idTopPriority != -1) + { + return topSortingPriorityList.size() - idTopPriority; + } + + // Sort at the bottom: Prefer name priority if it exists + if (nameBottomPriority != -1) + { + return -bottomSortingPriorityList.size() - nameBottomPriority - 2; + } + if (idBottomPriority != -1) + { + return -bottomSortingPriorityList.size() - idBottomPriority - 2; + } + + // Default: no specific priority found + return -1; + } + public static boolean onPong(StatisticsS2CPacket packet) { if (selectedSlotUpdateTask != null) @@ -2798,6 +3113,16 @@ private static boolean isEmptyShulkerBox(ItemStack stack) return isShulkerBox(stack) && stack.getOrDefault(DataComponentTypes.CONTAINER, ContainerComponent.DEFAULT).streamNonEmpty().findAny().isEmpty(); } + private static boolean isBundle(ItemStack stack) + { + return stack.isOf(Items.BUNDLE) || stack.getComponents().contains(DataComponentTypes.BUNDLE_CONTENTS); + } + + private static boolean isEmptyBundle(ItemStack stack) + { + return isBundle(stack) && fi.dy.masa.malilib.util.InventoryUtils.bundleCountItems(stack) < 1; + } + public static int stackMaxSize(ItemStack stack, boolean assumeShulkerStacking) { if (stack.isEmpty()) @@ -3042,6 +3367,8 @@ public static void dropStack(HandledScreen gui, int slo public static void swapSlots(HandledScreen gui, int slotNum, int otherSlot) { + //System.out.printf("swapSlots: [%d -> %d]\n", slotNum, otherSlot); + clickSlot(gui, slotNum, 8, SlotActionType.SWAP); clickSlot(gui, otherSlot, 8, SlotActionType.SWAP); clickSlot(gui, slotNum, 8, SlotActionType.SWAP); diff --git a/src/main/java/fi/dy/masa/itemscroller/util/SortingCategory.java b/src/main/java/fi/dy/masa/itemscroller/util/SortingCategory.java new file mode 100644 index 0000000..e45e34c --- /dev/null +++ b/src/main/java/fi/dy/masa/itemscroller/util/SortingCategory.java @@ -0,0 +1,172 @@ +package fi.dy.masa.itemscroller.util; + +import java.util.Collection; +import java.util.Iterator; +import javax.annotation.Nullable; +import com.google.common.collect.ImmutableList; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.item.ItemGroup; +import net.minecraft.item.ItemStack; +import net.minecraft.registry.Registries; +import net.minecraft.util.Identifier; + +import fi.dy.masa.malilib.config.IConfigLockedListEntry; +import fi.dy.masa.malilib.config.IConfigLockedListType; +import fi.dy.masa.malilib.util.StringUtils; +import fi.dy.masa.itemscroller.Reference; + +public class SortingCategory implements IConfigLockedListType +{ + public static final SortingCategory INSTANCE = new SortingCategory(); + public ImmutableList VALUES = ImmutableList.copyOf(Entry.values()); + + @Nullable + public ItemGroup.DisplayContext buildDisplayContext(MinecraftClient mc) + { + if (mc.world == null) + { + return null; + } + + ItemGroup.DisplayContext ctx = new ItemGroup.DisplayContext(mc.world.getEnabledFeatures(), true, mc.world.getRegistryManager()); + + Registries.ITEM_GROUP.stream().filter((group) -> + group.getType() == ItemGroup.Type.CATEGORY).forEach((group) -> + group.updateEntries(ctx)); + + return ctx; + } + + public Entry fromItemStack(ItemStack stack) + { + for (int i = 0; i < Registries.ITEM_GROUP.size(); i++) + { + ItemGroup itemGroup = Registries.ITEM_GROUP.get(i); + + if (itemGroup != null && itemGroup.getType().equals(ItemGroup.Type.CATEGORY)) + { + Collection stacks; + Iterator iter; + + if (itemGroup.hasStacks()) + { + stacks = itemGroup.getDisplayStacks(); + iter = stacks.iterator(); + + while (iter.hasNext()) + { + if (ItemStack.areItemsEqual(iter.next(), stack)) + { + return fromItemGroup(itemGroup); + } + } + + } + + stacks = itemGroup.getSearchTabStacks(); + iter = stacks.iterator(); + + while (iter.hasNext()) + { + if (ItemStack.areItemsEqual(iter.next(), stack)) + { + return fromItemGroup(itemGroup); + } + } + + } + } + + return Entry.OTHER; + } + + @Nullable + public Entry fromItemGroup(ItemGroup group) + { + Identifier id = Registries.ITEM_GROUP.getId(group); + + if (id != null) + { + return Entry.fromString(id.getPath()); + } + + return Entry.OTHER; + } + + @Override + public ImmutableList getDefaultEntries() + { + ImmutableList.Builder list = ImmutableList.builder(); + + VALUES.forEach((list::add)); + + return list.build(); + } + + @Override + @Nullable + public IConfigLockedListEntry fromString(String string) + { + return Entry.fromString(string); + } + + public enum Entry implements IConfigLockedListEntry + { + BUILDING_BLOCKS ("building_blocks", "building_blocks"), + COLORED_BLOCKS ("colored_blocks", "colored_blocks"), + NATURAL ("natural_blocks", "natural_blocks"), + FUNCTIONAL ("functional_blocks", "functional_blocks"), + REDSTONE ("redstone_blocks", "redstone_blocks"), + TOOLS ("tools_and_utilities", "tools_and_utilities"), + COMBAT ("combat", "combat"), + FOOD_AND_DRINK ("food_and_drinks", "food_and_drinks"), + INGREDIENTS ("ingredients", "ingredients"), + SPAWN_EGGS ("spawn_eggs", "spawn_eggs"), + OPERATOR ("op_blocks", "op_blocks"), + OTHER ("other", "other"); + + private final String configKey; + private final String translationKey; + + Entry(String configKey, String translationKey) + { + this.configKey = configKey; + this.translationKey = Reference.ID+".gui.label.sorting_category."+translationKey; + } + + @Override + public String getStringValue() + { + return this.configKey; + } + + @Override + public String getDisplayName() + { + return StringUtils.getTranslatedOrFallback(this.translationKey, this.configKey); + } + + @Nullable + public static Entry fromString(String key) + { + for (Entry entry : values()) + { + if (entry.configKey.equalsIgnoreCase(key)) + { + return entry; + } + else if (entry.translationKey.equalsIgnoreCase(key)) + { + return entry; + } + else if (StringUtils.hasTranslation(entry.translationKey) && StringUtils.translate(entry.translationKey).equalsIgnoreCase(key)) + { + return entry; + } + } + + return null; + } + } +} diff --git a/src/main/java/fi/dy/masa/itemscroller/util/SortingMethod.java b/src/main/java/fi/dy/masa/itemscroller/util/SortingMethod.java new file mode 100644 index 0000000..085cbed --- /dev/null +++ b/src/main/java/fi/dy/masa/itemscroller/util/SortingMethod.java @@ -0,0 +1,84 @@ +package fi.dy.masa.itemscroller.util; + +import com.google.common.collect.ImmutableList; + +import fi.dy.masa.malilib.config.IConfigOptionListEntry; +import fi.dy.masa.malilib.util.StringUtils; +import fi.dy.masa.itemscroller.Reference; + +public enum SortingMethod implements IConfigOptionListEntry +{ + CATEGORY_NAME ("category_name", "category_name"), + CATEGORY_COUNT ("category_count", "category_count"), + CATEGORY_RARITY ("category_rarity", "category_rarity"), + CATEGORY_RAWID ("category_rawid", "category_rawid"), + ITEM_NAME ("item_name", "item_name"), + ITEM_COUNT ("item_count", "item_count"), + ITEM_RARITY ("item_rarity", "item_rarity"), + ITEM_RAWID ("item_rawid", "item_rawid"); + + public static final ImmutableList VALUES = ImmutableList.copyOf(values()); + + private final String configString; + private final String translationKey; + + SortingMethod(String configString, String translationKey) + { + this.configString = configString; + this.translationKey = Reference.ID+".gui.label.sorting_method."+translationKey; + } + + @Override + public String getStringValue() + { + return this.configString; + } + + @Override + public String getDisplayName() + { + return StringUtils.getTranslatedOrFallback(this.translationKey, this.configString); + } + + @Override + public IConfigOptionListEntry cycle(boolean forward) + { + int id = this.ordinal(); + + if (forward) + { + if (++id >= values().length) + { + id = 0; + } + } + else + { + if (--id < 0) + { + id = values().length - 1; + } + } + + return values()[id % values().length]; + } + + @Override + public SortingMethod fromString(String value) + { + return fromStringStatic(value); + } + + public static SortingMethod fromStringStatic(String name) + { + for (SortingMethod val : VALUES) + { + if (val.configString.equalsIgnoreCase(name)) + { + return val; + } + } + + return SortingMethod.CATEGORY_NAME; + } +} diff --git a/src/main/java/org/thinkingstudio/rocknroller/RocknRoller.java b/src/main/java/org/thinkingstudio/rocknroller/RocknRoller.java index 237fe1d..b43918e 100644 --- a/src/main/java/org/thinkingstudio/rocknroller/RocknRoller.java +++ b/src/main/java/org/thinkingstudio/rocknroller/RocknRoller.java @@ -2,12 +2,12 @@ import fi.dy.masa.itemscroller.ItemScroller; import fi.dy.masa.itemscroller.Reference; -import fi.dy.masa.itemscroller.gui.GuiConfigs; +import fi.dy.masa.malilib.compat.modmenu.ModMenuImpl; import net.neoforged.api.distmarker.Dist; import net.neoforged.fml.ModContainer; import net.neoforged.fml.common.Mod; import net.neoforged.fml.loading.FMLLoader; -import org.thinkingstudio.mafglib.util.NeoUtils; +import net.neoforged.neoforge.client.gui.IConfigScreenFactory; @Mod(value = Reference.MOD_ID, dist = Dist.CLIENT) public class RocknRoller { @@ -15,12 +15,7 @@ public RocknRoller(ModContainer modContainer) { if (FMLLoader.getDist().isClient()) { ItemScroller.onInitialize(); - // Config Screen - NeoUtils.getInstance().registerConfigScreen(modContainer, (screen) -> { - GuiConfigs gui = new GuiConfigs(); - gui.setParent(screen); - return gui; - }); + modContainer.registerExtensionPoint(IConfigScreenFactory.class, new ModMenuImpl().getModConfigScreenFactory()); } } } \ No newline at end of file diff --git a/src/main/resources/assets/rocknroller/lang/en_us.json b/src/main/resources/assets/rocknroller/lang/en_us.json index 4d07093..6e9bbe3 100644 --- a/src/main/resources/assets/rocknroller/lang/en_us.json +++ b/src/main/resources/assets/rocknroller/lang/en_us.json @@ -1,6 +1,4 @@ { - "fml.menu.mods.info.description.rocknroller": "ItemScroller unofficial (Neo)Forge port.\nMove items in inventory GUIs by scrolling the mouse wheel or dragging over slots.", - "itemscroller.config.generic.name.carpetCtrlQCraftingEnabledOnServer": "carpetCtrlQCraftingEnabledOnServer", "itemscroller.config.generic.name.clientCraftingFixOn1_12": "clientCraftingFixOn1.12", "itemscroller.config.generic.name.craftingRenderRecipeItems": "craftingRenderRecipeItems", @@ -10,6 +8,7 @@ "itemscroller.config.generic.name.massCraftInterval": "massCraftInterval", "itemscroller.config.generic.name.massCraftIterations": "massCraftIterations", "itemscroller.config.generic.name.massCraftSwapsOnly": "massCraftSwapsOnly", + "itemscroller.config.generic.name.massCraftUseRecipeBook": "massCraftUseRecipeBook", "itemscroller.config.generic.name.packetRateLimit": "packetRateLimit", "itemscroller.config.generic.name.craftingRecipesSaveToFile": "craftingRecipesSaveToFile", "itemscroller.config.generic.name.craftingRecipesSaveFileIsGlobal": "craftingRecipesSaveFileIsGlobal", @@ -20,8 +19,16 @@ "itemscroller.config.generic.name.useSlotPositionAwareScrollDirection": "useSlotPositionAwareScrollDirection", "itemscroller.config.generic.name.villagerTradeUseGlobalFavorites": "villagerTradeUseGlobalFavorites", "itemscroller.config.generic.name.villagerTradeListRememberScrollPosition": "villagerTradeListRememberScrollPosition", + "itemscroller.config.generic.name.sortInventoryToggle": "sortInventoryToggle", "itemscroller.config.generic.name.sortAssumeEmptyBoxStacks": "sortAssumeEmptyBoxStacks", "itemscroller.config.generic.name.sortShulkerBoxesAtEnd": "sortShulkerBoxesAtEnd", + "itemscroller.config.generic.name.sortShulkerBoxesInverted": "sortShulkerBoxesInverted", + "itemscroller.config.generic.name.sortBundlesAtEnd": "sortBundlesAtEnd", + "itemscroller.config.generic.name.sortBundlesInverted": "sortBundlesInverted", + "itemscroller.config.generic.name.sortTopPriorityInventory": "sortTopPriorityInventory", + "itemscroller.config.generic.name.sortBottomPriorityInventory": "sortBottomPriorityInventory", + "itemscroller.config.generic.name.sortMethodDefault": "sortMethodDefault", + "itemscroller.config.generic.name.sortCategoryOrder": "sortCategoryOrder", "itemscroller.config.generic.comment.carpetCtrlQCraftingEnabledOnServer": "Set to true if the server is running the Carpet mod,\nand has the ctrlQCrafting option enabled.\nThis just changes which method Item Scroller uses\nfor the Drop key + Shift + Right click crafting.", "itemscroller.config.generic.comment.clientCraftingFixOn1_12": "Enable updating the crafting recipe output directly on the client side.\nThis fixes the quick/mass crafting and right-click-to-craft-a-stack\nfeatures otherwise being broken in 1.12.", @@ -32,18 +39,27 @@ "itemscroller.config.generic.comment.massCraftInterval": "The interval in game ticks the massCraft operation is repeated at", "itemscroller.config.generic.comment.massCraftIterations": "How many massCraft iterations/attempts to do per execution.\nWith unstackable items or a full inventory and \"small recipe\"\nthis will need to be larger, as a shift + click craft to the inventory\nmight only craft 1 or 2 items per operation.", "itemscroller.config.generic.comment.massCraftSwapsOnly": "Uses a newer method of filling the crafting grid,\nusing only swap slot packets.\n\nNote: Due to only using slot swap packets,\nno partial crafts are possible! And also no\nstack splitting will happen, at all.", + "itemscroller.config.generic.comment.massCraftUseRecipeBook": "Uses the \"Recipe Book Protocol\" inspired by Andrew54757's \"Craft-Fix\" fork,\nbut re-implemented in a way that works for 1.21+.\n§6NOTE: This is NOT compatible with \"rateLimitClickPackets\" enabled.", "itemscroller.config.generic.comment.packetRateLimit": "The limit of sent emulated slot click packets per game tick,\nif 'rateLimitClickPackets' is enabled", "itemscroller.config.generic.comment.craftingRecipesSaveToFile": "If enabled, then the crafting features recipes are saved to a file\ninside minecraft/itemscroller/recipes_worldorservername.nbt.\nThis makes the recipes persistent across game restarts.", "itemscroller.config.generic.comment.craftingRecipesSaveFileIsGlobal": "If true, then the recipe file is global, instead\n of being saved per-world or server", - "itemscroller.config.generic.comment.rateLimitClickPackets": "This is meant for compatibility with Spigot servers and similar,\nwhich apply rate limits to packets from the client.\nThis queues up the emulated slot click packets and sends\nthem rate limited over time. The limit per game tick can be set in 'packetRateLimit´.", + "itemscroller.config.generic.comment.rateLimitClickPackets": "This is meant for compatibility with Spigot servers and similar,\nwhich apply rate limits to packets from the client.\nThis queues up the emulated slot click packets and sends\nthem rate limited over time. The limit per game tick can be set in 'packetRateLimit´.\n§6NOTE: This is NOT compatible with \"massCraftUseRecipeBook\" enabled.", "itemscroller.config.generic.comment.reverseScrollDirectionSingle": "Reverse the scrolling direction for single item mode.", "itemscroller.config.generic.comment.reverseScrollDirectionStacks": "Reverse the scrolling direction for full stacks mode.", "itemscroller.config.generic.comment.useRecipeCaching": "Enables caching the last used recipe in the crafting\nrecipe output item fetching code. This can help a lot\nwith lowering CPU usage when mass crafting stuff.", "itemscroller.config.generic.comment.useSlotPositionAwareScrollDirection": "When enabled, the item movement direction depends\non the slots' y-position on screen. Might be derpy with more\ncomplex inventories, use with caution!", "itemscroller.config.generic.comment.villagerTradeUseGlobalFavorites": "Whether or not global (per-item-type) villager trade\nfavorites should be used.", "itemscroller.config.generic.comment.villagerTradeListRememberScrollPosition": "Remember and restore the last scroll position in the\ntrade list when re-opening the GUI", - "itemscroller.config.generic.comment.sortAssumeEmptyBoxStacks": "Assume that empty boxes are stacking items\nwhen sorting the inventory.\nThis is useful if you installed mods to stack\nshulker boxes on remote server.\nThis will send an extra packet to ensure the\ninventory is synced.", + "itemscroller.config.generic.comment.sortInventoryToggle": "Enables the Quick Inventory Sorting feature.\nThis is activated by hovering over any item,\nand then pressing the bound §a\"sortInventory\"§r key bind.", + "itemscroller.config.generic.comment.sortAssumeEmptyBoxStacks": "Assume that empty shulker boxes can\nstack when sorting the inventory.\nThis is useful if you installed mods\nto allow shulker boxes to stack.\nThis will send an extra packet to ensure the\ninventory is synced.", "itemscroller.config.generic.comment.sortShulkerBoxesAtEnd": "Sort shulker boxes at the end of the inventory\nwhen sorting the inventory.", + "itemscroller.config.generic.comment.sortShulkerBoxesInverted": "Sort shulker boxes so that the most-empty\nboxes are first, and the full boxes are last.", + "itemscroller.config.generic.comment.sortBundlesAtEnd": "Sort bundles at the end of the inventory\nwhen sorting the inventory.", + "itemscroller.config.generic.comment.sortBundlesInverted": "Sort bundles so that the most-empty\nbundles are first, and the full bundles are last.", + "itemscroller.config.generic.comment.sortTopPriorityInventory": "These items will have a priority to be sorted first.\nYou can put item IDs (eg. \"minecraft:diamond_sword\") or custom names (eg. \"Wither killer\").\nIf you put custom names, those will be preferred before the id.\nItems in the list will be sorted to the front according to the list.", + "itemscroller.config.generic.comment.sortBottomPriorityInventory": "These items will have a priority to be sorted last.\nYou can put item IDs (eg. \"minecraft:diamond_sword\") or custom names (eg. \"Wither killer\").\nIf you put custom names, those will be preferred before the id.\nItems in the list will be sorted to the back according to the list.", + "itemscroller.config.generic.comment.sortMethodDefault": "The default sorting method used to compare items.\n\n§6Category [ANY]§r = Sort by §a\"sortCategoryOrder\"§r first, and then followed by:\n§6Item Name§r - Sort by Items' Name\n§6Item Count§r - Sort by Item's Stack Size, then by RawID\n§6Item Rarity§r - Sort by Rarity, then by RawID\n§6Item RawID§r - Sort by RawID\n§d(RawID is the built-in Vanilla Item Index value)§r\n\n§cData Components are compared by Hash Code afterwards, and then\n§cby Item Stack Size after all other checks have passed.§r", + "itemscroller.config.generic.comment.sortCategoryOrder": "The Category order to be used for sorting items.\nThe Vanilla Creative Menu Categories are used.", "itemscroller.config.toggles.name.enableCraftingFeatures": "enableCraftingFeatures", "itemscroller.config.toggles.name.enableDropkeyDropMatching": "enableDropkeyDropMatching", @@ -145,6 +161,28 @@ "itemscroller.gui.label.trades": "Trades", "itemscroller.gui.label.trade_uses": "Trade uses: %d / %d", + "itemscroller.gui.label.sorting_method.category_name": "Category Name", + "itemscroller.gui.label.sorting_method.category_count": "Category Count", + "itemscroller.gui.label.sorting_method.category_rarity": "Category Rarity", + "itemscroller.gui.label.sorting_method.category_rawid": "Category RawID", + "itemscroller.gui.label.sorting_method.item_name": "Item Name", + "itemscroller.gui.label.sorting_method.item_count": "Item Count", + "itemscroller.gui.label.sorting_method.item_rarity": "Item Rarity", + "itemscroller.gui.label.sorting_method.item_rawid": "Item Raw ID", + + "itemscroller.gui.label.sorting_category.building_blocks": "Building Blocks", + "itemscroller.gui.label.sorting_category.colored_blocks": "Colored Blocks", + "itemscroller.gui.label.sorting_category.natural_blocks": "Natural Blocks", + "itemscroller.gui.label.sorting_category.functional_blocks": "Functional Blocks", + "itemscroller.gui.label.sorting_category.redstone_blocks": "Redstone Items", + "itemscroller.gui.label.sorting_category.tools_and_utilities": "Tools and Utilities", + "itemscroller.gui.label.sorting_category.combat": "Combat Items", + "itemscroller.gui.label.sorting_category.food_and_drinks": "Food and Drinks", + "itemscroller.gui.label.sorting_category.ingredients": "Ingredients", + "itemscroller.gui.label.sorting_category.spawn_eggs": "Spawn Eggs", + "itemscroller.gui.label.sorting_category.op_blocks": "Operator Blocks", + "itemscroller.gui.label.sorting_category.other": "Other Items", + "itemscroller.gui.title.configs": "%s Configs - %s", "itemscroller.message.toggled_mod_off": "Toggled all Item Scroller functionality §cOFF", diff --git a/src/main/resources/assets/rocknroller/lang/es_es.json b/src/main/resources/assets/rocknroller/lang/es_es.json index d6d2eda..57a3ba2 100644 --- a/src/main/resources/assets/rocknroller/lang/es_es.json +++ b/src/main/resources/assets/rocknroller/lang/es_es.json @@ -8,6 +8,7 @@ "itemscroller.config.generic.name.massCraftInhibitMidUpdates": "massCraftInhibitMidUpdates", "itemscroller.config.generic.name.massCraftInterval": "massCraftInterval", "itemscroller.config.generic.name.massCraftIterations": "massCraftIterations", + "itemscroller.config.generic.name.massCraftUseRecipeBook": "massCraftUseRecipeBook", "itemscroller.config.generic.name.massCraftSwapsOnly": "massCraftSwapsOnly", "itemscroller.config.generic.name.modMainToggle": "modMainToggle", "itemscroller.config.generic.name.packetRateLimit": "packetRateLimit", @@ -18,8 +19,16 @@ "itemscroller.config.generic.name.useSlotPositionAwareScrollDirection": "useSlotPositionAwareScrollDirection", "itemscroller.config.generic.name.villagerTradeListRememberScrollPosition": "villagerTradeListRememberScrollPosition", "itemscroller.config.generic.name.villagerTradeUseGlobalFavorites": "villagerTradeUseGlobalFavorites", + "itemscroller.config.generic.name.sortInventoryToggle": "sortInventoryToggle", "itemscroller.config.generic.name.sortAssumeEmptyBoxStacks": "sortAssumeEmptyBoxStacks", "itemscroller.config.generic.name.sortShulkerBoxesAtEnd": "sortShulkerBoxesAtEnd", + "itemscroller.config.generic.name.sortShulkerBoxesInverted": "sortShulkerBoxesInverted", + "itemscroller.config.generic.name.sortBundlesAtEnd": "sortBundlesAtEnd", + "itemscroller.config.generic.name.sortBundlesInverted": "sortBundlesInverted", + "itemscroller.config.generic.name.sortTopPriorityInventory": "sortTopPriorityInventory", + "itemscroller.config.generic.name.sortBottomPriorityInventory": "sortBottomPriorityInventory", + "itemscroller.config.generic.name.sortMethodDefault": "sortMethodDefault", + "itemscroller.config.generic.name.sortCategoryOrder": "sortCategoryOrder", "itemscroller.config.generic.comment.carpetCtrlQCraftingEnabledOnServer": "Activalo si el servidor tiene Carpet mod,\ny tienes activado el ctrlQCrafting .\nEsto solo cambia la forma en la forma en la que el mod de Item Scroller\nfunciona con la combiacion de teclas + Shift + Clik derecho en mesas de crafteo/inventario y demás.", "itemscroller.config.generic.comment.clientCraftingFixOn1_12": "Arregla el problema del crafteo masivo en la 1.12.", @@ -30,6 +39,7 @@ "itemscroller.config.generic.comment.massCraftInhibitMidUpdates": "Previene las actualizaciones de la preview del item a crafear \nmientras se mueven los items en el crafteo, esto deberia reducir el uso de CPU\n al no estar el sistema consultando cada actualizacion necesaria.", "itemscroller.config.generic.comment.massCraftInterval": "El intervalo en tick del juego mientras se relaiza el crafteo masivo", "itemscroller.config.generic.comment.massCraftIterations": "Cuantas interacciones/intentos para el crafteo masivo relaizar por ejeccucion\nCon stacks de objetos, objetos no apilables,el inventario lleno o una receta pequeña\nEsto se usa para un crafteo masivo, ya que de lo contrario solo creara 1 o 2 crafteos por operacion.", + "itemscroller.config.generic.comment.massCraftUseRecipeBook": "Uses the \"Recipe Book Protocol\" inspired by Andrew54757's \"Craft-Fix\" fork,\nbut re-implemented in a way that works for 1.21+.\n§6NOTE: This is NOT compatible with \"rateLimitClickPackets\" enabled.", "itemscroller.config.generic.comment.massCraftSwapsOnly": "Usa el metodo mas reciente para llenar la grid de crafteo,\nutilizando solo intercambios de espacios.\n\nNota: Debido a que solo se utilizan paquetes de intercambio de los espacios,\nno son posibles los crafteos parciales\ntampoco se dividira ningun stack.", "itemscroller.config.generic.comment.modMainToggle": "Desactiva todas las funcionalidades del mod.", "itemscroller.config.generic.comment.packetRateLimit": "El límite de paquetes de clics en tragamonedas emulados enviados por tick de juego,\nsi 'rateLimitClickPackets' está habilitado", @@ -40,8 +50,16 @@ "itemscroller.config.generic.comment.useSlotPositionAwareScrollDirection": "Cuando esta activado, el movimiento de los items, depende de la posicion en las ranuras en la pantalla\n Esto puede tener un comportamiento inesperado con invetarios mas complejos\n¡Usalo con precaucion!", "itemscroller.config.generic.comment.villagerTradeListRememberScrollPosition": "Recuerda y restaura la ultima posicion de scroll en\nla lista de tradeos al abrir la GUI", "itemscroller.config.generic.comment.villagerTradeUseGlobalFavorites": "Activa los intercambios favoritos con aldenos que tenga guardados como Globales o Locales", + "itemscroller.config.generic.comment.sortInventoryToggle": "Enables the Quick Inventory Sorting feature.\nThis is activated by hovering over any item,\nand then pressing the bound §a\"sortInventory\"§r key bind.", "itemscroller.config.generic.comment.sortAssumeEmptyBoxStacks": "Assume that empty boxes are stacking items\nwhen sorting the inventory.\nThis is useful if you installed mods to stack\nshulker boxes on remote server.\nThis will send an extra packet to ensure the\ninventory is synced.", "itemscroller.config.generic.comment.sortShulkerBoxesAtEnd": "Sort shulker boxes at the end of the inventory\nwhen sorting the inventory.", + "itemscroller.config.generic.comment.sortShulkerBoxesInverted": "Sort shulker boxes so that the most-empty\nboxes are first, and the full boxes are last.", + "itemscroller.config.generic.comment.sortBundlesAtEnd": "Sort bundles at the end of the inventory\nwhen sorting the inventory.", + "itemscroller.config.generic.comment.sortBundlesInverted": "Sort bundles so that the most-empty\nbundles are first, and the full bundles are last.", + "itemscroller.config.generic.comment.sortTopPriorityInventory": "Estos ítems tendrán prioridad para ser ordenados primero.\nPuedes poner IDs de ítems (ej. \"minecraft:diamond_sword\") o nombres personalizados (ej. \"Matador de Withers\").\nSi pones nombres personalizados, estos serán preferidos antes que los IDs.\nLos ítems en la lista se ordenarán al frente según la lista.", + "itemscroller.config.generic.comment.sortBottomPriorityInventory": "Estos ítems tendrán prioridad para ser ordenados al final.\nPuedes poner IDs de ítems (ej. \"minecraft:diamond_sword\") o nombres personalizados (ej. \"Matador de Withers\").\nSi pones nombres personalizados, estos serán preferidos antes que los IDs.\nLos ítems en la lista se ordenarán al final según la lista.", + "itemscroller.config.generic.comment.sortMethodDefault": "The default sorting method used to compare items.\n\n§6Category [ANY]§r = Sort by §a\"sortCategoryOrder\"§r first, and then followed by:\n§6Item Name§r - Sort by Items' Name\n§6Item Count§r - Sort by Item's Stack Size, then by RawID\n§6Item Rarity§r - Sort by Rarity, then by RawID\n§6Item RawID§r - Sort by RawID\n§d(RawID is the built-in Vanilla Item Index value)§r\n\n§cData Components are compared by Hash Code afterwards, and then\n§cby Item Stack Size after all other checks have passed.§r", + "itemscroller.config.generic.comment.sortCategoryOrder": "The Category order to be used for sorting items.\nThe Vanilla Creative Menu Categories are used.", "itemscroller.config.toggles.name.enableCraftingFeatures": "enableCraftingFeatures", "itemscroller.config.toggles.name.enableDropkeyDropMatching": "enableDropkeyDropMatching", @@ -143,6 +161,28 @@ "itemscroller.gui.label.trade_uses": "Uso de Tradeos: %d / %d", "itemscroller.gui.label.trades": "Tradeos", + "itemscroller.gui.label.sorting_method.category_name": "Category Name", + "itemscroller.gui.label.sorting_method.category_count": "Category Count", + "itemscroller.gui.label.sorting_method.category_rarity": "Category Rarity", + "itemscroller.gui.label.sorting_method.category_rawid": "Category RawID", + "itemscroller.gui.label.sorting_method.item_name": "Item Name", + "itemscroller.gui.label.sorting_method.item_count": "Item Count", + "itemscroller.gui.label.sorting_method.item_rarity": "Item Rarity", + "itemscroller.gui.label.sorting_method.item_rawid": "Item Raw ID", + + "itemscroller.gui.label.sorting_category.building_blocks": "Building Blocks", + "itemscroller.gui.label.sorting_category.colored_blocks": "Colored Blocks", + "itemscroller.gui.label.sorting_category.natural_blocks": "Natural Blocks", + "itemscroller.gui.label.sorting_category.functional_blocks": "Functional Blocks", + "itemscroller.gui.label.sorting_category.redstone_blocks": "Redstone Items", + "itemscroller.gui.label.sorting_category.tools_and_utilities": "Tools and Utilities", + "itemscroller.gui.label.sorting_category.combat": "Combat Items", + "itemscroller.gui.label.sorting_category.food_and_drinks": "Food and Drinks", + "itemscroller.gui.label.sorting_category.ingredients": "Ingredients", + "itemscroller.gui.label.sorting_category.spawn_eggs": "Spawn Eggs", + "itemscroller.gui.label.sorting_category.op_blocks": "Operator Blocks", + "itemscroller.gui.label.sorting_category.other": "Other Items", + "itemscroller.gui.title.configs": "%s Configuraciones - %s", "itemscroller.message.toggled_mod_off": "Activar todas las funionalidades de Item Scroller §cOFF", diff --git a/src/main/resources/assets/rocknroller/lang/zh_cn.json b/src/main/resources/assets/rocknroller/lang/zh_cn.json index 1fab30b..66f866d 100644 --- a/src/main/resources/assets/rocknroller/lang/zh_cn.json +++ b/src/main/resources/assets/rocknroller/lang/zh_cn.json @@ -1,6 +1,4 @@ { - "fml.menu.mods.info.description.rocknroller": "ItemScroller 非官方 (Neo)Forge 移植。\n通过滚动鼠标滚轮或在物品槽上拖动来移动库存图形用户界面中的物品。", - "itemscroller.config.generic.name.carpetCtrlQCraftingEnabledOnServer": "地毯端CtrlQ启用", "itemscroller.config.generic.name.clientCraftingFixOn1_12": "1.12客户端修复", "itemscroller.config.generic.name.craftingRenderRecipeItems": "配方显示", @@ -9,6 +7,7 @@ "itemscroller.config.generic.name.massCraftInhibitMidUpdates": "批量合成刷新优化", "itemscroller.config.generic.name.massCraftInterval": "批量合成速度", "itemscroller.config.generic.name.massCraftIterations": "批量合成尝试", + "itemscroller.config.generic.name.massCraftUseRecipeBook": "massCraftUseRecipeBook", "itemscroller.config.generic.name.massCraftSwapsOnly": "批量合成新方法", "itemscroller.config.generic.name.packetRateLimit": "最大数据流", "itemscroller.config.generic.name.craftingRecipesSaveToFile": "配方文件保存", @@ -20,8 +19,16 @@ "itemscroller.config.generic.name.useSlotPositionAwareScrollDirection": "栏位滚动方向", "itemscroller.config.generic.name.villagerTradeUseGlobalFavorites": "置顶收藏交易", "itemscroller.config.generic.name.villagerTradeListRememberScrollPosition": "交易书签", + "itemscroller.config.generic.name.sortInventoryToggle": "sortInventoryToggle", "itemscroller.config.generic.name.sortAssumeEmptyBoxStacks": "sortAssumeEmptyBoxStacks", "itemscroller.config.generic.name.sortShulkerBoxesAtEnd": "sortShulkerBoxesAtEnd", + "itemscroller.config.generic.name.sortShulkerBoxesInverted": "sortShulkerBoxesInverted", + "itemscroller.config.generic.name.sortBundlesAtEnd": "sortBundlesAtEnd", + "itemscroller.config.generic.name.sortBundlesInverted": "sortBundlesInverted", + "itemscroller.config.generic.name.sortTopPriorityInventory": "优先排序物品清单", + "itemscroller.config.generic.name.sortBottomPriorityInventory": "最后排序物品清单", + "itemscroller.config.generic.name.sortMethodDefault": "sortMethodDefault", + "itemscroller.config.generic.name.sortCategoryOrder": "sortCategoryOrder", "itemscroller.config.generic.comment.carpetCtrlQCraftingEnabledOnServer": "如果服务器正在运行地毯端则建议开启\n这将改变物品滚轮整组丢弃的使用方式\n变为丢弃+Shift+右击丢弃整组。", "itemscroller.config.generic.comment.clientCraftingFixOn1_12": "在客户端直接更新合成配方输出\n这修正了在1.12中快速/批量制造和右键点击制造所产生的不可使用的问题。", @@ -31,6 +38,7 @@ "itemscroller.config.generic.comment.massCraftInhibitMidUpdates": "禁止批量合成时配方输出槽在材料移动到合成GUI的过程中更新。\n这将会减少CPU的占用,因为游戏会在每次合成配方更改后\n不断地查询配方并显示产物。", "itemscroller.config.generic.comment.massCraftInterval": "每tick中批量合成的次数。", "itemscroller.config.generic.comment.massCraftIterations": "设置每次执行多少次批量合成的迭代或者尝试,\n适用于当玩家合成不可堆叠的物品或玩家背包已满这类情况,\n这将需要玩家拥有更大的背包空间,\n因为使用shift单击合成时,\n背包中可能只进行1或2次合成操作。", + "itemscroller.config.generic.comment.massCraftUseRecipeBook": "Uses the \"Recipe Book Protocol\" inspired by Andrew54757's \"Craft-Fix\" fork,\nbut re-implemented in a way that works for 1.21+.\n§6NOTE: This is NOT compatible with \"rateLimitClickPackets\" enabled.", "itemscroller.config.generic.comment.massCraftSwapsOnly": "使用新的方法填充配方GUI,\n即只使用slot swap packets(槽位交换包)\n注意:由于只使用槽位交换包,不可能有部分的合成!\n而且也不会发生整组物品拆开的现象。", "itemscroller.config.generic.comment.packetRateLimit": "当 §6[连点限制]§f 开启后每个tick发送的最大模拟操作数据包的数量。", "itemscroller.config.generic.comment.craftingRecipesSaveToFile": "启用后,合成配方将保存到文件\n\"minecraft/itemscroller/recipes_worldorservername.nbt\"\n中。这可以使得合成配方在游戏重启后能保持不变。", @@ -42,8 +50,16 @@ "itemscroller.config.generic.comment.useSlotPositionAwareScrollDirection": "启用后,物品的移动方向将取决于屏幕上栏位的y轴位置。\n可能导致更复杂的背包,谨慎使用!", "itemscroller.config.generic.comment.villagerTradeUseGlobalFavorites": "启用后,收藏的交易项目将会被置顶。", "itemscroller.config.generic.comment.villagerTradeListRememberScrollPosition": "启用后,退出村民交易GUI将不会重置滚动条。", + "itemscroller.config.generic.comment.sortInventoryToggle": "Enables the Quick Inventory Sorting feature.\nThis is activated by hovering over any item,\nand then pressing the bound §a\"sortInventory\"§r key bind.", "itemscroller.config.generic.comment.sortAssumeEmptyBoxStacks": "整理物品栏时假设空潜影盒可以堆叠。\n当你在远程服务器启用此特性时会很有用。\n这会多发送一个网络包来确保服务器同步", "itemscroller.config.generic.comment.sortShulkerBoxesAtEnd": "整理物品栏时将潜影盒放在最后。", + "itemscroller.config.generic.comment.sortShulkerBoxesInverted": "Sort shulker boxes so that the most-empty\nboxes are first, and the full boxes are last.", + "itemscroller.config.generic.comment.sortBundlesAtEnd": "Sort bundles at the end of the inventory\nwhen sorting the inventory.", + "itemscroller.config.generic.comment.sortBundlesInverted": "Sort bundles so that the most-empty\nbundles are first, and the full bundles are last.", + "itemscroller.config.generic.comment.sortTopPriorityInventory": "这些物品将优先排序。\n你可以输入物品ID(例如 \"minecraft:diamond_sword\")或自定义名称(例如 \"凋零杀手\")。\n如果输入了自定义名称,则优先使用自定义名称。\n列表中的物品将根据列表顺序排在前面。", + "itemscroller.config.generic.comment.sortBottomPriorityInventory": "这些物品将最后排序。\n你可以输入物品ID(例如 \"minecraft:diamond_sword\")或自定义名称(例如 \"凋零杀手\")。\n如果输入了自定义名称,则优先使用自定义名称。\n列表中的物品将根据列表顺序排在后面。", + "itemscroller.config.generic.comment.sortMethodDefault": "The default sorting method used to compare items.\n\n§6Category [ANY]§r = Sort by §a\"sortCategoryOrder\"§r first, and then followed by:\n§6Item Name§r - Sort by Items' Name\n§6Item Count§r - Sort by Item's Stack Size, then by RawID\n§6Item Rarity§r - Sort by Rarity, then by RawID\n§6Item RawID§r - Sort by RawID\n§d(RawID is the built-in Vanilla Item Index value)§r\n\n§cData Components are compared by Hash Code afterwards, and then\n§cby Item Stack Size after all other checks have passed.§r", + "itemscroller.config.generic.comment.sortCategoryOrder": "The Category order to be used for sorting items.\nThe Vanilla Creative Menu Categories are used.", "itemscroller.config.toggles.name.enableCraftingFeatures": "启用配方视图", "itemscroller.config.toggles.name.enableDropkeyDropMatching": "全部丢弃", @@ -145,6 +161,28 @@ "itemscroller.gui.label.trades": "交易", "itemscroller.gui.label.trade_uses": "交易使用: %d / %", + "itemscroller.gui.label.sorting_method.category_name": "Category Name", + "itemscroller.gui.label.sorting_method.category_count": "Category Count", + "itemscroller.gui.label.sorting_method.category_rarity": "Category Rarity", + "itemscroller.gui.label.sorting_method.category_rawid": "Category RawID", + "itemscroller.gui.label.sorting_method.item_name": "Item Name", + "itemscroller.gui.label.sorting_method.item_count": "Item Count", + "itemscroller.gui.label.sorting_method.item_rarity": "Item Rarity", + "itemscroller.gui.label.sorting_method.item_rawid": "Item Raw ID", + + "itemscroller.gui.label.sorting_category.building_blocks": "Building Blocks", + "itemscroller.gui.label.sorting_category.colored_blocks": "Colored Blocks", + "itemscroller.gui.label.sorting_category.natural_blocks": "Natural Blocks", + "itemscroller.gui.label.sorting_category.functional_blocks": "Functional Blocks", + "itemscroller.gui.label.sorting_category.redstone_blocks": "Redstone Items", + "itemscroller.gui.label.sorting_category.tools_and_utilities": "Tools and Utilities", + "itemscroller.gui.label.sorting_category.combat": "Combat Items", + "itemscroller.gui.label.sorting_category.food_and_drinks": "Food and Drinks", + "itemscroller.gui.label.sorting_category.ingredients": "Ingredients", + "itemscroller.gui.label.sorting_category.spawn_eggs": "Spawn Eggs", + "itemscroller.gui.label.sorting_category.op_blocks": "Operator Blocks", + "itemscroller.gui.label.sorting_category.other": "Other Items", + "itemscroller.gui.title.configs": "%s 设置 - %s", "itemscroller.message.toggled_mod_off": "Item Scroller 功能调整为:§c关闭", diff --git a/src/main/resources/assets/rocknroller/lang/zh_tw.json b/src/main/resources/assets/rocknroller/lang/zh_tw.json index 5f4d036..6c9ad52 100644 --- a/src/main/resources/assets/rocknroller/lang/zh_tw.json +++ b/src/main/resources/assets/rocknroller/lang/zh_tw.json @@ -7,6 +7,7 @@ "itemscroller.config.generic.name.massCraftInhibitMidUpdates": "批量合成刷新優化", "itemscroller.config.generic.name.massCraftInterval": "批量合成速度", "itemscroller.config.generic.name.massCraftIterations": "批量合成嘗試", + "itemscroller.config.generic.name.massCraftUseRecipeBook": "massCraftUseRecipeBook", "itemscroller.config.generic.name.massCraftSwapsOnly": "批量合成新方法", "itemscroller.config.generic.name.packetRateLimit": "最大數據流", "itemscroller.config.generic.name.craftingRecipesSaveToFile": "配方檔案保存", @@ -18,8 +19,16 @@ "itemscroller.config.generic.name.useSlotPositionAwareScrollDirection": "欄位滾動方向", "itemscroller.config.generic.name.villagerTradeUseGlobalFavorites": "置頂收藏交易", "itemscroller.config.generic.name.villagerTradeListRememberScrollPosition": "交易書籤", + "itemscroller.config.generic.name.sortInventoryToggle": "sortInventoryToggle", "itemscroller.config.generic.name.sortAssumeEmptyBoxStacks": "嘗試堆疊空界伏盒", "itemscroller.config.generic.name.sortShulkerBoxesAtEnd": "界伏盒置底", + "itemscroller.config.generic.name.sortShulkerBoxesInverted": "sortShulkerBoxesInverted", + "itemscroller.config.generic.name.sortBundlesAtEnd": "sortBundlesAtEnd", + "itemscroller.config.generic.name.sortBundlesInverted": "sortBundlesInverted", + "itemscroller.config.generic.name.sortTopPriorityInventory": "優先排序物品清單", + "itemscroller.config.generic.name.sortBottomPriorityInventory": "最後排序物品清單", + "itemscroller.config.generic.name.sortMethodDefault": "sortMethodDefault", + "itemscroller.config.generic.name.sortCategoryOrder": "sortCategoryOrder", "itemscroller.config.generic.comment.carpetCtrlQCraftingEnabledOnServer": "如果伺服器正在運行地毯端則建議開啟\n這將改變物品滾輪整組丟棄的使用方式\n變成 丟棄鍵+Shift+右鍵 丟棄整組。", "itemscroller.config.generic.comment.clientCraftingFixOn1_12": "在客戶端直接更新合成配方輸出\n這修正了在1.12中快速/大量製造和右鍵點擊製造所產生的不可使用的問題。", @@ -29,6 +38,7 @@ "itemscroller.config.generic.comment.massCraftInhibitMidUpdates": "禁止批量合成時配方輸出槽在材料移動到合成GUI的過程中更新。\n這將會減少CPU的佔用,因為遊戲會在每次合成配方更改後\n不斷地查詢配方並顯示產物。", "itemscroller.config.generic.comment.massCraftInterval": "每tick中批量合成的次數。", "itemscroller.config.generic.comment.massCraftIterations": "設定每次執行多少次批次合成的迭代或嘗試,\n適用於玩家合成不可堆疊的物品或玩家背包已滿這類情況,\n這將需要玩家擁有更大的背包空間,\n因為使用shift單擊合成時,\n背包中可能只進行1或2次合成操作。", + "itemscroller.config.generic.comment.massCraftUseRecipeBook": "Uses the \"Recipe Book Protocol\" inspired by Andrew54757's \"Craft-Fix\" fork,\nbut re-implemented in a way that works for 1.21+.\n§6NOTE: This is NOT compatible with \"rateLimitClickPackets\" enabled.", "itemscroller.config.generic.comment.massCraftSwapsOnly": "使用新的方法填滿配方GUI,\n即只使用槽位交換包\n注意:由於只使用槽位交換包,所以不可能有部分的合成!\n而且也不會發生整組物品拆開的現象。", "itemscroller.config.generic.comment.packetRateLimit": "當 §6[連點限制]§f 開啟後每個tick發送的最大模擬操作數據包的數量。", "itemscroller.config.generic.comment.craftingRecipesSaveToFile": "啟用後,合成配方將保存到檔案\n\"minecraft/itemscroller/recipes_worldorservername.nbt\"\n中。這可以使得合成配方在遊戲重啟後能保持不變。", @@ -40,8 +50,16 @@ "itemscroller.config.generic.comment.useSlotPositionAwareScrollDirection": "啟用後,物品的移動方向將取決於螢幕上欄位的y軸位置。\n可能導致更複雜的背包,謹慎使用!", "itemscroller.config.generic.comment.villagerTradeUseGlobalFavorites": "啟用後,收藏的交易項目將會被置頂。", "itemscroller.config.generic.comment.villagerTradeListRememberScrollPosition": "啟用後,退出村民交易GUI將不會重置滾動條。", + "itemscroller.config.generic.comment.sortInventoryToggle": "Enables the Quick Inventory Sorting feature.\nThis is activated by hovering over any item,\nand then pressing the bound §a\"sortInventory\"§r key bind.", "itemscroller.config.generic.comment.sortAssumeEmptyBoxStacks": "整理物品欄時假設空界伏盒可以堆疊。\n當你在遠端伺服器啟用此特性時會很有用。\n這會多發送一個網路封包來確保伺服器同步", "itemscroller.config.generic.comment.sortShulkerBoxesAtEnd": "整理物品欄時將界伏盒放在最後。", + "itemscroller.config.generic.comment.sortShulkerBoxesInverted": "Sort shulker boxes so that the most-empty\nboxes are first, and the full boxes are last.", + "itemscroller.config.generic.comment.sortBundlesAtEnd": "Sort bundles at the end of the inventory\nwhen sorting the inventory.", + "itemscroller.config.generic.comment.sortBundlesInverted": "Sort bundles so that the most-empty\nbundles are first, and the full bundles are last.", + "itemscroller.config.generic.comment.sortTopPriorityInventory": "這些物品將優先排序。\n你可以輸入物品ID(例如 \"minecraft:diamond_sword\")或自定義名稱(例如 \"凋零殺手\")。\n如果輸入了自定義名稱,則會優先使用自定義名稱。\n列表中的物品將根據列表順序排在前面。", + "itemscroller.config.generic.comment.sortBottomPriorityInventory": "這些物品將最後排序。\n你可以輸入物品ID(例如 \"minecraft:diamond_sword\")或自定義名稱(例如 \"凋零殺手\")。\n如果輸入了自定義名稱,則會優先使用自定義名稱。\n列表中的物品將根據列表順序排在後面。", + "itemscroller.config.generic.comment.sortMethodDefault": "The default sorting method used to compare items.\n\n§6Category [ANY]§r = Sort by §a\"sortCategoryOrder\"§r first, and then followed by:\n§6Item Name§r - Sort by Items' Name\n§6Item Count§r - Sort by Item's Stack Size, then by RawID\n§6Item Rarity§r - Sort by Rarity, then by RawID\n§6Item RawID§r - Sort by RawID\n§d(RawID is the built-in Vanilla Item Index value)§r\n\n§cData Components are compared by Hash Code afterwards, and then\n§cby Item Stack Size after all other checks have passed.§r", + "itemscroller.config.generic.comment.sortCategoryOrder": "The Category order to be used for sorting items.\nThe Vanilla Creative Menu Categories are used.", "itemscroller.config.toggles.name.enableCraftingFeatures": "啟用配方視圖", "itemscroller.config.toggles.name.enableDropkeyDropMatching": "全部丟棄", @@ -143,6 +161,28 @@ "itemscroller.gui.label.trades": "交易", "itemscroller.gui.label.trade_uses": "交易使用: %d / %", + "itemscroller.gui.label.sorting_method.category_name": "Category Name", + "itemscroller.gui.label.sorting_method.category_count": "Category Count", + "itemscroller.gui.label.sorting_method.category_rarity": "Category Rarity", + "itemscroller.gui.label.sorting_method.category_rawid": "Category RawID", + "itemscroller.gui.label.sorting_method.item_name": "Item Name", + "itemscroller.gui.label.sorting_method.item_count": "Item Count", + "itemscroller.gui.label.sorting_method.item_rarity": "Item Rarity", + "itemscroller.gui.label.sorting_method.item_rawid": "Item Raw ID", + + "itemscroller.gui.label.sorting_category.building_blocks": "Building Blocks", + "itemscroller.gui.label.sorting_category.colored_blocks": "Colored Blocks", + "itemscroller.gui.label.sorting_category.natural_blocks": "Natural Blocks", + "itemscroller.gui.label.sorting_category.functional_blocks": "Functional Blocks", + "itemscroller.gui.label.sorting_category.redstone_blocks": "Redstone Items", + "itemscroller.gui.label.sorting_category.tools_and_utilities": "Tools and Utilities", + "itemscroller.gui.label.sorting_category.combat": "Combat Items", + "itemscroller.gui.label.sorting_category.food_and_drinks": "Food and Drinks", + "itemscroller.gui.label.sorting_category.ingredients": "Ingredients", + "itemscroller.gui.label.sorting_category.spawn_eggs": "Spawn Eggs", + "itemscroller.gui.label.sorting_category.op_blocks": "Operator Blocks", + "itemscroller.gui.label.sorting_category.other": "Other Items", + "itemscroller.gui.title.configs": "%s 配置 - %s", "itemscroller.message.toggled_mod_off": "Item Scroller 功能調整為:§c關閉",