diff --git a/README.md b/README.md index 9a66ca1..5d974c1 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,9 @@ Licence : **GNU GPL 3.0 or Later** (`GPL-3.0-or-later`) Les descriptions FR/EN/RU sont déclarées dans chaque `fabric.mod.json` et dans les fichiers de langue. Les images fournies servent d'icônes Fabric de leur module. -Version courante du pack local : **`26.2.0-alpha.197`**. It's Alive passe en **`0.0.0-alpha.29`** et Sanctuary en **`0.0.0-alpha.113`** ; les huit autres modules restent inchangés. +Version courante du pack local : **`26.2.0-alpha.197`**. Sanctuary passe en **`0.0.0-alpha.114`** ; It's Alive reste à **`0.0.0-alpha.29`** et les huit autres modules restent inchangés. + +Le shift-clic de l’inventaire suit le comportement vanilla sur toutes les rangées débloquées : la hotbar alimente le stockage, rangées 5 et 6 comprises, et une rangée de stockage ou d’overflow n’alimente que la hotbar. L’alpha.197 ajoute un panneau communautaire de demandes de quêtes dans Sanctuary. Le demandeur renseigne son pseudonyme, un objectif, un type KILL/BRING/PAY, une quantité et une cible validée par le serveur ; un autre joueur prend la demande au clic droit et peut l’abandonner accroupi. Le panneau reste récupérable uniquement par son propriétaire ou un opérateur. La marmite It’s Alive regroupe désormais jusqu’à 64 objets strictement identiques dans une même pile logique et cuit un lot borné par l’ingrédient le plus rare et la capacité de sortie, afin de produire directement une pile de sel, de crème ou d’autres préparations. diff --git a/build.gradle b/build.gradle index edd2bb8..544e77a 100644 --- a/build.gradle +++ b/build.gradle @@ -3428,7 +3428,7 @@ tasks.register("verifyProgressivePauseMenuRelease") { "sanctuary/src/main/resources/assets/sanctuary/lang/${locale}.json"))] } if (rootProject.pack_version != "26.2.0-alpha.197" - || rootProject.sanctuary_version != "0.0.0-alpha.113" + || rootProject.sanctuary_version != "0.0.0-alpha.114" || rootProject.onlyfun_version != "0.0.0-alpha.19" || release.pack_version != rootProject.pack_version || index.versionId != rootProject.pack_version || migration.source?.modules != [sanctuary: "0.0.0-alpha.108", onlyfun: "0.0.0-alpha.17"] @@ -4123,7 +4123,7 @@ tasks.register("verifyShopPricingLayoutRelease") { def index = new JsonSlurper().parse(file("pack/prism/modrinth.index.json")) if (rootProject.pack_version != "26.2.0-alpha.197" || rootProject.onlyfun_version != "0.0.0-alpha.19" - || rootProject.sanctuary_version != "0.0.0-alpha.113" + || rootProject.sanctuary_version != "0.0.0-alpha.114" || release.pack_version != rootProject.pack_version || index.versionId != rootProject.pack_version || migration.source?.pack_version != "26.2.0-alpha.192" || migration.target?.pack_version != "26.2.0-alpha.193" @@ -4217,7 +4217,7 @@ tasks.register("verifyShopCsvBossGatesRelease") { def offerCatalog = file("sanctuary/src/main/java/fr/koka99cab/sanctuary26/sanctuary/shop/ShopOfferCatalog.java").text if (rootProject.pack_version != "26.2.0-alpha.197" || rootProject.onlyfun_version != "0.0.0-alpha.19" - || rootProject.sanctuary_version != "0.0.0-alpha.113" + || rootProject.sanctuary_version != "0.0.0-alpha.114" || release.pack_version != rootProject.pack_version || index.versionId != rootProject.pack_version || migration.source?.pack_version != "26.2.0-alpha.194" || migration.source?.modules != [onlyfun: "0.0.0-alpha.18", sanctuary: "0.0.0-alpha.110"] diff --git a/gradle.properties b/gradle.properties index 8c4b166..49a2a18 100644 --- a/gradle.properties +++ b/gradle.properties @@ -28,7 +28,7 @@ iliketomoveit_version=0.0.0-alpha.13 iliketomoveit_lifecycle=active onlyfun_version=0.0.0-alpha.19 onlyfun_lifecycle=active -sanctuary_version=0.0.0-alpha.113 +sanctuary_version=0.0.0-alpha.114 sanctuary_lifecycle=active pack_version=26.2.0-alpha.197 maven_group=fr.koka99cab.sanctuary26 diff --git a/sanctuary/INVENTORY_GUI.md b/sanctuary/INVENTORY_GUI.md index f5ad0c5..c8c1f63 100644 --- a/sanctuary/INVENTORY_GUI.md +++ b/sanctuary/INVENTORY_GUI.md @@ -177,9 +177,11 @@ Les implémentations de `quickMoveStack` utilisent des bornes correspondant aux Chaque famille doit connaître les quatre bornes décrites plus haut. Les règles sont : -- métier vers joueur : tenter les cases débloquées, overflow compris ; +- métier vers joueur : tenter les cases débloquées, overflow compris, en remplissant d'abord le stockage vanilla puis la première rangée overflow vide ; un coffre ne doit pas commencer par la dernière ligne rajoutée ; - joueur ou overflow vers métier : conserver les filtres du menu d'origine ; -- joueur vers joueur : respecter l'ordre hotbar, stockage, overflow ; +- joueur vers joueur : comme vanilla, la hotbar alimente toutes les rangées de stockage débloquées (slots 9–35 puis overflow) ; une rangée de stockage ou d'overflow n'alimente que la hotbar ; +- shift-clic à curseur vide : une seule pile, comme vanilla ; le glisser-shift continue de vider uniquement les cases traversées ; +- prendre un item puis shift-double-clic : toutes les piles identiques des rangées vanilla et overflow partent ensemble, que le double-clic parte d'une ligne vanilla ou d'une ligne rajoutée ; un coffre ne se mélange pas à l'inventaire joueur ; - une rangée verrouillée ne doit jamais recevoir un transfert automatique ; - le résultat d'un craft ou d'une machine conserve son traitement spécial et son appel à `onTake`. @@ -374,9 +376,12 @@ Tester au minimum : - clic simple dans les deux sens ; - shift-clic métier vers chaque type de rangée ; +- shift-clic hotbar vers les rangées 2 à 6 débloquées, overflow compris ; +- shift-clic d'une rangée de stockage ou d'overflow vers la hotbar uniquement ; - shift-clic overflow vers métier ; - glisser sur plusieurs slots ; - double-clic ; +- prendre un item puis shift-double-clic d'une pile vanilla ou overflow vers un coffre : les deux groupes de rangées partent ensemble ; - échange par touche 1–9 ; - drop d'une pile et d'un objet ; - fermeture avec une pile au curseur ; diff --git a/sanctuary/build.gradle b/sanctuary/build.gradle index 6267df7..c9684d3 100644 --- a/sanctuary/build.gradle +++ b/sanctuary/build.gradle @@ -2886,7 +2886,7 @@ tasks.register("verifyProgressivePauseMenu") { "pack/migrations/26.2.0-alpha.190-progressive-pause-menu-to-alpha.191.json")) def pauseMenu = file("src/main/java/fr/koka99cab/sanctuary26/sanctuary/mixin/client/PauseScreenMixin.java").text def costs = file("src/main/java/fr/koka99cab/sanctuary26/sanctuary/progression/SanctuaryProgressionCosts.java").text - if (project.version.toString() != "0.0.0-alpha.113" + if (project.version.toString() != "0.0.0-alpha.114" || migration.target?.pack_version != "26.2.0-alpha.191" || migration.target?.modules?.sanctuary != "0.0.0-alpha.109" || migration.pause_menu?.progression_full_width != true @@ -2924,7 +2924,7 @@ tasks.register("verifyShopPricingLayout") { "pack/migrations/26.2.0-alpha.192-shop-pricing-layout-to-alpha.193.json")) def screen = file("src/main/java/fr/koka99cab/sanctuary26/sanctuary/client/SanctuaryShopScreen.java").text def progression = file("src/main/java/fr/koka99cab/sanctuary26/sanctuary/shop/ShopProgression.java").text - if (project.version.toString() != "0.0.0-alpha.113" + if (project.version.toString() != "0.0.0-alpha.114" || migration.target?.modules?.sanctuary != "0.0.0-alpha.110" || migration.shop_layout?.locked_offer_buttons != 6 || migration.shop_layout?.button_slots != [4, 5, 6, 7, 8, 9] @@ -2955,7 +2955,7 @@ tasks.register("verifyShopCsvBossGates") { "pack/migrations/26.2.0-alpha.194-shop-csv-boss-gates-to-alpha.195.json")) def provider = file("src/main/java/fr/koka99cab/sanctuary26/sanctuary/shop/ShopCatalogProvider.java").text def catalog = file("src/main/java/fr/koka99cab/sanctuary26/sanctuary/shop/ShopOfferCatalog.java").text - if (project.version.toString() != "0.0.0-alpha.113" + if (project.version.toString() != "0.0.0-alpha.114" || migration.target?.pack_version != "26.2.0-alpha.195" || migration.target?.modules?.sanctuary != "0.0.0-alpha.111" || migration.compatibility?.sanctuary_data_version != 11 @@ -2986,7 +2986,7 @@ tasks.register("verifyDeliveryBoxOrientation") { def blockstate = new JsonSlurper().parse( file("src/main/resources/assets/sanctuary/blockstates/delivery_box.json")) def expectedVariants = ["facing=north", "facing=east", "facing=south", "facing=west"].toSet() - if (project.version.toString() != "0.0.0-alpha.113" + if (project.version.toString() != "0.0.0-alpha.114" || migration.target?.modules?.sanctuary != "0.0.0-alpha.112" || migration.mailbox?.id != "sanctuary:delivery_box" || migration.mailbox?.horizontal_facing != true @@ -3002,6 +3002,38 @@ tasks.register("verifyDeliveryBoxOrientation") { } } +tasks.register("verifyInventoryShiftClick") { + group = "verification" + description = "Checks Sanctuary alpha.114 native shift-click across every unlocked inventory row." + inputs.file(file("src/main/java/fr/koka99cab/sanctuary26/sanctuary/gameplay/SanctuaryInventoryTransfer.java")) + inputs.file(file("src/main/java/fr/koka99cab/sanctuary26/sanctuary/mixin/AbstractContainerMenuMixin.java")) + inputs.file(file("src/main/java/fr/koka99cab/sanctuary26/sanctuary/mixin/client/AbstractContainerScreenMixin.java")) + inputs.file(file("INVENTORY_GUI.md")) + doLast { + def transfer = file("src/main/java/fr/koka99cab/sanctuary26/sanctuary/gameplay/SanctuaryInventoryTransfer.java").text + def mixin = file("src/main/java/fr/koka99cab/sanctuary26/sanctuary/mixin/AbstractContainerMenuMixin.java").text + def screen = file("src/main/java/fr/koka99cab/sanctuary26/sanctuary/mixin/client/AbstractContainerScreenMixin.java").text + def gui = file("INVENTORY_GUI.md").text + if (project.version.toString() != "0.0.0-alpha.114" + || !transfer.contains("shouldAppendOverflow") + || !transfer.contains("isVanillaStorageRange") + || !transfer.contains("hotbarStart") + || !transfer.contains("sameInventoryGroup") + || !transfer.contains("groupingContainer") + || !mixin.contains("SanctuaryInventoryTransfer.shouldAppendOverflow") + || !mixin.contains('sanctuary$overflowEnd') + || !mixin.contains("SanctuaryInventoryTransfer.hotbarStart") + || !mixin.contains('sanctuary$isHotbarIndex') + || !screen.contains("sanctuary\$quickMoveMatchingStorage") + || !screen.contains("SanctuaryInventoryTransfer.sameInventoryGroup") + || !gui.contains("la hotbar alimente toutes les rangées de stockage débloquées") + || !gui.contains("prendre un item puis shift-double-clic") + || !gui.contains("première rangée overflow vide")) { + throw new GradleException("Sanctuary alpha.114 native inventory shift-click contract is incomplete") + } + } +} + tasks.register("verifyQuestRequests") { group = "verification" description = "Checks alpha.113 community quest-request persistence, networking and recovery." @@ -3028,7 +3060,7 @@ tasks.register("verifyQuestRequests") { new JsonSlurper().parse(file("src/main/resources/data/sanctuary/loot_table/blocks/${path}.json")) .pools[0].entries[0].name } - if (project.version.toString() != "0.0.0-alpha.113" + if (project.version.toString() != "0.0.0-alpha.114" || migration.target?.modules?.sanctuary != "0.0.0-alpha.113" || migration.quest_requests?.save_key != "quest_requests" || migration.quest_requests?.data_version != 1 @@ -3054,6 +3086,10 @@ tasks.register("verifyQuestRequests") { } } +tasks.named("verifySanctuary") { + dependsOn tasks.named("verifyInventoryShiftClick") +} + tasks.named("check") { dependsOn tasks.named("verifySanctuary") dependsOn tasks.named("verifyDeliveryBoxOrientation") @@ -3061,3 +3097,4 @@ tasks.named("check") { dependsOn tasks.named("celestialGraphSmoke") dependsOn tasks.named("questRequestModelSmoke") } + diff --git a/sanctuary/src/main/java/fr/koka99cab/sanctuary26/sanctuary/gameplay/SanctuaryInventoryTransfer.java b/sanctuary/src/main/java/fr/koka99cab/sanctuary26/sanctuary/gameplay/SanctuaryInventoryTransfer.java new file mode 100644 index 0000000..48a9669 --- /dev/null +++ b/sanctuary/src/main/java/fr/koka99cab/sanctuary26/sanctuary/gameplay/SanctuaryInventoryTransfer.java @@ -0,0 +1,103 @@ +package fr.koka99cab.sanctuary26.sanctuary.gameplay; + +import net.minecraft.world.Container; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.inventory.Slot; + +/** + * Native player-to-player shift-click ranges, including Sanctuary overflow rows. + * + *
Vanilla {@code InventoryMenu} moves the hotbar into slots 9–35 and storage + * back into the hotbar. Overflow is extra storage, so it joins the 9–35 target + * and never receives leftovers from a hotbar-only destination.
+ */ +public final class SanctuaryInventoryTransfer { + private SanctuaryInventoryTransfer() { + } + + /** Exclusive end of the 27 vanilla storage slots. */ + public static int vanillaStorageEnd(int playerInventoryStart) { + return playerInventoryStart + + (SanctuaryInventoryCapacity.VANILLA_ROW_COUNT - 1) + * SanctuaryInventoryCapacity.ROW_SIZE; + } + + /** Inclusive start of the nine hotbar slots in the recorded vanilla range. */ + public static int hotbarStart(int playerInventoryEnd) { + return playerInventoryEnd - SanctuaryInventoryCapacity.ROW_SIZE; + } + + public static boolean isFullPlayerRange(int startIndex, int endIndex, + int playerInventoryStart, int playerInventoryEnd) { + return startIndex == playerInventoryStart && endIndex == playerInventoryEnd; + } + + public static boolean isVanillaStorageRange(int startIndex, int endIndex, + int playerInventoryStart) { + return startIndex == playerInventoryStart + && endIndex == vanillaStorageEnd(playerInventoryStart); + } + + public static boolean isHotbarRange(int startIndex, int endIndex, + int playerInventoryEnd) { + return startIndex == hotbarStart(playerInventoryEnd) + && endIndex == playerInventoryEnd; + } + + /** + * Destinations that vanilla uses to fill player storage. Overflow rows must + * be appended so every unlocked line behaves like a vanilla inventory row. + */ + public static boolean shouldAppendOverflow(int startIndex, int endIndex, + int playerInventoryStart, int playerInventoryEnd) { + return isFullPlayerRange(startIndex, endIndex, playerInventoryStart, playerInventoryEnd) + || isVanillaStorageRange(startIndex, endIndex, playerInventoryStart); + } + + /** + * Chests call {@code moveItemStackTo} from the first player slot to + * {@code slots.size()} with reverse order. That range includes overflow, so + * vanilla would fill the last extra row before the first empty storage slot. + */ + public static boolean shouldAppendOverflow(int startIndex, int endIndex, + int playerInventoryStart, int playerInventoryEnd, int overflowEnd) { + if (shouldAppendOverflow(startIndex, endIndex, playerInventoryStart, playerInventoryEnd)) { + return true; + } + return startIndex == playerInventoryStart + && overflowEnd > playerInventoryEnd + && endIndex >= overflowEnd; + } + + /** + * Vanilla shift-double-click only moves slots that share the exact same + * {@link Container}. Overflow is extra player storage, so it must group with + * {@link Inventory} without pulling chest or equipment-only containers. + */ + public static boolean isPlayerStorage(Container container) { + return container instanceof Inventory || container instanceof SanctuaryOverflowContainer; + } + + /** + * Identity used by vanilla's shift-double-click container comparison. + * Overflow is rewritten to the player's {@link Inventory} so both row + * groups dump together into a chest. + */ + public static Container groupingContainer(Container container, Inventory inventory) { + if (container instanceof SanctuaryOverflowContainer && inventory != null) { + return inventory; + } + return container; + } + + public static boolean sameInventoryGroup(Container first, Container second) { + if (first == null || second == null) return false; + if (first == second) return true; + return isPlayerStorage(first) && isPlayerStorage(second); + } + + public static boolean sameInventoryGroup(Slot first, Slot second) { + return first != null && second != null + && sameInventoryGroup(first.container, second.container); + } +} diff --git a/sanctuary/src/main/java/fr/koka99cab/sanctuary26/sanctuary/mixin/AbstractContainerMenuMixin.java b/sanctuary/src/main/java/fr/koka99cab/sanctuary26/sanctuary/mixin/AbstractContainerMenuMixin.java index 9770233..ce18aba 100644 --- a/sanctuary/src/main/java/fr/koka99cab/sanctuary26/sanctuary/mixin/AbstractContainerMenuMixin.java +++ b/sanctuary/src/main/java/fr/koka99cab/sanctuary26/sanctuary/mixin/AbstractContainerMenuMixin.java @@ -4,6 +4,7 @@ import fr.koka99cab.sanctuary26.sanctuary.gameplay.SanctuaryExtendedInventoryMen import fr.koka99cab.sanctuary26.sanctuary.gameplay.SanctuaryCompanionEggSlot; import fr.koka99cab.sanctuary26.sanctuary.gameplay.SanctuaryCapeSlot; import fr.koka99cab.sanctuary26.sanctuary.gameplay.SanctuaryInventoryCapacity; +import fr.koka99cab.sanctuary26.sanctuary.gameplay.SanctuaryInventoryTransfer; import fr.koka99cab.sanctuary26.sanctuary.gameplay.SanctuaryOverflowContainer; import fr.koka99cab.sanctuary26.sanctuary.gameplay.SanctuaryOverflowAccess; import fr.koka99cab.sanctuary26.sanctuary.gameplay.SanctuaryOverflowSlot; @@ -70,24 +71,45 @@ abstract class AbstractContainerMenuMixin implements SanctuaryExtendedInventoryM * Extends hard-coded vanilla player targets while preserving their ordering. * The two calls are intentionally separate: Crafter adds its result slot after * the player inventory, so a single contiguous range would cross that slot. + * + *Hotbar shift-clicks target storage only (end exclusive of the hotbar). + * That range must include overflow so every unlocked row behaves like vanilla. + * Storage shift-clicks target the hotbar only and must not spill into overflow.
*/ @Inject(method = "moveItemStackTo", at = @At("HEAD"), cancellable = true) private void sanctuary$extendVanillaPlayerTarget(ItemStack stack, int startIndex, int endIndex, boolean reverseDirection, CallbackInfoReturnable