fix(sanctuary): compter rubis et saphirs aux rangs inventaire 4/5 et 5/5 #10

Merged
koka merged 2 commits from fix/shop-currency-overflow-rows into main 2026-08-27 10:00:08 +00:00
5 changed files with 79 additions and 21 deletions
+7
View File
@@ -74,6 +74,7 @@ tasks.register("verifyBlackMarket") {
inputs.file(file("src/main/java/fr/koka99cab/sanctuary26/sanctuary/shop/QuestBoardGenerator.java"))
inputs.file(file("src/main/java/fr/koka99cab/sanctuary26/sanctuary/shop/QuestBoardService.java"))
inputs.file(file("src/main/java/fr/koka99cab/sanctuary26/sanctuary/shop/QuestBoardTier.java"))
inputs.file(file("src/main/java/fr/koka99cab/sanctuary26/sanctuary/api/inventory/SanctuaryPlayerInventoryView.java"))
doLast {
def data = file("src/main/java/fr/koka99cab/sanctuary26/sanctuary/shop/BlackMarketData.java").text
def service = file("src/main/java/fr/koka99cab/sanctuary26/sanctuary/shop/BlackMarketService.java").text
@@ -81,6 +82,7 @@ tasks.register("verifyBlackMarket") {
def shop = file("src/main/java/fr/koka99cab/sanctuary26/sanctuary/shop/ShopService.java").text
def questData = file("src/main/java/fr/koka99cab/sanctuary26/sanctuary/shop/QuestBoardData.java").text
def quests = file("src/main/java/fr/koka99cab/sanctuary26/sanctuary/shop/QuestBoardService.java").text
def inventoryView = file("src/main/java/fr/koka99cab/sanctuary26/sanctuary/api/inventory/SanctuaryPlayerInventoryView.java").text
if (!data.contains('SAVE_KEY = "black_market"') || !data.contains("DATA_VERSION = 2")
|| !data.contains("ItemStack.CODEC.fieldOf(\"item\")") || !data.contains("replaceListing")
|| !service.contains("LISTING_FEE_RUBIES = 1") || !service.contains("COMMISSION_PERCENT = 5")
@@ -88,6 +90,11 @@ tasks.register("verifyBlackMarket") {
|| !service.contains("loadedDeliveryBox") || !service.contains("PARCEL_RETENTION_MILLIS")
|| !policy.contains("DataComponents.CONTAINER") || !policy.contains("DataComponents.BUNDLE_CONTENTS")
|| !shop.contains("public static final boolean OPEN = true")
|| !shop.contains("BlackMarketService.countPlayerItem")
|| !shop.contains("BlackMarketService.removePlayerItem")
|| shop.contains("getContainerSize()")
|| !inventoryView.contains("getNonEquipmentItems")
|| !service.contains("currencyStacks")
|| !questData.contains('SAVE_KEY = "black_market_quests"')
|| !quests.contains("ServerLivingEntityEvents.AFTER_DEATH")
|| !quests.contains("QuestBoardTier.ACTIVATION_COST")
@@ -33,8 +33,10 @@ public final class SanctuaryPlayerInventoryView {
List<SlotReference> slots = new ArrayList<>(rows * SanctuaryInventoryCapacity.ROW_SIZE);
Inventory inventory = player.getInventory();
int vanillaSlots = Math.min(rows, SanctuaryInventoryCapacity.VANILLA_ROW_COUNT)
* SanctuaryInventoryCapacity.ROW_SIZE;
int vanillaSlots = Math.min(
Math.min(rows, SanctuaryInventoryCapacity.VANILLA_ROW_COUNT)
* SanctuaryInventoryCapacity.ROW_SIZE,
inventory.getNonEquipmentItems().size());
for (int slot = 0; slot < vanillaSlots; slot++) {
slots.add(new SlotReference(inventory, slot, slot < SanctuaryInventoryCapacity.ROW_SIZE));
}
@@ -65,11 +67,16 @@ public final class SanctuaryPlayerInventoryView {
public record SlotReference(Container container, int slot, boolean hotbar) {
public ItemStack stack() {
if (container instanceof Inventory inventory && slot >= 0 && slot < Inventory.INVENTORY_SIZE) {
List<ItemStack> items = inventory.getNonEquipmentItems();
if (slot < items.size()) return items.get(slot);
}
return container.getItem(slot);
}
public ItemStack remove(int count) {
ItemStack removed = container.removeItem(slot, count);
ItemStack stored = stack();
ItemStack removed = stored.split(count);
container.setChanged();
return removed;
}
@@ -4,11 +4,16 @@ import fr.koka99cab.sanctuary26.sanctuary.SanctuaryMod;
import fr.koka99cab.sanctuary26.sanctuary.api.inventory.SanctuaryPlayerInventoryView;
import fr.koka99cab.sanctuary26.sanctuary.network.BlackMarketCreatePayload;
import fr.koka99cab.sanctuary26.sanctuary.network.BlackMarketEditorPayload;
import fr.koka99cab.sanctuary26.sanctuary.gameplay.SanctuaryOverflowAccess;
import fr.koka99cab.sanctuary26.sanctuary.gameplay.SanctuaryOverflowContainer;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.time.Duration;
import java.util.ArrayList;
import java.util.HexFormat;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
@@ -29,8 +34,11 @@ import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.permissions.Permissions;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.Container;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.inventory.Slot;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.entity.SignBlockEntity;
@@ -345,21 +353,58 @@ public final class BlackMarketService {
}
private static int countCurrency(ServerPlayer player, Item item) { return countPlayerItem(player, item); }
static int countPlayerItem(ServerPlayer player, Item item) {
if (item == null) return 0;
long count = 0L;
for (var slot : SanctuaryPlayerInventoryView.unlockedRows(player))
if (slot.stack().is(item)) count += slot.stack().getCount();
for (ItemStack stack : currencyStacks(player)) {
if (stack.is(item)) count += stack.getCount();
}
return (int) Math.min(Integer.MAX_VALUE, count);
}
private static void removeCurrency(ServerPlayer player, Item item, int amount) { removePlayerItem(player, item, amount); }
static void removePlayerItem(ServerPlayer player, Item item, int amount) {
int remaining = amount;
for (var slot : SanctuaryPlayerInventoryView.unlockedRows(player)) {
for (ItemStack stack : currencyStacks(player)) {
if (remaining <= 0) break;
ItemStack stack = slot.stack();
if (!stack.is(item)) continue;
int removed = Math.min(remaining, stack.getCount());
stack.shrink(removed); remaining -= removed; slot.markChanged();
stack.shrink(removed);
remaining -= removed;
}
player.getInventory().setChanged();
SanctuaryOverflowAccess.of(player).setChanged();
}
/**
* Rank 4 (5 rows) puts the second-to-last visual bank on vanilla slots 27-35;
* rank 5 (6 rows) puts it on overflow. Count both the 26.2 main stacks and the
* inventory menu so those banks match what the player sees.
*/
private static List<ItemStack> currencyStacks(ServerPlayer player) {
IdentityHashMap<ItemStack, Boolean> seen = new IdentityHashMap<>();
List<ItemStack> stacks = new ArrayList<>();
for (Slot slot : player.inventoryMenu.slots) {
if (!countsAsPlayerStorage(slot)) continue;
remember(stacks, seen, slot.getItem());
}
for (var slot : SanctuaryPlayerInventoryView.unlockedRows(player)) {
remember(stacks, seen, slot.stack());
}
remember(stacks, seen, SanctuaryPlayerInventoryView.offhand(player).stack());
return stacks;
}
private static boolean countsAsPlayerStorage(Slot slot) {
Container container = slot.container;
if (container instanceof SanctuaryOverflowContainer) return slot.isActive();
if (!(container instanceof Inventory)) return false;
int index = slot.getContainerSlot();
return index >= 0 && index < Inventory.INVENTORY_SIZE;
}
private static void remember(List<ItemStack> stacks, IdentityHashMap<ItemStack, Boolean> seen,
ItemStack stack) {
if (stack == null || stack.isEmpty() || seen.put(stack, Boolean.TRUE) != null) return;
stacks.add(stack);
}
private static void writeSign(ServerLevel level, SignBlockEntity sign, BlackMarketData.Listing listing) {
SignText text = new SignText()
@@ -237,24 +237,13 @@ public final class ShopService {
}
private static Item currency(Identifier id) { return BuiltInRegistries.ITEM.getOptional(id).orElse(null); }
private static int countCurrency(ServerPlayer player, Item item) {
if (item == null) return 0;
int total = 0;
for (int slot = 0; slot < player.getInventory().getContainerSize(); slot++) {
ItemStack stack = player.getInventory().getItem(slot); if (stack.is(item)) total += stack.getCount();
}
return total;
return item == null ? 0 : BlackMarketService.countPlayerItem(player, item);
}
private static boolean hasCurrency(ServerPlayer player, Item item, int count) {
return countCurrency(player, item) >= count;
}
private static void removeCurrency(ServerPlayer player, Item item, int count) {
int remaining = count;
for (int slot = 0; slot < player.getInventory().getContainerSize() && remaining > 0; slot++) {
ItemStack stack = player.getInventory().getItem(slot);
if (!stack.is(item)) continue;
int removed = Math.min(remaining, stack.getCount()); stack.shrink(removed); remaining -= removed;
}
player.getInventory().setChanged();
BlackMarketService.removePlayerItem(player, item, count);
}
private static void celebrate(ServerLevel level, BlockPos pos, java.util.UUID owner, ItemStack parcel) {
level.playSound(null, pos, SoundEvents.PLAYER_LEVELUP, SoundSource.BLOCKS, 0.65F, 1.35F);
@@ -1,5 +1,7 @@
package fr.koka99cab.sanctuary26.sanctuary.shop;
import fr.koka99cab.sanctuary26.sanctuary.gameplay.SanctuaryInventoryCapacity;
import fr.koka99cab.sanctuary26.sanctuary.progression.SanctuaryStat;
import net.minecraft.util.RandomSource;
public final class ShopDeliveryTimingSmoke {
@@ -35,5 +37,13 @@ public final class ShopDeliveryTimingSmoke {
|| ShopProgression.slotCost(10) != -1) {
throw new AssertionError("Shop capacity bounds are invalid");
}
if (SanctuaryInventoryCapacity.VANILLA_ROW_COUNT != 4
|| SanctuaryInventoryCapacity.MAX_ROW_COUNT != 6) {
throw new AssertionError("Shop currency counting must include overflow rows 5-6");
}
if (SanctuaryStat.INVENTORY.valueAt(4) != 5.0
|| SanctuaryStat.INVENTORY.valueAt(5) != 6.0) {
throw new AssertionError("Inventory ranks 4/5 and 5/5 no longer map to 5 and 6 rows");
}
}
}