diff --git a/src/generated/resources/data/anvilcraft/recipe/fast_cooking/cake_base_block.json b/src/generated/resources/data/anvilcraft/recipe/fast_cooking/cake_base_block.json index b5390b01b4..236fdfac61 100644 --- a/src/generated/resources/data/anvilcraft/recipe/fast_cooking/cake_base_block.json +++ b/src/generated/resources/data/anvilcraft/recipe/fast_cooking/cake_base_block.json @@ -5,7 +5,7 @@ "items": "#c:foods/dough" }, { - "items": "minecraft:egg" + "items": "#c:eggs" }, { "items": "minecraft:sugar" diff --git a/src/main/java/dev/dubhe/anvilcraft/block/entity/FluidTankBlockEntity.java b/src/main/java/dev/dubhe/anvilcraft/block/entity/FluidTankBlockEntity.java index 5d2420f54b..c580b13c75 100644 --- a/src/main/java/dev/dubhe/anvilcraft/block/entity/FluidTankBlockEntity.java +++ b/src/main/java/dev/dubhe/anvilcraft/block/entity/FluidTankBlockEntity.java @@ -164,6 +164,16 @@ public static boolean isEmptyItem(ItemStack stack, HolderLookup.Provider registr return FluidTankBlockEntity.readItemTank(stack, registries).getFluid().isEmpty(); } + public static FluidStack containedFluid(ItemStack stack, HolderLookup.Provider registries) { + if (!stack.is(ModBlocks.FLUID_TANK.asItem())) return FluidStack.EMPTY; + return FluidTankBlockEntity.readItemTank(stack, registries).getFluid(); + } + + public static int remainingCapacity(ItemStack stack, HolderLookup.Provider registries) { + if (!stack.is(ModBlocks.FLUID_TANK.asItem())) return 0; + return Math.max(0, BASE_CAPACITY - FluidTankBlockEntity.readItemTank(stack, registries).getFluidAmount()); + } + public static ItemStack fillItem(ItemStack stack, FluidStack fluid, HolderLookup.Provider registries) { if (fluid.isEmpty() || !FluidTankBlockEntity.isEmptyItem(stack, registries)) return ItemStack.EMPTY; diff --git a/src/main/java/dev/dubhe/anvilcraft/block/entity/LargeFluidTankBlockEntity.java b/src/main/java/dev/dubhe/anvilcraft/block/entity/LargeFluidTankBlockEntity.java index 1fd0fb5a1d..5e2bac6052 100644 --- a/src/main/java/dev/dubhe/anvilcraft/block/entity/LargeFluidTankBlockEntity.java +++ b/src/main/java/dev/dubhe/anvilcraft/block/entity/LargeFluidTankBlockEntity.java @@ -189,6 +189,17 @@ public static boolean isEmptyItem(ItemStack stack, HolderLookup.Provider registr return LargeFluidTankBlockEntity.readItemTank(stack, registries).getTotalAmount() == 0; } + public static List containedFluids(ItemStack stack, HolderLookup.Provider registries) { + if (!stack.is(ModBlocks.LARGE_FLUID_TANK.asItem())) return List.of(); + return LargeFluidTankBlockEntity.readItemTank(stack, registries).copyFluids(); + } + + public static int remainingCapacity(ItemStack stack, HolderLookup.Provider registries) { + if (!stack.is(ModBlocks.LARGE_FLUID_TANK.asItem())) return 0; + long stored = LargeFluidTankBlockEntity.readItemTank(stack, registries).getTotalAmount(); + return (int) Math.max(0, BASE_CAPACITY - stored); + } + public static ItemStack fillItem( ItemStack stack, List fluids, diff --git a/src/main/java/dev/dubhe/anvilcraft/data/recipe/FastCookingRecipeLoader.java b/src/main/java/dev/dubhe/anvilcraft/data/recipe/FastCookingRecipeLoader.java index 79d6884387..192da9bd99 100644 --- a/src/main/java/dev/dubhe/anvilcraft/data/recipe/FastCookingRecipeLoader.java +++ b/src/main/java/dev/dubhe/anvilcraft/data/recipe/FastCookingRecipeLoader.java @@ -21,7 +21,7 @@ public static void init(RegistrumRecipeProvider provider) { FastCookingRecipe.builder() .requires(items, ModItemTags.DOUGH) - .requires(Items.EGG) + .requires(items, ModItemTags.EGGS) .requires(Items.SUGAR) .result(ModBlocks.CAKE_BASE_BLOCK) .save(provider); diff --git a/src/main/java/dev/dubhe/anvilcraft/init/item/ModItemTags.java b/src/main/java/dev/dubhe/anvilcraft/init/item/ModItemTags.java index a3a0d73205..7c8b31ef4f 100644 --- a/src/main/java/dev/dubhe/anvilcraft/init/item/ModItemTags.java +++ b/src/main/java/dev/dubhe/anvilcraft/init/item/ModItemTags.java @@ -22,6 +22,7 @@ public class ModItemTags { public static final TagKey CREAM = ModItemTags.bindC("foods/cream"); public static final TagKey FLOUR = ModItemTags.bindC("foods/flour"); public static final TagKey DOUGH = ModItemTags.bindC("foods/dough"); + public static final TagKey EGGS = ModItemTags.bindC("eggs"); public static final TagKey RAW_MUTTON = ModItemTags.bindC("foods/raw_mutton"); public static final TagKey RAW_BEEF = ModItemTags.bindC("foods/raw_beef"); public static final TagKey RAW_CHICKEN = ModItemTags.bindC("foods/raw_chicken"); diff --git a/src/main/java/dev/dubhe/anvilcraft/rpc/StorageServerStub.java b/src/main/java/dev/dubhe/anvilcraft/rpc/StorageServerStub.java index 1d683e619a..0664b69dd9 100644 --- a/src/main/java/dev/dubhe/anvilcraft/rpc/StorageServerStub.java +++ b/src/main/java/dev/dubhe/anvilcraft/rpc/StorageServerStub.java @@ -17,9 +17,12 @@ import dev.dubhe.anvilcraft.api.itemhandler.unlimited.TypeLimitItemStacksResourceHandler; import dev.dubhe.anvilcraft.api.itemhandler.unlimited.UnlimitedItemStacksResourceHandler; import dev.dubhe.anvilcraft.block.container.storage.CrateBlock; +import dev.dubhe.anvilcraft.block.entity.FluidTankBlockEntity; +import dev.dubhe.anvilcraft.block.entity.LargeFluidTankBlockEntity; import dev.dubhe.anvilcraft.block.entity.storage.CrateBlockEntity; import dev.dubhe.anvilcraft.block.entity.storage.ShulkerContainerBlockEntity; import dev.dubhe.anvilcraft.block.entity.storage.StorageBlockEntity; +import dev.dubhe.anvilcraft.init.block.ModBlocks; import dev.dubhe.anvilcraft.init.item.ModComponents; import dev.dubhe.anvilcraft.item.TerminalItem; import dev.dubhe.anvilcraft.item.property.component.StorageRef; @@ -2040,6 +2043,11 @@ private static FluidOutcome takeFluidBucket(ServerPlayer player, StorageView vie return FluidOutcome.CHANGED; } } + // 指针上拿着储罐且没倒出东西:这一下的意图是「把流体灌进储罐」。 + // 储罐按一次倒满处理,与桶固定 1000 mB 的语义不同,故在装桶流程之前分流 + if (StorageServerStub.isFluidTankItem(carried)) { + return StorageServerStub.fillTankFromStorage(player, view, carried, fluid); + } FilledBucket filled = StorageServerStub.fillBucketFromStorage(player, view, fluid, false); if (filled.stack().isEmpty()) { return FluidOutcome.failed(filled.notice()); @@ -2283,6 +2291,9 @@ private static boolean consumeEmptyContainer( private static int pourIntoFluidPort(ServerPlayer player, StorageView view, ItemStack stack, int maxAmount) { if (stack.isEmpty() || maxAmount <= 0) return 0; + if (StorageServerStub.isFluidTankItem(stack)) { + return StorageServerStub.pourTankIntoFluidPort(player, view, stack) > 0 ? 1 : 0; + } PotionContents potion = stack.get(DataComponents.POTION_CONTENTS); boolean waterBottle = stack.is(Items.POTION) && potion != null && potion.is(Potions.WATER); FluidStack content = waterBottle ? new FluidStack(Fluids.WATER, FluidType.BUCKET_VOLUME / 4) @@ -2317,6 +2328,104 @@ private static int pourIntoFluidPort(ServerPlayer player, StorageView view, Item return poured; } + private static boolean isFluidTankItem(ItemStack stack) { + return stack.is(ModBlocks.FLUID_TANK.asItem()) || stack.is(ModBlocks.LARGE_FLUID_TANK.asItem()); + } + + private static List tankFluids(ItemStack stack, HolderLookup.Provider registries) { + return stack.is(ModBlocks.LARGE_FLUID_TANK.asItem()) + ? LargeFluidTankBlockEntity.containedFluids(stack, registries) + : List.of(FluidTankBlockEntity.containedFluid(stack, registries)); + } + + private static int tankRemainingCapacity(ItemStack stack, HolderLookup.Provider registries) { + return stack.is(ModBlocks.LARGE_FLUID_TANK.asItem()) + ? LargeFluidTankBlockEntity.remainingCapacity(stack, registries) + : FluidTankBlockEntity.remainingCapacity(stack, registries); + } + + private static int insertIntoTankItem(ItemStack stack, FluidStack fluid, HolderLookup.Provider registries) { + return stack.is(ModBlocks.LARGE_FLUID_TANK.asItem()) + ? LargeFluidTankBlockEntity.insertIntoItem(stack, fluid, registries) + : FluidTankBlockEntity.insertIntoItem(stack, fluid, registries); + } + + private static int drainFromTankItem(ItemStack stack, FluidStack fluid, HolderLookup.Provider registries) { + return stack.is(ModBlocks.LARGE_FLUID_TANK.asItem()) + ? LargeFluidTankBlockEntity.drainItem(stack, fluid, registries) + : FluidTankBlockEntity.drainItem(stack, fluid, registries); + } + + private static int pourTankIntoFluidPort(ServerPlayer player, StorageView view, ItemStack stack) { + if (stack.isEmpty()) return 0; + UUID storageId = view.primary().getId(); + HolderLookup.Provider registries = player.registryAccess(); + // 整叠储罐共享同一份方块实体数据,只能逐件处理:先拷出其中一件来倒, + // 倒完再让整叠减 1 并把倒空的罐按空桶的老规矩还回去 + ItemStack single = stack.copyWithCount(1); + List fluids = StorageServerStub.tankFluids(single, registries); + if (fluids.stream().allMatch(FluidStack::isEmpty)) return 0; + // 先按流体分别算出可倒量,确认有得倒再整体提交, + // 避免端口写入失败却已把罐里的流体扣掉 + List moved = new ArrayList<>(); + try (Transaction transaction = Transaction.openRoot()) { + for (FluidStack fluid : fluids) { + if (fluid.isEmpty()) continue; + var acceptor = StoragePortManager.findAcceptor(storageId, fluid); + if (acceptor == null) continue; + int inserted = acceptor.insert(FluidResource.of(fluid), fluid.getAmount(), transaction); + if (inserted <= 0) continue; + moved.add(fluid.copyWithAmount(inserted)); + } + if (moved.isEmpty()) return 0; + transaction.commit(); + } + // 提交成功后才把罐内数据扣掉:储罐物品没有 ITEM 流体能力, + // 只能借方块实体的静态方法直接改写 Tank 数据 + for (FluidStack fluid : moved) { + StorageServerStub.drainFromTankItem(single, fluid, registries); + } + StorageServerStub.playBucketSound(player, moved.getFirst(), SoundActions.BUCKET_EMPTY, false); + // 倒空的罐按空容器处理:进仓储或背包,指针上的那一件减少 1 + StorageServerStub.giveEmptiedContainerToStorage(view, player, single); + stack.shrink(1); + return moved.size(); + } + + private static FluidOutcome fillTankFromStorage( + ServerPlayer player, StorageView view, ItemStack carried, FluidStack fluid + ) { + if (carried.isEmpty()) return FluidOutcome.failed(FluidNotice.NONE); + HolderLookup.Provider registries = player.registryAccess(); + int space = StorageServerStub.tankRemainingCapacity(carried, registries); + if (space <= 0) return FluidOutcome.failed(FluidNotice.NONE); + UUID storageId = view.primary().getId(); + FluidEntry entry = StoragePortManager.find(storageId, fluid); + if (entry == null) return FluidOutcome.failed(FluidNotice.NONE); + int amount = Math.min(space, entry.amount()); + if (amount <= 0) return FluidOutcome.failed(FluidNotice.NOT_ENOUGH); + // 先在一件的副本上灌,拿到真实接收量;罐里已有别种流体且不相容时接收量为 0 + ItemStack filled = carried.copyWithCount(1); + int accepted = StorageServerStub.insertIntoTankItem(filled, entry.icon().copyWithAmount(amount), registries); + if (accepted <= 0) return FluidOutcome.failed(FluidNotice.NONE); + try (Transaction transaction = Transaction.openRoot()) { + if (StoragePortManager.drain(storageId, entry.icon(), accepted, transaction) < accepted) { + return FluidOutcome.failed(FluidNotice.NOT_ENOUGH); + } + transaction.commit(); + } + // 端口已抽出等量流体,副本也已灌好,直接决定成品去向 + if (carried.getCount() == 1) { + // 单件时直接把指针上的储罐换成灌好的那件,与装桶时把指针换成满桶一致 + player.inventoryMenu.setCarried(filled); + } else { + StorageServerStub.giveBucket(player, view, filled); + carried.shrink(1); + } + StorageServerStub.playBucketSound(player, entry.icon(), SoundActions.BUCKET_FILL, false); + return FluidOutcome.CHANGED; + } + private static boolean matchesFilters( Holder item, UnlimitedItemStack stack,