diff --git a/CHANGELOG.md b/CHANGELOG.md index c80aff8..d8e4038 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ Ce journal suit les **versions du modpack**. Lorsqu'un seul module change, sa ve - Depuis `26.2.0-alpha.6`, le pack et les modules ont des versions indépendantes. I Like To Move It et Only Fun deviennent les cinquième et sixième modules actifs dans l'alpha 46. - Les versions ci-dessous ont été reconstituées à partir des JAR archivés, des packs locaux et du code source actuel. Les dates n'étant pas enregistrées de manière fiable, elles ne sont pas inventées. +## Non publié + +Module modifié : **Red-Stoner `0.0.0-alpha.16`**. + +- ajoute au terminal les tris alphabétique, par quantité croissante et par quantité décroissante, appliqués explicitement sans déplacer les objets sous le curseur lors des rafraîchissements périodiques ; + ## `26.2.0-alpha.228` Modules modifiés : **Sanctuary `0.0.0-alpha.132`** et **Red-Stoner `0.0.0-alpha.15`**. diff --git a/README.md b/README.md index 6fcb106..c4ef3b5 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,8 @@ Les descriptions FR/EN/RU sont déclarées dans chaque `fabric.mod.json` et dans Version courante du pack distribué : **`26.2.0-alpha.228`**. Cette release distribue Red-Stoner **`0.0.0-alpha.15`** et Sanctuary **`0.0.0-alpha.132`** ; les huit autres modules conservent leur version issue de l'alpha.227. +Le prochain module Red-Stoner **`0.0.0-alpha.16`** ajoute au terminal les tris alphabétique, par quantité croissante et par quantité décroissante. Le tri ne change que sur demande explicite et les rafraîchissements périodiques conservent l’ordre ainsi que les objets sous le curseur. + L’alpha.228 reconstruit la Backroom sur cinq niveaux empilés entre Y -64 et Y 383. Le monde devient entièrement noir et sans monstres ; l’arrivée est signalée par une torche récupérable. Les nouveaux chunks composent seuils modulaires, bibliothèques et bureaux, ville dense avec autoroutes et parcs, couloirs techniques, puis une grille 3D et un labyrinthe connecté dont certains trous mènent au vide. Red-Stoner corrige aussi le focus du terminal et permet de retrouver une boîte de Shulker par son contenu. Les chunks déjà générés et les sauvegardes existantes restent compatibles. L’alpha.227 ajoute la dimension persistante `sanctuary:backroom`. On y entre accroupi devant un lit et on n’en ressort que par un lit. L’équipement d’entrée est transféré atomiquement vers la Mailbox, tandis que les objets perdus ou abandonnés dans la dimension alimentent des coffres renouvelables sans duplication. Les anciennes sauvegardes sont migrées vers Sanctuary data v14 sans réinitialisation ; le protocole réseau reste en version 24. @@ -44,7 +46,7 @@ L’alpha.220 transforme le terminal de stockage en véritable menu de conteneur L’alpha.219 porte l'île finie Minecraft Indev dans `sanctuary:alpha` : 128 × 128 × 64 blocs déterministes, plages, cavernes, minerais, fluides, végétation et maison historique, le tout borné sans chunks parasites. Le cadre 4 × 5 en cobblestone moussue est maintenant actif et crée une paire persistante à une arrivée sûre hors de la maison. Ambiance peut enfin composer localement Golden Days Base puis Alpha uniquement dans cette dimension, sans redistribuer leurs textures et en restaurant exactement la pile précédente. Voir le [contrat Alpha](sanctuary/ALPHA.md), le [contrat des portails](iliketomoveit/PORTALS.md) et le [guide Golden Days](ambiance/GOLDEN_DAYS.md). -L’alpha.218 ajoute le terminal de stockage Red-Stoner : cette demi-dalle verticale se fixe sur un coffre, un coffre piégé ou un baril et donne accès, par pages défilantes de 54 types, à un réseau contigu borné à 128 conteneurs chargés. Les retraits et dépôts sont validés par le serveur, les composants d’items restent strictement séparés et le terminal endgame est exclu du Shop, du Black Market et des récompenses Only Fun. La recherche retrouve aussi une boîte de Shulker d’après son contenu direct ; la boîte reste affichée dans la grille afin que son aperçu vanilla soit consultable sans modifier les règles de retrait. +L’alpha.218 ajoute le terminal de stockage Red-Stoner : cette demi-dalle verticale se fixe sur un coffre, un coffre piégé ou un baril et donne accès, par pages défilantes de 54 types, à un réseau contigu borné à 128 conteneurs chargés. Les retraits et dépôts sont validés par le serveur, les composants d’items restent strictement séparés et le terminal endgame est exclu du Shop, du Black Market et des récompenses Only Fun. La recherche retrouve aussi une boîte de Shulker d’après son contenu direct ; la boîte reste affichée dans la grille afin que son aperçu vanilla soit consultable sans modifier les règles de retrait. Un bouton permet enfin de trier explicitement les résultats par nom, quantité croissante ou quantité décroissante. L’alpha.217 tentait de compenser la hauteur trop grave du disque blanc par un facteur OpenAL de `4/3`. Cette compensation est retirée par l’alpha.221, qui corrige directement la conversion stéréo vers mono. diff --git a/build.gradle b/build.gradle index 97e477f..90409e2 100644 --- a/build.gradle +++ b/build.gradle @@ -3692,7 +3692,7 @@ tasks.register("verifyMirrorLavenderDrawerRelease") { def drawerRecipe = file("redstoner/src/main/resources/data/redstoner/recipe/drawer.json") if (rootProject.pack_version != "26.2.0-alpha.228" || rootProject.anotherworld_version != "0.0.0-alpha.57" - || rootProject.redstoner_version != "0.0.0-alpha.15" + || rootProject.redstoner_version != "0.0.0-alpha.16" || release.pack_version != rootProject.pack_version || index.versionId != rootProject.pack_version || migration.source?.pack_version != "26.2.0-alpha.191" || migration.source?.modules != [anotherworld: "0.0.0-alpha.48", redstoner: "0.0.0-alpha.9"] @@ -4294,6 +4294,10 @@ project(":redstoner") { "screen.redstoner.storage_terminal.search", "screen.redstoner.storage_terminal.no_results", "screen.redstoner.storage_terminal.summary", + "screen.redstoner.storage_terminal.sort", + "screen.redstoner.storage_terminal.sort.alphabetical", + "screen.redstoner.storage_terminal.sort.count_ascending", + "screen.redstoner.storage_terminal.sort.count_descending", "screen.redstoner.storage_terminal.error.invalid_terminal", "screen.redstoner.storage_terminal.error.disconnected", "screen.redstoner.storage_terminal.error.too_large", @@ -4310,7 +4314,7 @@ project(":redstoner") { "src/main/resources/data/redstoner/recipe/controller.json")) def terminalRecipe = new JsonSlurper().parse(file( "src/main/resources/data/redstoner/recipe/storage_terminal.json")) - if (project.version.toString() != "0.0.0-alpha.15" + if (project.version.toString() != "0.0.0-alpha.16" || manifest.custom?.sanctuary26?.data_version != 3 || manifest.custom?.sanctuary26?.network_protocol != 7 || terminalMigration.source?.pack_version != "26.2.0-alpha.217" @@ -6659,7 +6663,7 @@ tasks.register("verifyAlpha218Release") { "file = \"mods/${id}-${version}.jar\"" } if (rootProject.pack_version != "26.2.0-alpha.228" - || rootProject.redstoner_version != "0.0.0-alpha.15" + || rootProject.redstoner_version != "0.0.0-alpha.16" || currentModules.findAll { id, version -> id != "redstoner" && id != "sanctuary" && id != "onlyfun" && id != "iliketomoveit" }.any { @@ -6848,7 +6852,7 @@ tasks.register("verifyAlpha220Release") { def networking = file( "redstoner/src/main/java/fr/koka99cab/sanctuary26/redstoner/network/RedStonerNetworking.java").text if (rootProject.pack_version != "26.2.0-alpha.228" - || rootProject.redstoner_version != "0.0.0-alpha.15" + || rootProject.redstoner_version != "0.0.0-alpha.16" || migration.source?.pack_version != "26.2.0-alpha.219" || migration.source?.modules != [redstoner: "0.0.0-alpha.11"] || migration.target?.pack_version != "26.2.0-alpha.220" @@ -6883,7 +6887,7 @@ tasks.register("verifyAlpha220Release") { || !inventory.contains("for (Container container : containers)") || !screen.contains("extends AbstractContainerScreen") || !screen.contains("textures/gui/container/generic_54.png") - || screen.contains("Button.builder") + || screen.contains('Button.builder(Component.translatable("gui.done")') || !networking.contains("PROTOCOL_VERSION = 7") || networking.contains("storage_terminal_snapshot") || networking.contains("storage_terminal_action") @@ -7077,7 +7081,7 @@ tasks.register("verifyAlpha222Release") { it.path == "mods/jei-26.2-fabric-30.29.0.199.jar" } if (rootProject.pack_version != "26.2.0-alpha.228" - || rootProject.redstoner_version != "0.0.0-alpha.15" + || rootProject.redstoner_version != "0.0.0-alpha.16" || rootProject.sanctuary_version != "0.0.0-alpha.132" || migration.source?.pack_version != "26.2.0-alpha.221" || migration.source?.modules != [ @@ -7191,6 +7195,40 @@ tasks.register("verifyStorageTerminalSearchFocus") { } } +tasks.register("verifyStorageTerminalSorting") { + group = "verification" + description = "Checks the three server-authoritative storage-terminal sorting modes and client control." + inputs.file(file( + "redstoner/src/main/java/fr/koka99cab/sanctuary26/redstoner/storage/StorageTerminalSort.java")) + inputs.file(file( + "redstoner/src/main/java/fr/koka99cab/sanctuary26/redstoner/storage/StorageTerminalMenu.java")) + inputs.file(file( + "redstoner/src/main/java/fr/koka99cab/sanctuary26/redstoner/client/gui/StorageTerminalScreen.java")) + inputs.file(file( + "redstoner/src/test/java/fr/koka99cab/sanctuary26/redstoner/storage/StorageTerminalSmoke.java")) + doLast { + def sort = file( + "redstoner/src/main/java/fr/koka99cab/sanctuary26/redstoner/storage/StorageTerminalSort.java").text + def menu = file( + "redstoner/src/main/java/fr/koka99cab/sanctuary26/redstoner/storage/StorageTerminalMenu.java").text + def screen = file( + "redstoner/src/main/java/fr/koka99cab/sanctuary26/redstoner/client/gui/StorageTerminalScreen.java").text + def smoke = file( + "redstoner/src/test/java/fr/koka99cab/sanctuary26/redstoner/storage/StorageTerminalSmoke.java").text + if (!sort.contains('ALPHABETICAL("alphabetical")') + || !sort.contains('COUNT_ASCENDING("count_ascending")') + || !sort.contains('COUNT_DESCENDING("count_descending")') + || !menu.contains("SORT_BUTTON_BASE") + || !menu.contains("sortMode.order(") + || !screen.contains("button -> requestSort()") + || !screen.contains("sortMode = sortMode.next()") + || !smoke.contains("verifySorting()") + || !smoke.contains("periodic quantity refresh")) { + throw new GradleException("The storage-terminal sorting regression contract is incomplete") + } + } +} + tasks.register("verifyAlpha223Release") { group = "verification" description = "Checks the Sanctuary 26.2.0-alpha.223 inventory-sort and quest-tracker display release." @@ -7503,7 +7541,7 @@ tasks.register("verifyAlpha225Release") { def workspaceModules = moduleIds.collectEntries { id -> [(id): project(":${id}").version.toString()] } - def releasedModules = workspaceModules + def releasedModules = workspaceModules + [redstoner: "0.0.0-alpha.15"] def requiredJars = releasedModules.collect { id, version -> "file = \"mods/${id}-${version}.jar\"" } @@ -7663,7 +7701,7 @@ tasks.register("verifyAlpha226Release") { def workspaceModules = moduleIds.collectEntries { id -> [(id): project(":${id}").version.toString()] } - def releasedModules = workspaceModules + def releasedModules = workspaceModules + [redstoner: "0.0.0-alpha.15"] def requiredJars = releasedModules.collect { id, version -> "file = \"mods/${id}-${version}.jar\"" } @@ -7754,7 +7792,7 @@ tasks.register("verifyAlpha227Release") { def workspaceModules = moduleIds.collectEntries { id -> [(id): project(":${id}").version.toString()] } - def releasedModules = workspaceModules + def releasedModules = workspaceModules + [redstoner: "0.0.0-alpha.15"] def requiredJars = releasedModules.collect { id, version -> "file = \"mods/${id}-${version}.jar\"" } @@ -7852,11 +7890,12 @@ tasks.register("verifyAlpha228Release") { def workspaceModules = moduleIds.collectEntries { id -> [(id): project(":${id}").version.toString()] } - def requiredJars = workspaceModules.collect { id, version -> + def releasedModules = workspaceModules + [redstoner: "0.0.0-alpha.15"] + def requiredJars = releasedModules.collect { id, version -> "file = \"mods/${id}-${version}.jar\"" } if (rootProject.pack_version != "26.2.0-alpha.228" - || rootProject.redstoner_version != "0.0.0-alpha.15" + || rootProject.redstoner_version != "0.0.0-alpha.16" || rootProject.sanctuary_version != "0.0.0-alpha.132" || migration.status != "released" || migration.source?.pack_version != "26.2.0-alpha.227" @@ -7928,6 +7967,7 @@ tasks.named("verifyWorkspace") { dependsOn tasks.named("verifyAlpha221Release") dependsOn tasks.named("verifyAlpha222Release") dependsOn tasks.named("verifyStorageTerminalSearchFocus") + dependsOn tasks.named("verifyStorageTerminalSorting") dependsOn tasks.named("verifyAlpha223Release") dependsOn tasks.named("verifyAlpha224Release") dependsOn tasks.named("verifyAlpha225Release") diff --git a/gradle.properties b/gradle.properties index b07a5d7..93792b1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,7 +14,7 @@ mod_version=0.0.0-alpha.0 module_lifecycle=standby ambiance_version=0.0.0-alpha.22 ambiance_lifecycle=active -redstoner_version=0.0.0-alpha.15 +redstoner_version=0.0.0-alpha.16 redstoner_lifecycle=active anotherworld_version=0.0.0-alpha.57 anotherworld_lifecycle=active diff --git a/redstoner/src/main/java/fr/koka99cab/sanctuary26/redstoner/client/gui/StorageTerminalScreen.java b/redstoner/src/main/java/fr/koka99cab/sanctuary26/redstoner/client/gui/StorageTerminalScreen.java index b4b5adb..fa5bc26 100644 --- a/redstoner/src/main/java/fr/koka99cab/sanctuary26/redstoner/client/gui/StorageTerminalScreen.java +++ b/redstoner/src/main/java/fr/koka99cab/sanctuary26/redstoner/client/gui/StorageTerminalScreen.java @@ -2,12 +2,15 @@ package fr.koka99cab.sanctuary26.redstoner.client.gui; import fr.koka99cab.sanctuary26.redstoner.storage.StorageTerminalMenu; import fr.koka99cab.sanctuary26.redstoner.storage.StorageTerminalRules; +import fr.koka99cab.sanctuary26.redstoner.storage.StorageTerminalSort; import fr.koka99cab.sanctuary26.redstoner.storage.StorageTerminalStatus; import java.util.ArrayList; import java.util.List; import net.minecraft.ChatFormatting; import net.minecraft.client.gui.GuiGraphicsExtractor; +import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.EditBox; +import net.minecraft.client.gui.components.Tooltip; import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; import net.minecraft.client.input.KeyEvent; import net.minecraft.client.input.MouseButtonEvent; @@ -28,8 +31,10 @@ public final class StorageTerminalScreen extends AbstractContainerScreen requestSort()) + .bounds(leftPos + 108, topPos + 2, 34, 14) + .tooltip(Tooltip.create(sortTooltip())) + .build()); } @Override @@ -151,6 +160,28 @@ public final class StorageTerminalScreen extends AbstractContainerScreen "A→Z"; + case COUNT_ASCENDING -> "1↑"; + case COUNT_DESCENDING -> "1↓"; + }); + } + + private Component sortTooltip() { + return Component.translatable("screen.redstoner.storage_terminal.sort", + Component.translatable("screen.redstoner.storage_terminal.sort." + + sortMode.translationSuffix())); + } + private void searchChanged(String value) { String previous = sentSearchValue; searchValue = value; diff --git a/redstoner/src/main/java/fr/koka99cab/sanctuary26/redstoner/storage/StorageTerminalMenu.java b/redstoner/src/main/java/fr/koka99cab/sanctuary26/redstoner/storage/StorageTerminalMenu.java index 839cfb9..0490663 100644 --- a/redstoner/src/main/java/fr/koka99cab/sanctuary26/redstoner/storage/StorageTerminalMenu.java +++ b/redstoner/src/main/java/fr/koka99cab/sanctuary26/redstoner/storage/StorageTerminalMenu.java @@ -27,6 +27,7 @@ public final class StorageTerminalMenu extends AbstractContainerMenu { public static final int PAGE_BUTTON_BASE = 10; public static final int SEARCH_CLEAR_BUTTON = 100; public static final int SEARCH_BACKSPACE_BUTTON = 101; + public static final int SORT_BUTTON_BASE = 200; public static final int SEARCH_CODEPOINT_BUTTON_BASE = 1_000; private static final int PLAYER_INVENTORY_Y = 140; private static final int REFRESH_INTERVAL = 20; @@ -49,6 +50,7 @@ public final class StorageTerminalMenu extends AbstractContainerMenu { private final StorageTerminalLayout layout = new StorageTerminalLayout(); private List entries = List.of(); private String searchQuery = ""; + private StorageTerminalSort sortMode = StorageTerminalSort.ALPHABETICAL; private int refreshCooldown = REFRESH_INTERVAL; public StorageTerminalMenu(int containerId, Inventory inventory) { @@ -131,6 +133,9 @@ public final class StorageTerminalMenu extends AbstractContainerMenu { if (searchQuery.isEmpty()) return true; searchQuery = searchQuery.substring(0, searchQuery.offsetByCodePoints(searchQuery.length(), -1)); + } else if (buttonId >= SORT_BUTTON_BASE + && buttonId < SORT_BUTTON_BASE + StorageTerminalSort.values().length) { + sortMode = StorageTerminalSort.values()[buttonId - SORT_BUTTON_BASE]; } else { int codePoint = buttonId - SEARCH_CODEPOINT_BUTTON_BASE; if (!isSearchCodePoint(codePoint)) return false; @@ -211,7 +216,7 @@ public final class StorageTerminalMenu extends AbstractContainerMenu { if (serverPlayer == null || terminalPos == null) return; discovery = StorageTerminalNetwork.discover(serverPlayer, terminalPos); List liveEntries = - StorageTerminalNetwork.filteredEntries(discovery, searchQuery); + sortMode.order(StorageTerminalNetwork.filteredEntries(discovery, searchQuery)); layout.update(liveEntries, resetOrder); int page = discovery.status() == StorageTerminalStatus.READY diff --git a/redstoner/src/main/java/fr/koka99cab/sanctuary26/redstoner/storage/StorageTerminalSort.java b/redstoner/src/main/java/fr/koka99cab/sanctuary26/redstoner/storage/StorageTerminalSort.java new file mode 100644 index 0000000..2c3ee02 --- /dev/null +++ b/redstoner/src/main/java/fr/koka99cab/sanctuary26/redstoner/storage/StorageTerminalSort.java @@ -0,0 +1,59 @@ +package fr.koka99cab.sanctuary26.redstoner.storage; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.Identifier; +import net.minecraft.world.item.ItemStack; + +/** Explicit server-side ordering modes for terminal entries. */ +public enum StorageTerminalSort { + ALPHABETICAL("alphabetical"), + COUNT_ASCENDING("count_ascending"), + COUNT_DESCENDING("count_descending"); + + private final String translationSuffix; + + StorageTerminalSort(String translationSuffix) { + this.translationSuffix = translationSuffix; + } + + public String translationSuffix() { + return translationSuffix; + } + + public StorageTerminalSort next() { + StorageTerminalSort[] values = values(); + return values[(ordinal() + 1) % values.length]; + } + + public List order( + List entries) { + List ordered = new ArrayList<>(entries); + ordered.sort(comparator()); + return List.copyOf(ordered); + } + + private Comparator comparator() { + Comparator alphabetical = Comparator + .comparing((StorageTerminalInventory.Entry entry) -> alphabeticalName(entry.stack())) + .thenComparing(entry -> itemId(entry.stack())); + return switch (this) { + case ALPHABETICAL -> alphabetical; + case COUNT_ASCENDING -> Comparator + .comparingInt(StorageTerminalInventory.Entry::total).thenComparing(alphabetical); + case COUNT_DESCENDING -> Comparator + .comparingInt(StorageTerminalInventory.Entry::total).reversed().thenComparing(alphabetical); + }; + } + + private static String alphabeticalName(ItemStack stack) { + return StorageTerminalSearch.normalize(stack.getHoverName().getString()); + } + + private static String itemId(ItemStack stack) { + Identifier id = BuiltInRegistries.ITEM.getKey(stack.getItem()); + return id == null ? "" : id.toString(); + } +} diff --git a/redstoner/src/main/resources/assets/redstoner/lang/en_us.json b/redstoner/src/main/resources/assets/redstoner/lang/en_us.json index 7de24e6..2f7ac08 100644 --- a/redstoner/src/main/resources/assets/redstoner/lang/en_us.json +++ b/redstoner/src/main/resources/assets/redstoner/lang/en_us.json @@ -55,6 +55,10 @@ "screen.redstoner.storage_terminal.search": "Search…", "screen.redstoner.storage_terminal.no_results": "No items found.", "screen.redstoner.storage_terminal.summary": "%s/%s · %s blocks · %s types", + "screen.redstoner.storage_terminal.sort": "Sort: %s", + "screen.redstoner.storage_terminal.sort.alphabetical": "alphabetical", + "screen.redstoner.storage_terminal.sort.count_ascending": "quantity, ascending", + "screen.redstoner.storage_terminal.sort.count_descending": "quantity, descending", "screen.redstoner.storage_terminal.error.invalid_terminal": "The terminal is no longer accessible.", "screen.redstoner.storage_terminal.error.disconnected": "No connected chest or barrel.", "screen.redstoner.storage_terminal.error.too_large": "Network too large (maximum 128 storage blocks).", diff --git a/redstoner/src/main/resources/assets/redstoner/lang/fr_fr.json b/redstoner/src/main/resources/assets/redstoner/lang/fr_fr.json index 6d4cac0..99742a3 100644 --- a/redstoner/src/main/resources/assets/redstoner/lang/fr_fr.json +++ b/redstoner/src/main/resources/assets/redstoner/lang/fr_fr.json @@ -55,6 +55,10 @@ "screen.redstoner.storage_terminal.search": "Rechercher…", "screen.redstoner.storage_terminal.no_results": "Aucun objet trouvé.", "screen.redstoner.storage_terminal.summary": "%s/%s · %s blocs · %s types", + "screen.redstoner.storage_terminal.sort": "Tri : %s", + "screen.redstoner.storage_terminal.sort.alphabetical": "alphabétique", + "screen.redstoner.storage_terminal.sort.count_ascending": "quantité croissante", + "screen.redstoner.storage_terminal.sort.count_descending": "quantité décroissante", "screen.redstoner.storage_terminal.error.invalid_terminal": "Le terminal n'est plus accessible.", "screen.redstoner.storage_terminal.error.disconnected": "Aucun coffre ou baril connecté.", "screen.redstoner.storage_terminal.error.too_large": "Réseau trop grand (128 stockages maximum).", diff --git a/redstoner/src/main/resources/assets/redstoner/lang/ru_ru.json b/redstoner/src/main/resources/assets/redstoner/lang/ru_ru.json index fec0373..7d77e70 100644 --- a/redstoner/src/main/resources/assets/redstoner/lang/ru_ru.json +++ b/redstoner/src/main/resources/assets/redstoner/lang/ru_ru.json @@ -55,6 +55,10 @@ "screen.redstoner.storage_terminal.search": "Поиск…", "screen.redstoner.storage_terminal.no_results": "Предметы не найдены.", "screen.redstoner.storage_terminal.summary": "%s/%s · блоков: %s · типов: %s", + "screen.redstoner.storage_terminal.sort": "Сортировка: %s", + "screen.redstoner.storage_terminal.sort.alphabetical": "по алфавиту", + "screen.redstoner.storage_terminal.sort.count_ascending": "по возрастанию количества", + "screen.redstoner.storage_terminal.sort.count_descending": "по убыванию количества", "screen.redstoner.storage_terminal.error.invalid_terminal": "Терминал больше недоступен.", "screen.redstoner.storage_terminal.error.disconnected": "Нет подключённых сундуков или бочек.", "screen.redstoner.storage_terminal.error.too_large": "Сеть слишком велика (не более 128 хранилищ).", diff --git a/redstoner/src/test/java/fr/koka99cab/sanctuary26/redstoner/storage/StorageTerminalSmoke.java b/redstoner/src/test/java/fr/koka99cab/sanctuary26/redstoner/storage/StorageTerminalSmoke.java index fd6c684..f48d6d5 100644 --- a/redstoner/src/test/java/fr/koka99cab/sanctuary26/redstoner/storage/StorageTerminalSmoke.java +++ b/redstoner/src/test/java/fr/koka99cab/sanctuary26/redstoner/storage/StorageTerminalSmoke.java @@ -27,6 +27,7 @@ public final class StorageTerminalSmoke { verifyPagination(); verifyAggregationAndTransfers(); verifySearch(); + verifySorting(); verifyStableLayout(); } @@ -149,6 +150,47 @@ public final class StorageTerminalSmoke { .set(DataComponents.MAX_STACK_SIZE, 64).build()); } + private static void verifySorting() { + ItemStack eclair = new ItemStack(stackable(Items.STONE)); + eclair.set(DataComponents.CUSTOM_NAME, Component.literal("Éclair")); + ItemStack banana = new ItemStack(stackable(Items.DIRT)); + banana.set(DataComponents.CUSTOM_NAME, Component.literal("Banane")); + ItemStack zebra = new ItemStack(stackable(Items.DIAMOND)); + zebra.set(DataComponents.CUSTOM_NAME, Component.literal("Zèbre")); + List entries = List.of( + new StorageTerminalInventory.Entry(eclair, 64), + new StorageTerminalInventory.Entry(banana, 8), + new StorageTerminalInventory.Entry(zebra, 2)); + + require(names(StorageTerminalSort.ALPHABETICAL.order(entries)) + .equals(List.of("Banane", "Éclair", "Zèbre")), + "Alphabetical terminal sorting ignored names or accent folding"); + require(names(StorageTerminalSort.COUNT_ASCENDING.order(entries)) + .equals(List.of("Zèbre", "Banane", "Éclair")), + "Ascending terminal quantity sorting is incorrect"); + require(names(StorageTerminalSort.COUNT_DESCENDING.order(entries)) + .equals(List.of("Éclair", "Banane", "Zèbre")), + "Descending terminal quantity sorting is incorrect"); + require(StorageTerminalSort.ALPHABETICAL.next() == StorageTerminalSort.COUNT_ASCENDING + && StorageTerminalSort.COUNT_ASCENDING.next() == StorageTerminalSort.COUNT_DESCENDING + && StorageTerminalSort.COUNT_DESCENDING.next() == StorageTerminalSort.ALPHABETICAL, + "Terminal sorting modes do not cycle deterministically"); + + StorageTerminalLayout stable = new StorageTerminalLayout(); + stable.update(StorageTerminalSort.COUNT_DESCENDING.order(entries), true); + List changedCounts = List.of( + new StorageTerminalInventory.Entry(eclair, 1), + new StorageTerminalInventory.Entry(banana, 80), + new StorageTerminalInventory.Entry(zebra, 2)); + stable.update(StorageTerminalSort.COUNT_DESCENDING.order(changedCounts), false); + require(names(stable.page(0).subList(0, 3)).equals(List.of("Éclair", "Banane", "Zèbre")), + "A periodic quantity refresh moved terminal entries under the cursor"); + } + + private static List names(List entries) { + return entries.stream().map(entry -> entry.stack().getHoverName().getString()).toList(); + } + private static void verifyStableLayout() { StorageTerminalInventory.Entry stone = new StorageTerminalInventory.Entry( new ItemStack(stackable(Items.STONE)), 64);