From dfee84670ff4f83cc70dd43769a25121da4448dc Mon Sep 17 00:00:00 2001 From: Pigeon_Nian Date: Thu, 20 Aug 2026 17:44:04 +0800 Subject: [PATCH 1/8] =?UTF-8?q?feat(fluid):=20=E6=96=B0=E5=A2=9E=E5=9B=9B?= =?UTF-8?q?=E7=A7=8D=E6=B0=94=E4=BD=93=E6=B5=81=E4=BD=93=E5=92=8C=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E6=A1=B6=E7=89=A9=E5=93=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增氢气、氧气、氦气和氘气四种气体流体类型及其流体实例 - 实现GasFluid类,定义气体流体的特殊行为和属性 - 新增对应的桶物品及其模型,支持气体流体的倒置渲染效果 - 在物品标签页和物品集合中添加新桶物品 - 更新流体注册和客户端流体类型注册,定义各气体颜色和特效 - 添加语言文件条目,支持新桶物品的显示名称 - 为新桶物品注册动态流体模型颜色处理器 --- .../assets/anvilcraft/lang/en_ud.json | 4 + .../assets/anvilcraft/lang/en_us.json | 4 + .../models/item/deuterium_bucket.json | 6 + .../anvilcraft/models/item/helium_bucket.json | 6 + .../models/item/hydrogen_bucket.json | 6 + .../anvilcraft/models/item/oxygen_bucket.json | 6 + .../resources/data/c/tags/item/buckets.json | 6 +- .../RegisterColorHandlersEventListener.java | 8 ++ .../dev/dubhe/anvilcraft/fluid/GasFluid.java | 83 ++++++++++++ .../anvilcraft/init/block/ModFluids.java | 120 ++++++++++++++++++ .../dubhe/anvilcraft/init/item/ModItems.java | 29 +++++ .../init/item/tabs/Ingredients.java | 4 + .../init/item/tabs/ItemsSections.java | 6 +- .../util/registrater/ModelProviderUtil.java | 11 +- 14 files changed, 296 insertions(+), 3 deletions(-) create mode 100644 src/generated/resources/assets/anvilcraft/models/item/deuterium_bucket.json create mode 100644 src/generated/resources/assets/anvilcraft/models/item/helium_bucket.json create mode 100644 src/generated/resources/assets/anvilcraft/models/item/hydrogen_bucket.json create mode 100644 src/generated/resources/assets/anvilcraft/models/item/oxygen_bucket.json create mode 100644 src/main/java/dev/dubhe/anvilcraft/fluid/GasFluid.java diff --git a/src/generated/resources/assets/anvilcraft/lang/en_ud.json b/src/generated/resources/assets/anvilcraft/lang/en_ud.json index 9f595874c5..9a345a18c9 100644 --- a/src/generated/resources/assets/anvilcraft/lang/en_ud.json +++ b/src/generated/resources/assets/anvilcraft/lang/en_ud.json @@ -988,6 +988,7 @@ "item.anvilcraft.cursed_golden_apple": "ǝꞁddⱯ uǝpꞁo⅁ pǝsɹnƆ", "item.anvilcraft.cyan_cement_bucket": "ʇǝʞɔnᗺ ʇuǝɯǝƆ uɐʎƆ", "item.anvilcraft.deformation_smithing_template": "ǝʇɐꞁdɯǝ⟘ ᵷuᴉɥʇᴉɯS uoᴉʇɐɯɹoɟǝᗡ", + "item.anvilcraft.deuterium_bucket": "ʇǝʞɔnᗺ ɯnᴉɹǝʇnǝᗡ", "item.anvilcraft.disk": "ʞsᴉᗡ", "item.anvilcraft.disk.stored_from": "%s :ɯoɹɟ pǝɹoʇS", "item.anvilcraft.dog_amulet": "ʇǝꞁnɯⱯ ᵷoᗡ", @@ -1042,6 +1043,8 @@ "item.anvilcraft.hardend_resin": "uᴉsǝᴚ puǝpɹɐH", "item.anvilcraft.heavy_halberd_core": "ǝɹoƆ pɹǝqꞁɐH ʎʌɐǝH", "item.anvilcraft.heliostats.pos_set": "%s ǝʇɐᴉpɐɹɹᴉ ꞁꞁᴉM", + "item.anvilcraft.helium_bucket": "ʇǝʞɔnᗺ ɯnᴉꞁǝH", + "item.anvilcraft.hydrogen_bucket": "ʇǝʞɔnᗺ uǝᵷoɹpʎH", "item.anvilcraft.hyperdimension_terminal": "ꞁɐuᴉɯɹǝ⟘ uoᴉsuǝɯᴉpɹǝdʎH", "item.anvilcraft.hyperdimension_terminal.bound": "uoᴉʇɐʇS ǝᵷɐɹoʇS uoᴉsuǝɯᴉpɹǝdʎH ɐ oʇ punoᗺ", "item.anvilcraft.hyperdimension_terminal.unbound": "puᴉq oʇ uoᴉʇɐʇS ǝᵷɐɹoʇS uoᴉsuǝɯᴉpɹǝdʎH ɐ ʞɔᴉꞁɔ-ʇɥᵷᴉᴚ", @@ -1072,6 +1075,7 @@ "item.anvilcraft.neutronium_ingot": "ʇoᵷuI ɯnᴉuoɹʇnǝN", "item.anvilcraft.oil_bucket": "ʇǝʞɔnᗺ ꞁᴉO", "item.anvilcraft.orange_cement_bucket": "ʇǝʞɔnᗺ ʇuǝɯǝƆ ǝᵷuɐɹO", + "item.anvilcraft.oxygen_bucket": "ʇǝʞɔnᗺ uǝᵷʎxO", "item.anvilcraft.penrose_sphere_component": "ʇuǝuodɯoƆ ǝɹǝɥdS ǝsoɹuǝԀ", "item.anvilcraft.permutation_smithing_template": "ǝʇɐꞁdɯǝ⟘ ᵷuᴉɥʇᴉɯS uoᴉʇɐʇnɯɹǝԀ", "item.anvilcraft.pill": "ꞁꞁᴉԀ", diff --git a/src/generated/resources/assets/anvilcraft/lang/en_us.json b/src/generated/resources/assets/anvilcraft/lang/en_us.json index 9881914221..11abd9f5a0 100644 --- a/src/generated/resources/assets/anvilcraft/lang/en_us.json +++ b/src/generated/resources/assets/anvilcraft/lang/en_us.json @@ -988,6 +988,7 @@ "item.anvilcraft.cursed_golden_apple": "Cursed Golden Apple", "item.anvilcraft.cyan_cement_bucket": "Cyan Cement Bucket", "item.anvilcraft.deformation_smithing_template": "Deformation Smithing Template", + "item.anvilcraft.deuterium_bucket": "Deuterium Bucket", "item.anvilcraft.disk": "Disk", "item.anvilcraft.disk.stored_from": "Stored from: %s", "item.anvilcraft.dog_amulet": "Dog Amulet", @@ -1042,6 +1043,8 @@ "item.anvilcraft.hardend_resin": "Hardend Resin", "item.anvilcraft.heavy_halberd_core": "Heavy Halberd Core", "item.anvilcraft.heliostats.pos_set": "Will irradiate %s", + "item.anvilcraft.helium_bucket": "Helium Bucket", + "item.anvilcraft.hydrogen_bucket": "Hydrogen Bucket", "item.anvilcraft.hyperdimension_terminal": "Hyperdimension Terminal", "item.anvilcraft.hyperdimension_terminal.bound": "Bound to a Hyperdimension Storage Station", "item.anvilcraft.hyperdimension_terminal.unbound": "Right-click a Hyperdimension Storage Station to bind", @@ -1072,6 +1075,7 @@ "item.anvilcraft.neutronium_ingot": "Neutronium Ingot", "item.anvilcraft.oil_bucket": "Oil Bucket", "item.anvilcraft.orange_cement_bucket": "Orange Cement Bucket", + "item.anvilcraft.oxygen_bucket": "Oxygen Bucket", "item.anvilcraft.penrose_sphere_component": "Penrose Sphere Component", "item.anvilcraft.permutation_smithing_template": "Permutation Smithing Template", "item.anvilcraft.pill": "Pill", diff --git a/src/generated/resources/assets/anvilcraft/models/item/deuterium_bucket.json b/src/generated/resources/assets/anvilcraft/models/item/deuterium_bucket.json new file mode 100644 index 0000000000..428db01b86 --- /dev/null +++ b/src/generated/resources/assets/anvilcraft/models/item/deuterium_bucket.json @@ -0,0 +1,6 @@ +{ + "parent": "neoforge:item/bucket_drip", + "flip_gas": true, + "fluid": "anvilcraft:deuterium", + "loader": "neoforge:fluid_container" +} \ No newline at end of file diff --git a/src/generated/resources/assets/anvilcraft/models/item/helium_bucket.json b/src/generated/resources/assets/anvilcraft/models/item/helium_bucket.json new file mode 100644 index 0000000000..2b7e1714f5 --- /dev/null +++ b/src/generated/resources/assets/anvilcraft/models/item/helium_bucket.json @@ -0,0 +1,6 @@ +{ + "parent": "neoforge:item/bucket_drip", + "flip_gas": true, + "fluid": "anvilcraft:helium", + "loader": "neoforge:fluid_container" +} \ No newline at end of file diff --git a/src/generated/resources/assets/anvilcraft/models/item/hydrogen_bucket.json b/src/generated/resources/assets/anvilcraft/models/item/hydrogen_bucket.json new file mode 100644 index 0000000000..14000c302a --- /dev/null +++ b/src/generated/resources/assets/anvilcraft/models/item/hydrogen_bucket.json @@ -0,0 +1,6 @@ +{ + "parent": "neoforge:item/bucket_drip", + "flip_gas": true, + "fluid": "anvilcraft:hydrogen", + "loader": "neoforge:fluid_container" +} \ No newline at end of file diff --git a/src/generated/resources/assets/anvilcraft/models/item/oxygen_bucket.json b/src/generated/resources/assets/anvilcraft/models/item/oxygen_bucket.json new file mode 100644 index 0000000000..3b1a79c9c8 --- /dev/null +++ b/src/generated/resources/assets/anvilcraft/models/item/oxygen_bucket.json @@ -0,0 +1,6 @@ +{ + "parent": "neoforge:item/bucket_drip", + "flip_gas": true, + "fluid": "anvilcraft:oxygen", + "loader": "neoforge:fluid_container" +} \ No newline at end of file diff --git a/src/generated/resources/data/c/tags/item/buckets.json b/src/generated/resources/data/c/tags/item/buckets.json index 6e504011e9..4a58be01c1 100644 --- a/src/generated/resources/data/c/tags/item/buckets.json +++ b/src/generated/resources/data/c/tags/item/buckets.json @@ -18,6 +18,10 @@ "anvilcraft:purple_cement_bucket", "anvilcraft:magenta_cement_bucket", "anvilcraft:pink_cement_bucket", - "anvilcraft:melt_gem_bucket" + "anvilcraft:melt_gem_bucket", + "anvilcraft:hydrogen_bucket", + "anvilcraft:oxygen_bucket", + "anvilcraft:helium_bucket", + "anvilcraft:deuterium_bucket" ] } \ No newline at end of file diff --git a/src/main/java/dev/dubhe/anvilcraft/event/RegisterColorHandlersEventListener.java b/src/main/java/dev/dubhe/anvilcraft/event/RegisterColorHandlersEventListener.java index 0907115f45..ff0159f1c8 100644 --- a/src/main/java/dev/dubhe/anvilcraft/event/RegisterColorHandlersEventListener.java +++ b/src/main/java/dev/dubhe/anvilcraft/event/RegisterColorHandlersEventListener.java @@ -4,6 +4,7 @@ import dev.dubhe.anvilcraft.block.RedstoneWireClientPowerCache; import dev.dubhe.anvilcraft.init.block.ModBlocks; import dev.dubhe.anvilcraft.init.item.ModFoodItems; +import dev.dubhe.anvilcraft.init.item.ModItems; import net.minecraft.core.component.DataComponents; import net.minecraft.util.FastColor; import net.minecraft.world.item.alchemy.PotionContents; @@ -11,6 +12,7 @@ import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.fml.common.EventBusSubscriber; import net.neoforged.neoforge.client.event.RegisterColorHandlersEvent; +import net.neoforged.neoforge.client.model.DynamicFluidContainerModel; @EventBusSubscriber(modid = AnvilCraft.MOD_ID) public class RegisterColorHandlersEventListener { @@ -40,5 +42,11 @@ public static void registerItemColorHandlersEvent(RegisterColorHandlersEvent.Ite return FastColor.ARGB32.opaque(potionContents.getColor()); } }, ModFoodItems.PILL); + + event.register(new DynamicFluidContainerModel.Colors(), + ModItems.HYDROGEN_BUCKET, + ModItems.OXYGEN_BUCKET, + ModItems.HELIUM_BUCKET, + ModItems.DEUTERIUM_BUCKET); } } diff --git a/src/main/java/dev/dubhe/anvilcraft/fluid/GasFluid.java b/src/main/java/dev/dubhe/anvilcraft/fluid/GasFluid.java new file mode 100644 index 0000000000..1392cff0b8 --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/fluid/GasFluid.java @@ -0,0 +1,83 @@ +package dev.dubhe.anvilcraft.fluid; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.level.material.FluidState; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.phys.shapes.VoxelShape; +import net.neoforged.neoforge.fluids.FluidType; + +public class GasFluid extends Fluid { + private final FluidType type; + + public GasFluid(FluidType type) { + this.type = type; + } + + @Override + public Item getBucket() { + return Items.AIR; + } + + @Override + protected boolean canBeReplacedWith(FluidState state, BlockGetter level, BlockPos pos, Fluid fluid, Direction direction) { + return false; + } + + @Override + protected Vec3 getFlow(BlockGetter reader, BlockPos pos, FluidState state) { + return Vec3.ZERO; + } + + @Override + public int getTickDelay(LevelReader level) { + return 0; + } + + @Override + protected float getExplosionResistance() { + return 100; + } + + @Override + public float getHeight(FluidState state, BlockGetter level, BlockPos pos) { + return 0; + } + + @Override + public float getOwnHeight(FluidState state) { + return 0; + } + + @Override + protected BlockState createLegacyBlock(FluidState state) { + return Blocks.AIR.defaultBlockState(); + } + + @Override + public boolean isSource(FluidState state) { + return true; + } + + @Override + public int getAmount(FluidState state) { + return 8; + } + + @Override + public VoxelShape getShape(FluidState state, BlockGetter level, BlockPos pos) { + return Blocks.AIR.defaultBlockState().getShape(level, pos); + } + + @Override + public FluidType getFluidType() { + return this.type; + } +} \ No newline at end of file diff --git a/src/main/java/dev/dubhe/anvilcraft/init/block/ModFluids.java b/src/main/java/dev/dubhe/anvilcraft/init/block/ModFluids.java index c008d75de3..62b1cd66d7 100644 --- a/src/main/java/dev/dubhe/anvilcraft/init/block/ModFluids.java +++ b/src/main/java/dev/dubhe/anvilcraft/init/block/ModFluids.java @@ -3,6 +3,7 @@ import dev.dubhe.anvilcraft.AnvilCraft; import dev.dubhe.anvilcraft.block.MeltGemFluid; import dev.dubhe.anvilcraft.block.state.Color; +import dev.dubhe.anvilcraft.fluid.GasFluid; import dev.dubhe.anvilcraft.fluid.HoneyFluid; import dev.dubhe.anvilcraft.fluid.LiquidEnchantmentFluid; import dev.dubhe.anvilcraft.fluid.PowderSnowFluid; @@ -109,6 +110,84 @@ public class ModFluids { .slopeFindDistance(3) .explosionResistance(100); + // === Gases === + + public static final FluidType HYDROGEN_FLUID_TYPE = createHydrogenType(); + public static final DeferredHolder HYDROGEN_TYPE = FLUID_TYPES.register( + "hydrogen", () -> ModFluids.HYDROGEN_FLUID_TYPE + ); + public static final DeferredHolder HYDROGEN = FLUIDS.register( + "hydrogen", () -> new GasFluid(ModFluids.HYDROGEN_FLUID_TYPE) + ); + + public static final FluidType OXYGEN_FLUID_TYPE = createOxygenType(); + public static final DeferredHolder OXYGEN_TYPE = FLUID_TYPES.register( + "oxygen", () -> ModFluids.OXYGEN_FLUID_TYPE + ); + public static final DeferredHolder OXYGEN = FLUIDS.register( + "oxygen", () -> new GasFluid(ModFluids.OXYGEN_FLUID_TYPE) + ); + + public static final FluidType HELIUM_FLUID_TYPE = createHeliumType(); + public static final DeferredHolder HELIUM_TYPE = FLUID_TYPES.register( + "helium", () -> ModFluids.HELIUM_FLUID_TYPE + ); + public static final DeferredHolder HELIUM = FLUIDS.register( + "helium", () -> new GasFluid(ModFluids.HELIUM_FLUID_TYPE) + ); + + public static final FluidType DEUTERIUM_FLUID_TYPE = createDeuteriumType(); + public static final DeferredHolder DEUTERIUM_TYPE = FLUID_TYPES.register( + "deuterium", () -> ModFluids.DEUTERIUM_FLUID_TYPE + ); + public static final DeferredHolder DEUTERIUM = FLUIDS.register( + "deuterium", () -> new GasFluid(ModFluids.DEUTERIUM_FLUID_TYPE) + ); + + private static FluidType createHydrogenType() { + return new FluidType(FluidType.Properties.create() + .descriptionId("block.anvilcraft.hydrogen") + .density(-1000) + .viscosity(100) + .fallDistanceModifier(0) + .sound(SoundActions.BUCKET_FILL, SoundEvents.BUCKET_FILL) + .sound(SoundActions.BUCKET_EMPTY, SoundEvents.BUCKET_EMPTY) + ); + } + + private static FluidType createOxygenType() { + return new FluidType(FluidType.Properties.create() + .descriptionId("block.anvilcraft.oxygen") + .density(-1000) + .viscosity(100) + .fallDistanceModifier(0) + .sound(SoundActions.BUCKET_FILL, SoundEvents.BUCKET_FILL) + .sound(SoundActions.BUCKET_EMPTY, SoundEvents.BUCKET_EMPTY) + ); + } + + private static FluidType createHeliumType() { + return new FluidType(FluidType.Properties.create() + .descriptionId("block.anvilcraft.helium") + .density(-1000) + .viscosity(100) + .fallDistanceModifier(0) + .sound(SoundActions.BUCKET_FILL, SoundEvents.BUCKET_FILL) + .sound(SoundActions.BUCKET_EMPTY, SoundEvents.BUCKET_EMPTY) + ); + } + + private static FluidType createDeuteriumType() { + return new FluidType(FluidType.Properties.create() + .descriptionId("block.anvilcraft.deuterium") + .density(-1000) + .viscosity(100) + .fallDistanceModifier(0) + .sound(SoundActions.BUCKET_FILL, SoundEvents.BUCKET_FILL) + .sound(SoundActions.BUCKET_EMPTY, SoundEvents.BUCKET_EMPTY) + ); + } + public static final Object2ObjectMap> CEMENT_TYPES = registerAllCementTypes(); public static final Object2ObjectMap> SOURCE_CEMENTS = registerAllSourceCement(); public static final Object2ObjectMap> FLOWING_CEMENTS = registerAllFlowingCement(); @@ -324,6 +403,47 @@ public static void onRegisterFluidType(RegisterClientExtensionsEvent e) { false ), OIL_TYPE ); + e.registerFluidType( + new ModClientFluidTypeExtensionImpl( + ResourceLocation.withDefaultNamespace("block/water_still"), + ResourceLocation.withDefaultNamespace("block/water_flow"), + 0xC9E4F7, + 2.0f, + 0xFFC9E4F7, + false + ), HYDROGEN_TYPE + ); + e.registerFluidType( + new ModClientFluidTypeExtensionImpl( + ResourceLocation.withDefaultNamespace("block/water_still"), + ResourceLocation.withDefaultNamespace("block/water_flow"), + 0x9CCCF8, + 2.0f, + 0xFF9CCCF8, + false + ), OXYGEN_TYPE + ); + e.registerFluidType( + new ModClientFluidTypeExtensionImpl( + ResourceLocation.withDefaultNamespace("block/water_still"), + ResourceLocation.withDefaultNamespace("block/water_flow"), + 0xF0C8E0, + 2.0f, + 0xFFF0C8E0, + false + ), HELIUM_TYPE + ); + e.registerFluidType( + new ModClientFluidTypeExtensionImpl( + ResourceLocation.withDefaultNamespace("block/water_still"), + ResourceLocation.withDefaultNamespace("block/water_flow"), + 0xA8E8DC, + 2.0f, + 0xFFA8E8DC, + false + ), DEUTERIUM_TYPE + ); + for (Color color : Color.values()) { e.registerFluidType( new ModClientFluidTypeExtensionImpl( diff --git a/src/main/java/dev/dubhe/anvilcraft/init/item/ModItems.java b/src/main/java/dev/dubhe/anvilcraft/init/item/ModItems.java index 42767e2590..fa70fc399d 100644 --- a/src/main/java/dev/dubhe/anvilcraft/init/item/ModItems.java +++ b/src/main/java/dev/dubhe/anvilcraft/init/item/ModItems.java @@ -1157,6 +1157,35 @@ private static ItemEntry createBigAmuletItem(String type, Suppli .model(ModelProviderUtil::bucket) .register(); + public static final ItemEntry HYDROGEN_BUCKET = REGISTRUM.item("hydrogen_bucket", ModItems.bucket(() -> ModFluids.HYDROGEN)) + .tag(Tags.Items.BUCKETS) + .lang("Hydrogen Bucket") + .properties(properties -> properties.stacksTo(1).craftRemainder(Items.BUCKET)) + .model(ModelProviderUtil::bucketGassy) + .register(); + + public static final ItemEntry OXYGEN_BUCKET = REGISTRUM.item("oxygen_bucket", ModItems.bucket(() -> ModFluids.OXYGEN)) + .tag(Tags.Items.BUCKETS) + .lang("Oxygen Bucket") + .properties(properties -> properties.stacksTo(1).craftRemainder(Items.BUCKET)) + .model(ModelProviderUtil::bucketGassy) + .register(); + + public static final ItemEntry HELIUM_BUCKET = REGISTRUM.item("helium_bucket", ModItems.bucket(() -> ModFluids.HELIUM)) + .tag(Tags.Items.BUCKETS) + .lang("Helium Bucket") + .properties(properties -> properties.stacksTo(1).craftRemainder(Items.BUCKET)) + .model(ModelProviderUtil::bucketGassy) + .register(); + + public static final ItemEntry DEUTERIUM_BUCKET = REGISTRUM + .item("deuterium_bucket", ModItems.bucket(() -> ModFluids.DEUTERIUM)) + .tag(Tags.Items.BUCKETS) + .lang("Deuterium Bucket") + .properties(properties -> properties.stacksTo(1).craftRemainder(Items.BUCKET)) + .model(ModelProviderUtil::bucketGassy) + .register(); + public static final ItemEntry PIPE = REGISTRUM.item("pipe", PipeBlockItem::new) .model((ctx, provider) -> provider .withExistingParent(ctx.getId().toString(), AnvilCraft.of("block/pipe")) diff --git a/src/main/java/dev/dubhe/anvilcraft/init/item/tabs/Ingredients.java b/src/main/java/dev/dubhe/anvilcraft/init/item/tabs/Ingredients.java index 465a76d091..e42c14f532 100644 --- a/src/main/java/dev/dubhe/anvilcraft/init/item/tabs/Ingredients.java +++ b/src/main/java/dev/dubhe/anvilcraft/init/item/tabs/Ingredients.java @@ -112,6 +112,10 @@ public void accept() { this.plain(ModItems.EXP_BUCKET); this.plain(ModItems.OIL_BUCKET); this.plain(ModItems.MELT_GEM_BUCKET); + this.plain(ModItems.HYDROGEN_BUCKET); + this.plain(ModItems.OXYGEN_BUCKET); + this.plain(ModItems.HELIUM_BUCKET); + this.plain(ModItems.DEUTERIUM_BUCKET); ModItems.CEMENT_BUCKETS.forEach((color, bucketItem) -> this.plain(bucketItem)); } } diff --git a/src/main/java/dev/dubhe/anvilcraft/init/item/tabs/ItemsSections.java b/src/main/java/dev/dubhe/anvilcraft/init/item/tabs/ItemsSections.java index 8c1c939d77..6d61158832 100644 --- a/src/main/java/dev/dubhe/anvilcraft/init/item/tabs/ItemsSections.java +++ b/src/main/java/dev/dubhe/anvilcraft/init/item/tabs/ItemsSections.java @@ -275,7 +275,11 @@ public void accept() { content.accept(ModItems.EXP_BUCKET); content.accept(ModItems.OIL_BUCKET); content.accept(ModItems.MELT_GEM_BUCKET); - ModItems.CEMENT_BUCKETS.forEach((color, bucketItem) -> content.accept(bucketItem)); + content.accept(ModItems.HYDROGEN_BUCKET); + content.accept(ModItems.OXYGEN_BUCKET); + content.accept(ModItems.HELIUM_BUCKET); + content.accept(ModItems.DEUTERIUM_BUCKET); + ModItems.CEMENT_BUCKETS.forEach((color, bucketItem) -> this.acceptFolded(content, bucketItem)); } ); } diff --git a/src/main/java/dev/dubhe/anvilcraft/util/registrater/ModelProviderUtil.java b/src/main/java/dev/dubhe/anvilcraft/util/registrater/ModelProviderUtil.java index 690de41100..8cbb94633f 100644 --- a/src/main/java/dev/dubhe/anvilcraft/util/registrater/ModelProviderUtil.java +++ b/src/main/java/dev/dubhe/anvilcraft/util/registrater/ModelProviderUtil.java @@ -25,8 +25,17 @@ public static void liquid(DataGenContext ctx, Regi } /** - * 用于流体的ItemModel生成器 + * Used for gaseous fluid buckets; the fluid is rendered upside-down in the bucket. */ + public static void bucketGassy(DataGenContext ctx, RegistrumItemModelProvider provider) { + provider.withExistingParent( + ctx.getName(), + ResourceLocation.parse("neoforge:item/bucket_drip") + ).customLoader((builder, helper) -> DynamicFluidContainerModelBuilder.begin(builder, helper) + .fluid(ctx.get().content) + .flipGas(true)); + } + public static void bucket(DataGenContext ctx, RegistrumItemModelProvider provider) { provider.withExistingParent( ctx.getName(), From ae0f06506e324792742173d4f3b0a2b6f0979563 Mon Sep 17 00:00:00 2001 From: Pigeon_Nian Date: Thu, 20 Aug 2026 19:32:10 +0800 Subject: [PATCH 2/8] =?UTF-8?q?feat(fluid):=20=E6=B7=BB=E5=8A=A0=E6=B0=94?= =?UTF-8?q?=E4=BD=93=E6=B5=81=E4=BD=93=E5=8F=8A=E6=B0=94=E5=8E=8B=E5=9D=87?= =?UTF-8?q?=E8=A1=A1=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增多种气体流体名称及对应中英文本地化 - 实现气体在流体管网内基于气压差的均衡分配算法 - 气压值由气体充填率与泵的压力偏置共同决定 - 气体流动不再依赖高度拟合的扬程,而由压强驱动 - 气体绘制支持全量显示,通过透明度表达气体填充比例 - 渲染时气体始终填满容器体积,区别于液体的高度变化 - 添加对气体流速限制及阈值判定的控制,避免过度计算 - 优化气体管网中的阀门流量限制与可达性判断逻辑 --- .../assets/anvilcraft/lang/en_ud.json | 4 + .../assets/anvilcraft/lang/en_us.json | 4 + .../api/fluid/network/FluidPipeNetwork.java | 233 ++++++++++++++++++ .../client/renderer/FluidTankRenderUtil.java | 14 +- .../LargeFluidTankBlockEntityRenderer.java | 17 +- .../client/support/FluidRenderHelper.java | 81 ++++++ .../dubhe/anvilcraft/data/lang/FluidLang.java | 6 + 7 files changed, 354 insertions(+), 5 deletions(-) diff --git a/src/generated/resources/assets/anvilcraft/lang/en_ud.json b/src/generated/resources/assets/anvilcraft/lang/en_ud.json index 9a345a18c9..62a556ac5d 100644 --- a/src/generated/resources/assets/anvilcraft/lang/en_ud.json +++ b/src/generated/resources/assets/anvilcraft/lang/en_ud.json @@ -442,6 +442,7 @@ "block.anvilcraft.deepslate_zinc_ore": "ǝɹO ɔuᴉZ ǝʇɐꞁsdǝǝᗡ", "block.anvilcraft.deflection_ring": "ᵷuᴉᴚ uoᴉʇɔǝꞁɟǝᗡ", "block.anvilcraft.detector_sliding_rail": "ꞁᴉɐᴚ ᵷuᴉpᴉꞁS ɹoʇɔǝʇǝᗡ", + "block.anvilcraft.deuterium": "ɯnᴉɹǝʇnǝᗡ", "block.anvilcraft.discharger": "ɹǝᵷɹɐɥɔsᴉᗡ", "block.anvilcraft.drain": "uᴉɐɹᗡ", "block.anvilcraft.earth_core_shard_block": "pɹɐɥS ǝɹoƆ ɥʇɹɐƎ ɟo ʞɔoꞁᗺ", @@ -501,9 +502,11 @@ "block.anvilcraft.heliostats": "sʇɐʇsoᴉꞁǝH", "block.anvilcraft.heliostats.invalid_placement": "ʇuǝɯǝɔɐꞁd pᴉꞁɐʌuI", "block.anvilcraft.heliostats.placement_no_pos": "ʇǝs ʇou uoᴉʇᴉsod uoᴉʇɐᴉpɐɹɹI", + "block.anvilcraft.helium": "ɯnᴉꞁǝH", "block.anvilcraft.hollow_magnet_block": "ʇǝuᵷɐW ɟo ʞɔoꞁᗺ pǝʍoꞁꞁoH", "block.anvilcraft.honey": "ʎǝuoH", "block.anvilcraft.honey_cauldron": "uoɹpꞁnɐƆ ʎǝuoH", + "block.anvilcraft.hydrogen": "uǝᵷoɹpʎH", "block.anvilcraft.hypercube": "ǝqnɔɹǝdʎH", "block.anvilcraft.hyperdimension_storage_station": "uoᴉʇɐʇS ǝᵷɐɹoʇS uoᴉsuǝɯᴉpɹǝdʎH", "block.anvilcraft.impact_pile": "ǝꞁᴉԀ ʇɔɐdɯI", @@ -561,6 +564,7 @@ "block.anvilcraft.overheated_ember_metal_block": "ꞁɐʇǝW ɹǝqɯƎ ɟo ʞɔoꞁᗺ pǝʇɐǝɥɹǝʌO", "block.anvilcraft.overseer": "ɹǝǝsɹǝʌO", "block.anvilcraft.oxidized_copper_pressure_plate": "ǝʇɐꞁԀ ǝɹnssǝɹԀ ɹǝddoƆ pǝzᴉpᴉxO", + "block.anvilcraft.oxygen": "uǝᵷʎxO", "block.anvilcraft.piezoelectric_crystal": "ꞁɐʇsʎɹƆ ɔᴉɹʇɔǝꞁǝozǝᴉԀ", "block.anvilcraft.pink_cement": "ʇuǝɯǝƆ ʞuᴉԀ", "block.anvilcraft.pink_cement_cauldron": "uoɹpꞁnɐƆ ʇuǝɯǝƆ ʞuᴉԀ", diff --git a/src/generated/resources/assets/anvilcraft/lang/en_us.json b/src/generated/resources/assets/anvilcraft/lang/en_us.json index 11abd9f5a0..078d2a8f36 100644 --- a/src/generated/resources/assets/anvilcraft/lang/en_us.json +++ b/src/generated/resources/assets/anvilcraft/lang/en_us.json @@ -442,6 +442,7 @@ "block.anvilcraft.deepslate_zinc_ore": "Deepslate Zinc Ore", "block.anvilcraft.deflection_ring": "Deflection Ring", "block.anvilcraft.detector_sliding_rail": "Detector Sliding Rail", + "block.anvilcraft.deuterium": "Deuterium", "block.anvilcraft.discharger": "Discharger", "block.anvilcraft.drain": "Drain", "block.anvilcraft.earth_core_shard_block": "Block of Earth Core Shard", @@ -501,9 +502,11 @@ "block.anvilcraft.heliostats": "Heliostats", "block.anvilcraft.heliostats.invalid_placement": "Invalid placement", "block.anvilcraft.heliostats.placement_no_pos": "Irradiation position not set", + "block.anvilcraft.helium": "Helium", "block.anvilcraft.hollow_magnet_block": "Hollowed Block of Magnet", "block.anvilcraft.honey": "Honey", "block.anvilcraft.honey_cauldron": "Honey Cauldron", + "block.anvilcraft.hydrogen": "Hydrogen", "block.anvilcraft.hypercube": "Hypercube", "block.anvilcraft.hyperdimension_storage_station": "Hyperdimension Storage Station", "block.anvilcraft.impact_pile": "Impact Pile", @@ -561,6 +564,7 @@ "block.anvilcraft.overheated_ember_metal_block": "Overheated Block of Ember Metal", "block.anvilcraft.overseer": "Overseer", "block.anvilcraft.oxidized_copper_pressure_plate": "Oxidized Copper Pressure Plate", + "block.anvilcraft.oxygen": "Oxygen", "block.anvilcraft.piezoelectric_crystal": "Piezoelectric Crystal", "block.anvilcraft.pink_cement": "Pink Cement", "block.anvilcraft.pink_cement_cauldron": "Pink Cement Cauldron", diff --git a/src/main/java/dev/dubhe/anvilcraft/api/fluid/network/FluidPipeNetwork.java b/src/main/java/dev/dubhe/anvilcraft/api/fluid/network/FluidPipeNetwork.java index 7d78acfbb5..0b905f1a9b 100644 --- a/src/main/java/dev/dubhe/anvilcraft/api/fluid/network/FluidPipeNetwork.java +++ b/src/main/java/dev/dubhe/anvilcraft/api/fluid/network/FluidPipeNetwork.java @@ -49,6 +49,16 @@ public class FluidPipeNetwork { public static final int MAX_SPEED = 2000; /** 达到流速上限所需的高度差(格)= MAX_SPEED / HEIGHT_RATE = 40 */ public static final int FULL_SPEED_HEIGHT = MAX_SPEED / HEIGHT_RATE; + /** 气体满罐时的压力标度:气体压力 = 填充率(0..1) × 该值 + 泵气压偏置。 */ + private static final int GAS_PRESSURE_SCALE = 1000; + /** 泵每提供 1 格"扬程"在气压上的折算增量(气体由气压差驱动,不再使用扬程)。 */ + private static final int GAS_PRESSURE_PER_LIFT = 100; + /** 单种气体单 tick 全网均衡的总移动预算(mB)。 */ + private static final int GAS_EQUILIBRIUM_BUDGET = MAX_SPEED; + /** 判定气压已均衡的差值阈值(低于该值不再转移)。 */ + private static final int GAS_PRESSURE_EPSILON = 1; + /** 气体均衡每 tick 的最大轮数,限制 O(n²) 遍历成本。 */ + private static final int GAS_MAX_ROUNDS = 8; /** * 按高度差计算流速(线性增长): @@ -168,6 +178,8 @@ public void tick() { } distributeFromSource(source); } + // 气体由压强驱动在网络内均衡,不走重力/扬程分配 + equilibrateGases(); } /** @@ -256,6 +268,223 @@ public void pushFromExternalSource( srcPos, entryPipePos, null, srcHandler, sourceEffectiveHeight, false, null)); } + /** + * Gas equalization: gas diffuses from higher-pressure tanks to lower-pressure tanks until + * the whole network balances. Gas is driven by pressure only (never by head/altitude); + * pressure is derived from fill ratio plus the pump's pressure bias. + */ + private void equilibrateGases() { + Set gasTypes = new HashSet<>(); + for (FluidEndpoint ep : endpoints) { + if (!isEndpointConnected(ep)) { + continue; + } + for (FluidStack stored : distinctFluidTypes(ep.handler())) { + if (!stored.isEmpty() && stored.getFluid().getFluidType().isLighterThanAir()) { + gasTypes.add(stored.copyWithAmount(1)); + } + } + } + for (FluidStack gasType : gasTypes) { + List candidates = gasCandidates(gasType); + if (candidates.size() < 2) { + continue; + } + equilibrateGasType(gasType, candidates); + } + } + + /** Endpoints that can hold this gas: current holders or containers with free slots. */ + private List gasCandidates(FluidStack fluidType) { + List candidates = new ArrayList<>(); + for (FluidEndpoint ep : endpoints) { + if (!isEndpointConnected(ep)) { + continue; + } + if (gasStorage(ep.handler(), fluidType)[1] > 0) { + candidates.add(ep); + } + } + return candidates; + } + + /** + * One pass over a single gas type: repeatedly take the highest-pressure reachable source and + * move gas to the lowest-pressure reachable target that still has room, until pressure + * differences converge or the global budget is exhausted. + */ + private void equilibrateGasType(FluidStack fluidType, List candidates) { + int globalBudget = GAS_EQUILIBRIUM_BUDGET; + for (int round = 0; round < GAS_MAX_ROUNDS && globalBudget > 0; round++) { + boolean progressed = false; + candidates.sort(Comparator + .comparingInt((FluidEndpoint e) -> gasPressure(e, fluidType)) + .reversed()); + for (int i = 0; i < candidates.size() && globalBudget > 0; i++) { + FluidEndpoint hi = candidates.get(i); + int[] hiSc = gasStorage(hi.handler(), fluidType); + if (hiSc[0] <= 0 || hiSc[1] <= 0) { + continue; + } + if (!canDrainFromEndpoint(hi)) { + continue; + } + FluidStack hiStored = matchingFluid(hi.handler(), fluidType); + if (hiStored.isEmpty()) { + continue; + } + int hiPressure = gasPressure(hi, fluidType); + Reachability reach = directionalConstraints + ? computeReachableCached(hi.fromPipePos(), hiStored) + : null; + Map> pathValves = reach == null ? Map.of() : reach.pathValves(); + FluidEndpoint bestTarget = null; + int bestDelta = 0; + for (int j = 0; j < candidates.size(); j++) { + if (i == j) { + continue; + } + FluidEndpoint lo = candidates.get(j); + if (lo.handler().equals(hi.handler())) { + continue; + } + if (reach != null && !isEndpointReachable(reach, lo)) { + continue; + } + if (minValveRemaining(pathValves.get(lo.fromPipePos())) <= 0) { + continue; + } + int[] loSc = gasStorage(lo.handler(), fluidType); + if (loSc[0] >= loSc[1]) { + continue; + } + int loPressure = gasPressure(lo, fluidType); + int delta = hiPressure - loPressure; + if (delta <= GAS_PRESSURE_EPSILON) { + continue; + } + if (delta > bestDelta) { + bestDelta = delta; + bestTarget = lo; + } + } + if (bestTarget == null) { + continue; + } + int moved = transferGas(hi, bestTarget, fluidType, bestDelta, pathValves, reach); + if (moved > 0) { + globalBudget -= moved; + progressed = true; + } + } + if (!progressed) { + break; + } + } + } + + /** + * Transfer driven by a single pressure difference: move exactly enough to equalize the two + * pressures, capped by flow rate, valve allowance, source stock and target free space. + */ + private int transferGas( + FluidEndpoint hi, FluidEndpoint lo, FluidStack fluidType, int pressureDiff, + Map> pathValves, @Nullable Reachability reach + ) { + int[] hiSc = gasStorage(hi.handler(), fluidType); + int[] loSc = gasStorage(lo.handler(), fluidType); + int hiStored = hiSc[0]; + int hiCap = hiSc[1]; + int loStored = loSc[0]; + int loCap = loSc[1]; + if (hiStored <= 0 || hiCap <= 0 || loCap <= 0 || loStored >= loCap) { + return 0; + } + int loFree = loCap - loStored; + int hiBias = (hi.effectiveHeight() - hi.containerPos().getY()) * GAS_PRESSURE_PER_LIFT; + int loBias = (lo.effectiveHeight() - lo.containerPos().getY()) * GAS_PRESSURE_PER_LIFT; + double num = (double) hiStored * loCap - (double) loStored * hiCap + - (double) (loBias - hiBias) * hiCap * loCap / GAS_PRESSURE_SCALE; + double x = num / (double) (hiCap + loCap); + int equalAmt = Math.max(0, (int) Math.floor(x)); + if (equalAmt <= 0) { + return 0; + } + int flowCap = gasFlowForPressureDiff(pressureDiff); + int valveLimit = minValveRemaining(pathValves.get(lo.fromPipePos())); + int want = Math.min(equalAmt, Math.min(flowCap, Math.min(hiStored, loFree))); + want = Math.min(want, valveLimit); + if (want <= 0) { + return 0; + } + FluidStack hiStoredStack = matchingFluid(hi.handler(), fluidType); + if (hiStoredStack.isEmpty()) { + return 0; + } + FluidStack toMove = hiStoredStack.copyWithAmount(want); + int filled = lo.handler().fill(toMove, IFluidHandler.FluidAction.SIMULATE); + if (filled <= 0) { + return 0; + } + FluidStack drained = hi.handler().drain( + hiStoredStack.copyWithAmount(filled), IFluidHandler.FluidAction.EXECUTE); + if (drained.isEmpty() || !FluidStack.isSameFluidSameComponents(drained, fluidType)) { + if (!drained.isEmpty()) { + hi.handler().fill(drained, IFluidHandler.FluidAction.EXECUTE); + } + return 0; + } + int actuallyFilled = lo.handler().fill(drained, IFluidHandler.FluidAction.EXECUTE); + if (actuallyFilled < drained.getAmount()) { + hi.handler().fill( + drained.copyWithAmount(drained.getAmount() - actuallyFilled), IFluidHandler.FluidAction.EXECUTE); + } + if (actuallyFilled > 0) { + deductValves(pathValves.get(lo.fromPipePos()), actuallyFilled); + showFluidAlongPipePath(drained, hi, lo, reach); + onTransferred(hi); + } + return actuallyFilled; + } + + /** Current stock and total capacity of this gas in a handler (empty slots count as capacity). */ + private static int[] gasStorage(IFluidHandler handler, FluidStack fluidType) { + int stored = 0; + int capacity = 0; + for (int i = 0; i < handler.getTanks(); i++) { + FluidStack tankFluid = handler.getFluidInTank(i); + if (!tankFluid.isEmpty() && FluidStack.isSameFluidSameComponents(tankFluid, fluidType)) { + stored += tankFluid.getAmount(); + capacity += handler.getTankCapacity(i); + } else if (tankFluid.isEmpty()) { + capacity += handler.getTankCapacity(i); + } + } + return new int[]{stored, capacity}; + } + + /** + * Gas pressure = fill ratio x {@link #GAS_PRESSURE_SCALE} + pump bias. + * The bias comes from the accumulated pump contribution ({@code effectiveHeight - tank Y}), + * so it is independent of altitude. + */ + private static int gasPressure(FluidEndpoint endpoint, FluidStack fluidType) { + int[] sc = gasStorage(endpoint.handler(), fluidType); + if (sc[1] <= 0) { + return 0; + } + int bias = (endpoint.effectiveHeight() - endpoint.containerPos().getY()) * GAS_PRESSURE_PER_LIFT; + return (int) ((long) sc[0] * GAS_PRESSURE_SCALE / sc[1]) + bias; + } + + /** Pressure-difference driven flow rate, capped at {@value #MAX_SPEED} mB/tick. */ + private static int gasFlowForPressureDiff(int pressureDiff) { + if (pressureDiff <= 0) { + return 0; + } + return Math.min(pressureDiff, MAX_SPEED); + } + /** 从单个源端点向所有更低的端点分配其持有的流体。 */ private void distributeFromSource(FluidEndpoint source) { // 源接管口朝本源容器那一面若装止逆阀,其允许方向必须朝网络(背离容器),否则本源无法向网络排液 @@ -271,6 +500,10 @@ private void distributeFromSource(FluidEndpoint source) { if (stored.isEmpty()) { continue; } + // 气体不走重力/扬程分配,交给均衡(equilibrateGases)按压强处理 + if (stored.getFluid().getFluidType().isLighterThanAir()) { + continue; + } // 从源出发做方向感知可达 BFS:二极管(泵)只能正向穿过、阀门按过滤放行、面止逆阀只能沿允许方向穿过;得到 可达接管口 → 路径上的阀门列表。 Reachability reach = directionalConstraints ? computeReachableCached(source.fromPipePos(), stored) diff --git a/src/main/java/dev/dubhe/anvilcraft/client/renderer/FluidTankRenderUtil.java b/src/main/java/dev/dubhe/anvilcraft/client/renderer/FluidTankRenderUtil.java index e6ffef254f..342705272b 100644 --- a/src/main/java/dev/dubhe/anvilcraft/client/renderer/FluidTankRenderUtil.java +++ b/src/main/java/dev/dubhe/anvilcraft/client/renderer/FluidTankRenderUtil.java @@ -19,11 +19,19 @@ public static void drawFluidInTank(PoseStack ps, MultiBufferSource mbs, int ligh FluidType attributes = fluid.getFluid().getFluidType(); if (attributes.isLighterThanAir()) { - minY = maxY - fill * height; - } else { - maxY = minY + fill * height; + // Gas always fills the whole tank; the amount is conveyed by opacity. + FluidRenderHelper.INSTANCE.renderFluidBox( + fluid, + TANK_W, minY, TANK_W, + 1 - TANK_W, maxY, 1 - TANK_W, + mbs, ps, light, + true, fill + ); + return; } + maxY = minY + fill * height; + FluidRenderHelper.INSTANCE.renderFluidBox( fluid, TANK_W, minY, TANK_W, diff --git a/src/main/java/dev/dubhe/anvilcraft/client/renderer/blockentity/LargeFluidTankBlockEntityRenderer.java b/src/main/java/dev/dubhe/anvilcraft/client/renderer/blockentity/LargeFluidTankBlockEntityRenderer.java index ee1024c3ab..2ce1469699 100644 --- a/src/main/java/dev/dubhe/anvilcraft/client/renderer/blockentity/LargeFluidTankBlockEntityRenderer.java +++ b/src/main/java/dev/dubhe/anvilcraft/client/renderer/blockentity/LargeFluidTankBlockEntityRenderer.java @@ -82,12 +82,25 @@ public static void drawFluidInTank( float height = 3 - 2 * TANK_W; float minX = TANK_W - 1; - float minY = (float) (TANK_W - 1 + layerBottom * height); float minZ = TANK_W - 1; float maxX = 2 - TANK_W; - float maxY = (float) (TANK_W - 1 + layerTop * height); float maxZ = 2 - TANK_W; + if (fluid.getFluid().getFluidType().isLighterThanAir()) { + // Gas always fills the whole tank; its share is conveyed by opacity. + FluidRenderHelper.INSTANCE.renderFluidBox( + fluid, + minX, TANK_W - 1, minZ, + maxX, 2 - TANK_W, maxZ, + mbs, ps, light, + true, (float) (layerTop - layerBottom) + ); + return; + } + + float minY = (float) (TANK_W - 1 + layerBottom * height); + float maxY = (float) (TANK_W - 1 + layerTop * height); + FluidRenderHelper.INSTANCE.renderFluidBox( fluid, minX, minY, minZ, diff --git a/src/main/java/dev/dubhe/anvilcraft/client/support/FluidRenderHelper.java b/src/main/java/dev/dubhe/anvilcraft/client/support/FluidRenderHelper.java index fada5e269d..5dc3acac3e 100644 --- a/src/main/java/dev/dubhe/anvilcraft/client/support/FluidRenderHelper.java +++ b/src/main/java/dev/dubhe/anvilcraft/client/support/FluidRenderHelper.java @@ -217,6 +217,87 @@ public void renderFluidBox( ms.popPose(); } + /** + * Gas-fluid variant of {@link #renderFluidBox}: the box is always rendered at the given + * full extents, and {@code alphaFill} (0..1) scales the fluid tint's alpha so the amount + * of gas is conveyed by opacity while still filling the whole tank. + */ + public void renderFluidBox( + FluidStack fluid, + float minX, float minY, float minZ, + float maxX, float maxY, float maxZ, + MultiBufferSource buffer, PoseStack ms, int light, + boolean renderBottom, float alphaFill + ) { + var renderProps = IClientFluidTypeExtensions.of(fluid.getFluid()); + boolean opaque = (renderProps instanceof ModClientFluidTypeExtensionImpl ext && ext.isOpaque()) + || fluid.is(NeoForgeMod.MILK.value()); + RenderType renderType = opaque ? RenderType.cutout() : RenderType.translucent(); + VertexConsumer builder = buffer.getBuffer(renderType); + var stillTexture = Minecraft.getInstance().getTextureAtlas(InventoryMenu.BLOCK_ATLAS) + .apply(renderProps.getStillTexture(fluid)); + this.renderFluidBoxWithAlpha(fluid, minX, minY, minZ, maxX, maxY, maxZ, builder, ms, light, + getSkippedSides(renderBottom), false, stillTexture, alphaFill); + } + + private void renderFluidBoxWithAlpha( + FluidStack fluid, + float minX, float minY, float minZ, + float maxX, float maxY, float maxZ, + VertexConsumer builder, PoseStack ms, int light, + Set skippedSides, boolean invertGasses, + TextureAtlasSprite sideTexture, float alphaFill + ) { + var renderProps = IClientFluidTypeExtensions.of(fluid.getFluid()); + final TextureAtlasSprite stillTexture = Minecraft.getInstance().getTextureAtlas(InventoryMenu.BLOCK_ATLAS) + .apply(renderProps.getStillTexture(fluid)); + final int[] baseColors = renderProps instanceof LiquidEnchantmentClientFluidTypeExtension extension + ? extension.getLayerColors(fluid) + : new int[]{renderProps.getTintColor(fluid)}; + + int blockLightIn = (light >> 4) & 0xF; + int luminosity = Math.max(blockLightIn, fluid.getFluidType().getLightLevel()); + light = (light & 0xF00000) | luminosity << 4; + + int clampedAlpha = (int) (Mth.clamp(alphaFill, 0, 1) * 255); + int[] colors = new int[baseColors.length]; + for (int i = 0; i < baseColors.length; i++) { + int base = baseColors[i]; + int a = ((base >> 24) & 0xFF) * clampedAlpha / 255; + colors[i] = (base & 0x00FFFFFF) | (a << 24); + } + + Vec3 center = new Vec3(minX + (maxX - minX) / 2, minY + (maxY - minY) / 2, minZ + (maxZ - minZ) / 2); + ms.pushPose(); + if (invertGasses && fluid.getFluidType().isLighterThanAir()) { + ms.translate(center.x, center.y, center.z); + ms.mulPose(Axis.XP.rotationDegrees(180)); + ms.translate(-center.x, -center.y, -center.z); + } + + for (int color : colors) { + for (Direction side : Direction.values()) { + if (skippedSides.contains(side)) continue; + TextureAtlasSprite tex = side.getAxis().isHorizontal() ? sideTexture : stillTexture; + boolean positive = side.getAxisDirection() == Direction.AxisDirection.POSITIVE; + if (side.getAxis().isHorizontal()) { + if (side.getAxis() == Direction.Axis.X) { + renderStillTiledFace(side, minZ, minY, maxZ, maxY, positive ? maxX : minX, + builder, ms, light, color, tex); + } else { + renderStillTiledFace(side, minX, minY, maxX, maxY, positive ? maxZ : minZ, + builder, ms, light, color, tex); + } + } else { + renderStillTiledFace(side, minX, minZ, maxX, maxZ, positive ? maxY : minY, + builder, ms, light, color, tex); + } + } + } + + ms.popPose(); + } + private static Set getSkippedSides(boolean renderBottom) { return renderBottom ? Set.of() : Set.of(Direction.DOWN); } diff --git a/src/main/java/dev/dubhe/anvilcraft/data/lang/FluidLang.java b/src/main/java/dev/dubhe/anvilcraft/data/lang/FluidLang.java index d4bc8a23eb..888eed95b4 100644 --- a/src/main/java/dev/dubhe/anvilcraft/data/lang/FluidLang.java +++ b/src/main/java/dev/dubhe/anvilcraft/data/lang/FluidLang.java @@ -14,5 +14,11 @@ public static void init(RegistrumLangProvider provider) { provider.add("block.anvilcraft.primordial_matter", "Primordial Matter"); provider.add("block.anvilcraft.liquid_enchantment", "Liquid Enchantment"); provider.add("block.anvilcraft.liquid_enchantment.enchanted", "Liquid Enchantment (%s)"); + + // Gases + provider.add("block.anvilcraft.hydrogen", "Hydrogen"); + provider.add("block.anvilcraft.oxygen", "Oxygen"); + provider.add("block.anvilcraft.helium", "Helium"); + provider.add("block.anvilcraft.deuterium", "Deuterium"); } } From adf12b8b66f9c44715a8c512ce3a4e0bf70954df Mon Sep 17 00:00:00 2001 From: Pigeon_Nian Date: Thu, 20 Aug 2026 19:59:18 +0800 Subject: [PATCH 3/8] =?UTF-8?q?refactor(fluid-network):=20=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E6=B0=94=E4=BD=93=E5=8E=8B=E5=8A=9B=E8=AE=A1=E7=AE=97?= =?UTF-8?q?=E5=92=8C=E6=97=A0=E9=99=90=E6=B0=94=E4=BD=93=E6=BA=90=E5=A4=84?= =?UTF-8?q?=E7=90=86=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将气压相关变量类型由 int 改为 long 提高分辨率和范围 - 添加气体压力细化分辨率常量,避免整除死区 - 引入无限气体源和无限气体汇的压力标度,保证特殊储罐行为准确 - 在传输逻辑中区分无限气体源,突破存量限制直接填充目标 - 重构 gasPressure 方法,支持无限气体源/汇的专用压力计算 - 扩展气体均衡轮数限制,适应更复杂网络均衡需求 - 调整 transferGas 函数参数和实现,去除对压力差的依赖 - 新增辅助方法判断是否为无限气体源或汇,完善气体流动规则 --- .../api/fluid/network/FluidPipeNetwork.java | 90 ++++++++++++------- 1 file changed, 60 insertions(+), 30 deletions(-) diff --git a/src/main/java/dev/dubhe/anvilcraft/api/fluid/network/FluidPipeNetwork.java b/src/main/java/dev/dubhe/anvilcraft/api/fluid/network/FluidPipeNetwork.java index 0b905f1a9b..fb75a7d9db 100644 --- a/src/main/java/dev/dubhe/anvilcraft/api/fluid/network/FluidPipeNetwork.java +++ b/src/main/java/dev/dubhe/anvilcraft/api/fluid/network/FluidPipeNetwork.java @@ -1,5 +1,6 @@ package dev.dubhe.anvilcraft.api.fluid.network; +import dev.dubhe.anvilcraft.api.fluidtank.InfinityFluidTank; import dev.dubhe.anvilcraft.block.entity.fluid.AbstractPipeCheckValveBlockEntity; import dev.dubhe.anvilcraft.block.entity.fluid.GlassPipeBlockEntity; import dev.dubhe.anvilcraft.util.TriggerUtil; @@ -51,14 +52,20 @@ public class FluidPipeNetwork { public static final int FULL_SPEED_HEIGHT = MAX_SPEED / HEIGHT_RATE; /** 气体满罐时的压力标度:气体压力 = 填充率(0..1) × 该值 + 泵气压偏置。 */ private static final int GAS_PRESSURE_SCALE = 1000; + /** 气体压力排序的分辨率(在 GAS_PRESSURE_SCALE 基础上进一步细化,避免整除产生的死区)。 */ + private static final int GAS_PRESSURE_RESOLUTION = 100; /** 泵每提供 1 格"扬程"在气压上的折算增量(气体由气压差驱动,不再使用扬程)。 */ private static final int GAS_PRESSURE_PER_LIFT = 100; + /** 无限气体源(创造流体储罐)的压力杠标:远大于任何常规满罐,确保始终作为源向外扩散。 */ + private static final long GAS_INFINITE_PRESSURE = Long.MAX_VALUE / 4; + /** 空创造流体储罐的压力杠标:无穷小,确保任意气体都能浇入并被销毁。 */ + private static final long GAS_INFINITE_SINK_PRESSURE = Long.MIN_VALUE / 4; /** 单种气体单 tick 全网均衡的总移动预算(mB)。 */ private static final int GAS_EQUILIBRIUM_BUDGET = MAX_SPEED; /** 判定气压已均衡的差值阈值(低于该值不再转移)。 */ - private static final int GAS_PRESSURE_EPSILON = 1; + private static final long GAS_PRESSURE_EPSILON = 1; /** 气体均衡每 tick 的最大轮数,限制 O(n²) 遍历成本。 */ - private static final int GAS_MAX_ROUNDS = 8; + private static final int GAS_MAX_ROUNDS = 16; /** * 按高度差计算流速(线性增长): @@ -318,7 +325,7 @@ private void equilibrateGasType(FluidStack fluidType, List candid for (int round = 0; round < GAS_MAX_ROUNDS && globalBudget > 0; round++) { boolean progressed = false; candidates.sort(Comparator - .comparingInt((FluidEndpoint e) -> gasPressure(e, fluidType)) + .comparingLong((FluidEndpoint e) -> gasPressure(e, fluidType)) .reversed()); for (int i = 0; i < candidates.size() && globalBudget > 0; i++) { FluidEndpoint hi = candidates.get(i); @@ -333,13 +340,13 @@ private void equilibrateGasType(FluidStack fluidType, List candid if (hiStored.isEmpty()) { continue; } - int hiPressure = gasPressure(hi, fluidType); + long hiPressure = gasPressure(hi, fluidType); Reachability reach = directionalConstraints ? computeReachableCached(hi.fromPipePos(), hiStored) : null; Map> pathValves = reach == null ? Map.of() : reach.pathValves(); FluidEndpoint bestTarget = null; - int bestDelta = 0; + long bestDelta = 0; for (int j = 0; j < candidates.size(); j++) { if (i == j) { continue; @@ -358,8 +365,8 @@ private void equilibrateGasType(FluidStack fluidType, List candid if (loSc[0] >= loSc[1]) { continue; } - int loPressure = gasPressure(lo, fluidType); - int delta = hiPressure - loPressure; + long loPressure = gasPressure(lo, fluidType); + long delta = hiPressure - loPressure; if (delta <= GAS_PRESSURE_EPSILON) { continue; } @@ -371,7 +378,7 @@ private void equilibrateGasType(FluidStack fluidType, List candid if (bestTarget == null) { continue; } - int moved = transferGas(hi, bestTarget, fluidType, bestDelta, pathValves, reach); + int moved = transferGas(hi, bestTarget, fluidType, pathValves, reach); if (moved > 0) { globalBudget -= moved; progressed = true; @@ -385,10 +392,10 @@ private void equilibrateGasType(FluidStack fluidType, List candid /** * Transfer driven by a single pressure difference: move exactly enough to equalize the two - * pressures, capped by flow rate, valve allowance, source stock and target free space. + * pressures, capped only by valve allowance, source stock and target free space. */ private int transferGas( - FluidEndpoint hi, FluidEndpoint lo, FluidStack fluidType, int pressureDiff, + FluidEndpoint hi, FluidEndpoint lo, FluidStack fluidType, Map> pathValves, @Nullable Reachability reach ) { int[] hiSc = gasStorage(hi.handler(), fluidType); @@ -401,19 +408,24 @@ private int transferGas( return 0; } int loFree = loCap - loStored; - int hiBias = (hi.effectiveHeight() - hi.containerPos().getY()) * GAS_PRESSURE_PER_LIFT; - int loBias = (lo.effectiveHeight() - lo.containerPos().getY()) * GAS_PRESSURE_PER_LIFT; - double num = (double) hiStored * loCap - (double) loStored * hiCap - - (double) (loBias - hiBias) * hiCap * loCap / GAS_PRESSURE_SCALE; - double x = num / (double) (hiCap + loCap); - int equalAmt = Math.max(0, (int) Math.floor(x)); - if (equalAmt <= 0) { - return 0; - } - int flowCap = gasFlowForPressureDiff(pressureDiff); int valveLimit = minValveRemaining(pathValves.get(lo.fromPipePos())); - int want = Math.min(equalAmt, Math.min(flowCap, Math.min(hiStored, loFree))); - want = Math.min(want, valveLimit); + int want; + if (isInfiniteGasSource(hi.handler(), fluidType)) { + // 无限气体源:不受源存量限制,直接尽可能填满目标(受阀门限流) + want = Math.min(loFree, valveLimit); + } else { + int hiBias = (hi.effectiveHeight() - hi.containerPos().getY()) * GAS_PRESSURE_PER_LIFT; + int loBias = (lo.effectiveHeight() - lo.containerPos().getY()) * GAS_PRESSURE_PER_LIFT; + double num = (double) hiStored * loCap - (double) loStored * hiCap + - (double) (loBias - hiBias) * hiCap * loCap / GAS_PRESSURE_SCALE; + double x = num / (double) (hiCap + loCap); + int equalAmt = Math.max(0, (int) Math.round(x)); + if (equalAmt <= 0) { + return 0; + } + want = Math.min(equalAmt, Math.min(hiStored, loFree)); + want = Math.min(want, valveLimit); + } if (want <= 0) { return 0; } @@ -468,21 +480,39 @@ private static int[] gasStorage(IFluidHandler handler, FluidStack fluidType) { * The bias comes from the accumulated pump contribution ({@code effectiveHeight - tank Y}), * so it is independent of altitude. */ - private static int gasPressure(FluidEndpoint endpoint, FluidStack fluidType) { + private static long gasPressure(FluidEndpoint endpoint, FluidStack fluidType) { + if (isInfiniteGasSource(endpoint.handler(), fluidType)) { + return GAS_INFINITE_PRESSURE; + } + if (isInfiniteGasSink(endpoint.handler())) { + return GAS_INFINITE_SINK_PRESSURE; + } int[] sc = gasStorage(endpoint.handler(), fluidType); if (sc[1] <= 0) { return 0; } - int bias = (endpoint.effectiveHeight() - endpoint.containerPos().getY()) * GAS_PRESSURE_PER_LIFT; - return (int) ((long) sc[0] * GAS_PRESSURE_SCALE / sc[1]) + bias; + long bias = (endpoint.effectiveHeight() - endpoint.containerPos().getY()) * GAS_PRESSURE_PER_LIFT; + return (long) sc[0] * GAS_PRESSURE_SCALE * GAS_PRESSURE_RESOLUTION / sc[1] + + bias * GAS_PRESSURE_RESOLUTION; } - /** Pressure-difference driven flow rate, capped at {@value #MAX_SPEED} mB/tick. */ - private static int gasFlowForPressureDiff(int pressureDiff) { - if (pressureDiff <= 0) { - return 0; + /** + * 创造流体储罐(InfinityFluidTank)且当前搭载该气体时视为无限气体源: + * 存量无限,压力设为最大,持续向网络外扩散气体。 + */ + private static boolean isInfiniteGasSource(IFluidHandler handler, FluidStack fluidType) { + if (!(handler instanceof InfinityFluidTank endless) || endless.isEmpty()) { + return false; } - return Math.min(pressureDiff, MAX_SPEED); + return FluidStack.isSameFluidSameComponents(endless.getFluid(), fluidType); + } + + /** + * 空创造流体储罐(InfinityFluidTank 且未搭载任何流体)视为无限气体汇: + * 压力无穷小,任意相连的源都会把气体扩散进去并被销毁。 + */ + private static boolean isInfiniteGasSink(IFluidHandler handler) { + return handler instanceof InfinityFluidTank endless && endless.isEmpty(); } /** 从单个源端点向所有更低的端点分配其持有的流体。 */ From ba45b441c2367a07d507898d130d253bb6f968ab Mon Sep 17 00:00:00 2001 From: Pigeon_Nian Date: Thu, 20 Aug 2026 20:13:21 +0800 Subject: [PATCH 4/8] =?UTF-8?q?fix(event):=20=E4=BF=AE=E5=A4=8D=E5=AE=A2?= =?UTF-8?q?=E6=88=B7=E7=AB=AF=E9=A2=9C=E8=89=B2=E5=A4=84=E7=90=86=E4=BA=8B?= =?UTF-8?q?=E4=BB=B6=E6=B3=A8=E5=86=8C=E7=9A=84=E6=B3=A8=E8=A7=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 为事件总线订阅者添加客户端限定标识 - 避免服务端加载客户端专用的颜色处理代码 - 确保颜色处理器仅在客户端环境下注册 - 修改注解为 @EventBusSubscriber(value = Dist.CLIENT, modid = AnvilCraft.MOD_ID) --- .../anvilcraft/event/RegisterColorHandlersEventListener.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/dev/dubhe/anvilcraft/event/RegisterColorHandlersEventListener.java b/src/main/java/dev/dubhe/anvilcraft/event/RegisterColorHandlersEventListener.java index ff0159f1c8..4a5a532258 100644 --- a/src/main/java/dev/dubhe/anvilcraft/event/RegisterColorHandlersEventListener.java +++ b/src/main/java/dev/dubhe/anvilcraft/event/RegisterColorHandlersEventListener.java @@ -9,12 +9,13 @@ import net.minecraft.util.FastColor; import net.minecraft.world.item.alchemy.PotionContents; import net.minecraft.world.level.block.RedStoneWireBlock; +import net.neoforged.api.distmarker.Dist; import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.fml.common.EventBusSubscriber; import net.neoforged.neoforge.client.event.RegisterColorHandlersEvent; import net.neoforged.neoforge.client.model.DynamicFluidContainerModel; -@EventBusSubscriber(modid = AnvilCraft.MOD_ID) +@EventBusSubscriber(value = Dist.CLIENT, modid = AnvilCraft.MOD_ID) public class RegisterColorHandlersEventListener { @SubscribeEvent public static void registerBlockColorHandlersEvent(RegisterColorHandlersEvent.Block event) { From e4a80f38ec3c0f9c06c2755c7881cf881d2129f7 Mon Sep 17 00:00:00 2001 From: Pigeon_Nian Date: Thu, 20 Aug 2026 21:04:09 +0800 Subject: [PATCH 5/8] =?UTF-8?q?feat(fluid):=20=E6=B7=BB=E5=8A=A0=E6=B0=99?= =?UTF-8?q?=E6=B0=94=E5=92=8C=E6=B0=AA=E6=B0=94=E6=B5=81=E4=BD=93=E5=8F=8A?= =?UTF-8?q?=E6=A1=B6=E7=89=A9=E5=93=81=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增氙气和氪气流体类型及对应的流体注册 - 实现氙气和氪气桶物品及其模型文件 - 在物品标签和物品选项卡中添加氙气桶和氪气桶 - 更新流体客户端渲染设置以支持氙气和氪气颜色 - 增加多语言支持,包括英语和倒置英语的名称 - 修改流体网络代码,调整无限气体源和汇的处理逻辑 - 注册颜色处理器以支持新增的氙气桶和氪气桶 --- .../assets/anvilcraft/lang/en_ud.json | 4 ++ .../assets/anvilcraft/lang/en_us.json | 4 ++ .../models/item/krypton_bucket.json | 6 ++ .../anvilcraft/models/item/xenon_bucket.json | 6 ++ .../resources/data/c/tags/item/buckets.json | 4 +- .../api/fluid/network/FluidPipeNetwork.java | 5 +- .../RegisterColorHandlersEventListener.java | 4 +- .../anvilcraft/init/block/ModFluids.java | 58 +++++++++++++++++++ .../dubhe/anvilcraft/init/item/ModItems.java | 16 +++++ .../init/item/tabs/Ingredients.java | 2 + .../init/item/tabs/ItemsSections.java | 2 + 11 files changed, 108 insertions(+), 3 deletions(-) create mode 100644 src/generated/resources/assets/anvilcraft/models/item/krypton_bucket.json create mode 100644 src/generated/resources/assets/anvilcraft/models/item/xenon_bucket.json diff --git a/src/generated/resources/assets/anvilcraft/lang/en_ud.json b/src/generated/resources/assets/anvilcraft/lang/en_ud.json index 62a556ac5d..ce1ec7e16b 100644 --- a/src/generated/resources/assets/anvilcraft/lang/en_ud.json +++ b/src/generated/resources/assets/anvilcraft/lang/en_ud.json @@ -518,6 +518,7 @@ "block.anvilcraft.item_collector": "ɹoʇɔǝꞁꞁoƆ ɯǝʇI", "block.anvilcraft.item_detector": "ɹoʇɔǝʇǝᗡ ɯǝʇI", "block.anvilcraft.jewelcrafting_table": "ǝꞁqɐ⟘ ᵷuᴉʇɟɐɹƆ ꞁǝʍǝՐ", + "block.anvilcraft.krypton": "uoʇdʎɹꞰ", "block.anvilcraft.large_cake": "ǝʞɐƆ ǝᵷɹɐꞀ", "block.anvilcraft.large_cauldron": "uoɹpꞁnɐƆ ǝᵷɹɐꞀ", "block.anvilcraft.large_crate": "ǝʇɐɹƆ ǝᵷɹɐꞀ", @@ -742,6 +743,7 @@ "block.anvilcraft.white_chocolate_stairs": "sɹᴉɐʇS ǝʇɐꞁoɔoɥƆ ǝʇᴉɥM", "block.anvilcraft.white_hole": "ǝꞁoH ǝʇᴉɥM", "block.anvilcraft.wip_block": "ʞɔoꞁᗺ dᴉM", + "block.anvilcraft.xenon": "uouǝX", "block.anvilcraft.yellow_cement": "ʇuǝɯǝƆ ʍoꞁꞁǝ⅄", "block.anvilcraft.yellow_cement_cauldron": "uoɹpꞁnɐƆ ʇuǝɯǝƆ ʍoꞁꞁǝ⅄", "block.anvilcraft.zinc_block": "ɔuᴉZ ɟo ʞɔoꞁᗺ", @@ -1056,6 +1058,7 @@ "item.anvilcraft.ionocraft": "ʇɟɐɹɔouoI", "item.anvilcraft.ionocraft_backpack": "ʞɔɐdʞɔɐᗺ ʇɟɐɹɔouoI", "item.anvilcraft.ionocraft_backpack.flight_time": "s%s ɯ%s :ǝɯᴉ⟘ ʇɥᵷᴉꞁℲ ᵷuᴉuᴉɐɯǝᴚ", + "item.anvilcraft.krypton_bucket": "ʇǝʞɔnᗺ uoʇdʎɹꞰ", "item.anvilcraft.laser_gun": "un⅁ ɹǝsɐꞀ", "item.anvilcraft.lead_ingot": "ʇoᵷuI pɐǝꞀ", "item.anvilcraft.lead_nugget": "ʇǝᵷᵷnN pɐǝꞀ", @@ -1162,6 +1165,7 @@ "item.anvilcraft.white_cement_bucket": "ʇǝʞɔnᗺ ʇuǝɯǝƆ ǝʇᴉɥM", "item.anvilcraft.wood_fiber": "ɹǝqᴉℲ pooM", "item.anvilcraft.wormhole_stabilizer_component": "ʇuǝuodɯoƆ ɹǝzᴉꞁᴉqɐʇS ǝꞁoɥɯɹoM", + "item.anvilcraft.xenon_bucket": "ʇǝʞɔnᗺ uouǝX", "item.anvilcraft.yellow_cement_bucket": "ʇǝʞɔnᗺ ʇuǝɯǝƆ ʍoꞁꞁǝ⅄", "item.anvilcraft.zinc_ingot": "ʇoᵷuI ɔuᴉZ", "item.anvilcraft.zinc_nugget": "ʇǝᵷᵷnN ɔuᴉZ", diff --git a/src/generated/resources/assets/anvilcraft/lang/en_us.json b/src/generated/resources/assets/anvilcraft/lang/en_us.json index 078d2a8f36..f4821183c8 100644 --- a/src/generated/resources/assets/anvilcraft/lang/en_us.json +++ b/src/generated/resources/assets/anvilcraft/lang/en_us.json @@ -518,6 +518,7 @@ "block.anvilcraft.item_collector": "Item Collector", "block.anvilcraft.item_detector": "Item Detector", "block.anvilcraft.jewelcrafting_table": "Jewel Crafting Table", + "block.anvilcraft.krypton": "Krypton", "block.anvilcraft.large_cake": "Large Cake", "block.anvilcraft.large_cauldron": "Large Cauldron", "block.anvilcraft.large_crate": "Large Crate", @@ -742,6 +743,7 @@ "block.anvilcraft.white_chocolate_stairs": "White Chocolate Stairs", "block.anvilcraft.white_hole": "White Hole", "block.anvilcraft.wip_block": "Wip Block", + "block.anvilcraft.xenon": "Xenon", "block.anvilcraft.yellow_cement": "Yellow Cement", "block.anvilcraft.yellow_cement_cauldron": "Yellow Cement Cauldron", "block.anvilcraft.zinc_block": "Block of Zinc", @@ -1056,6 +1058,7 @@ "item.anvilcraft.ionocraft": "Ionocraft", "item.anvilcraft.ionocraft_backpack": "Ionocraft Backpack", "item.anvilcraft.ionocraft_backpack.flight_time": "Remaining Flight Time: %sm %ss", + "item.anvilcraft.krypton_bucket": "Krypton Bucket", "item.anvilcraft.laser_gun": "Laser Gun", "item.anvilcraft.lead_ingot": "Lead Ingot", "item.anvilcraft.lead_nugget": "Lead Nugget", @@ -1162,6 +1165,7 @@ "item.anvilcraft.white_cement_bucket": "White Cement Bucket", "item.anvilcraft.wood_fiber": "Wood Fiber", "item.anvilcraft.wormhole_stabilizer_component": "Wormhole Stabilizer Component", + "item.anvilcraft.xenon_bucket": "Xenon Bucket", "item.anvilcraft.yellow_cement_bucket": "Yellow Cement Bucket", "item.anvilcraft.zinc_ingot": "Zinc Ingot", "item.anvilcraft.zinc_nugget": "Zinc Nugget", diff --git a/src/generated/resources/assets/anvilcraft/models/item/krypton_bucket.json b/src/generated/resources/assets/anvilcraft/models/item/krypton_bucket.json new file mode 100644 index 0000000000..379775de28 --- /dev/null +++ b/src/generated/resources/assets/anvilcraft/models/item/krypton_bucket.json @@ -0,0 +1,6 @@ +{ + "parent": "neoforge:item/bucket_drip", + "flip_gas": true, + "fluid": "anvilcraft:krypton", + "loader": "neoforge:fluid_container" +} \ No newline at end of file diff --git a/src/generated/resources/assets/anvilcraft/models/item/xenon_bucket.json b/src/generated/resources/assets/anvilcraft/models/item/xenon_bucket.json new file mode 100644 index 0000000000..d45575b748 --- /dev/null +++ b/src/generated/resources/assets/anvilcraft/models/item/xenon_bucket.json @@ -0,0 +1,6 @@ +{ + "parent": "neoforge:item/bucket_drip", + "flip_gas": true, + "fluid": "anvilcraft:xenon", + "loader": "neoforge:fluid_container" +} \ No newline at end of file diff --git a/src/generated/resources/data/c/tags/item/buckets.json b/src/generated/resources/data/c/tags/item/buckets.json index 4a58be01c1..b4b558437b 100644 --- a/src/generated/resources/data/c/tags/item/buckets.json +++ b/src/generated/resources/data/c/tags/item/buckets.json @@ -22,6 +22,8 @@ "anvilcraft:hydrogen_bucket", "anvilcraft:oxygen_bucket", "anvilcraft:helium_bucket", - "anvilcraft:deuterium_bucket" + "anvilcraft:deuterium_bucket", + "anvilcraft:xenon_bucket", + "anvilcraft:krypton_bucket" ] } \ No newline at end of file diff --git a/src/main/java/dev/dubhe/anvilcraft/api/fluid/network/FluidPipeNetwork.java b/src/main/java/dev/dubhe/anvilcraft/api/fluid/network/FluidPipeNetwork.java index fb75a7d9db..b9c397d13e 100644 --- a/src/main/java/dev/dubhe/anvilcraft/api/fluid/network/FluidPipeNetwork.java +++ b/src/main/java/dev/dubhe/anvilcraft/api/fluid/network/FluidPipeNetwork.java @@ -410,7 +410,10 @@ private int transferGas( int loFree = loCap - loStored; int valveLimit = minValveRemaining(pathValves.get(lo.fromPipePos())); int want; - if (isInfiniteGasSource(hi.handler(), fluidType)) { + if (isInfiniteGasSink(lo.handler())) { + // 空创造流体储罐(无限汇):吸收源端全部可转移气体,至源排空或被阀门限制 + want = Math.min(hiStored, valveLimit); + } else if (isInfiniteGasSource(hi.handler(), fluidType)) { // 无限气体源:不受源存量限制,直接尽可能填满目标(受阀门限流) want = Math.min(loFree, valveLimit); } else { diff --git a/src/main/java/dev/dubhe/anvilcraft/event/RegisterColorHandlersEventListener.java b/src/main/java/dev/dubhe/anvilcraft/event/RegisterColorHandlersEventListener.java index 4a5a532258..ccbb0b5880 100644 --- a/src/main/java/dev/dubhe/anvilcraft/event/RegisterColorHandlersEventListener.java +++ b/src/main/java/dev/dubhe/anvilcraft/event/RegisterColorHandlersEventListener.java @@ -48,6 +48,8 @@ public static void registerItemColorHandlersEvent(RegisterColorHandlersEvent.Ite ModItems.HYDROGEN_BUCKET, ModItems.OXYGEN_BUCKET, ModItems.HELIUM_BUCKET, - ModItems.DEUTERIUM_BUCKET); + ModItems.DEUTERIUM_BUCKET, + ModItems.XENON_BUCKET, + ModItems.KRYPTON_BUCKET); } } diff --git a/src/main/java/dev/dubhe/anvilcraft/init/block/ModFluids.java b/src/main/java/dev/dubhe/anvilcraft/init/block/ModFluids.java index 62b1cd66d7..2ebccc6e68 100644 --- a/src/main/java/dev/dubhe/anvilcraft/init/block/ModFluids.java +++ b/src/main/java/dev/dubhe/anvilcraft/init/block/ModFluids.java @@ -144,6 +144,22 @@ public class ModFluids { "deuterium", () -> new GasFluid(ModFluids.DEUTERIUM_FLUID_TYPE) ); + public static final FluidType XENON_FLUID_TYPE = createXenonType(); + public static final DeferredHolder XENON_TYPE = FLUID_TYPES.register( + "xenon", () -> ModFluids.XENON_FLUID_TYPE + ); + public static final DeferredHolder XENON = FLUIDS.register( + "xenon", () -> new GasFluid(ModFluids.XENON_FLUID_TYPE) + ); + + public static final FluidType KRYPTON_FLUID_TYPE = createKryptonType(); + public static final DeferredHolder KRYPTON_TYPE = FLUID_TYPES.register( + "krypton", () -> ModFluids.KRYPTON_FLUID_TYPE + ); + public static final DeferredHolder KRYPTON = FLUIDS.register( + "krypton", () -> new GasFluid(ModFluids.KRYPTON_FLUID_TYPE) + ); + private static FluidType createHydrogenType() { return new FluidType(FluidType.Properties.create() .descriptionId("block.anvilcraft.hydrogen") @@ -188,6 +204,28 @@ private static FluidType createDeuteriumType() { ); } + private static FluidType createXenonType() { + return new FluidType(FluidType.Properties.create() + .descriptionId("block.anvilcraft.xenon") + .density(-1000) + .viscosity(100) + .fallDistanceModifier(0) + .sound(SoundActions.BUCKET_FILL, SoundEvents.BUCKET_FILL) + .sound(SoundActions.BUCKET_EMPTY, SoundEvents.BUCKET_EMPTY) + ); + } + + private static FluidType createKryptonType() { + return new FluidType(FluidType.Properties.create() + .descriptionId("block.anvilcraft.krypton") + .density(-1000) + .viscosity(100) + .fallDistanceModifier(0) + .sound(SoundActions.BUCKET_FILL, SoundEvents.BUCKET_FILL) + .sound(SoundActions.BUCKET_EMPTY, SoundEvents.BUCKET_EMPTY) + ); + } + public static final Object2ObjectMap> CEMENT_TYPES = registerAllCementTypes(); public static final Object2ObjectMap> SOURCE_CEMENTS = registerAllSourceCement(); public static final Object2ObjectMap> FLOWING_CEMENTS = registerAllFlowingCement(); @@ -443,6 +481,26 @@ public static void onRegisterFluidType(RegisterClientExtensionsEvent e) { false ), DEUTERIUM_TYPE ); + e.registerFluidType( + new ModClientFluidTypeExtensionImpl( + ResourceLocation.withDefaultNamespace("block/water_still"), + ResourceLocation.withDefaultNamespace("block/water_flow"), + 0xC9C2F0, + 2.0f, + 0xFFC9C2F0, + false + ), XENON_TYPE + ); + e.registerFluidType( + new ModClientFluidTypeExtensionImpl( + ResourceLocation.withDefaultNamespace("block/water_still"), + ResourceLocation.withDefaultNamespace("block/water_flow"), + 0xB0E8A8, + 2.0f, + 0xFFB0E8A8, + false + ), KRYPTON_TYPE + ); for (Color color : Color.values()) { e.registerFluidType( diff --git a/src/main/java/dev/dubhe/anvilcraft/init/item/ModItems.java b/src/main/java/dev/dubhe/anvilcraft/init/item/ModItems.java index fa70fc399d..390ffbba38 100644 --- a/src/main/java/dev/dubhe/anvilcraft/init/item/ModItems.java +++ b/src/main/java/dev/dubhe/anvilcraft/init/item/ModItems.java @@ -1186,6 +1186,22 @@ private static ItemEntry createBigAmuletItem(String type, Suppli .model(ModelProviderUtil::bucketGassy) .register(); + public static final ItemEntry XENON_BUCKET = REGISTRUM + .item("xenon_bucket", ModItems.bucket(() -> ModFluids.XENON)) + .tag(Tags.Items.BUCKETS) + .lang("Xenon Bucket") + .properties(properties -> properties.stacksTo(1).craftRemainder(Items.BUCKET)) + .model(ModelProviderUtil::bucketGassy) + .register(); + + public static final ItemEntry KRYPTON_BUCKET = REGISTRUM + .item("krypton_bucket", ModItems.bucket(() -> ModFluids.KRYPTON)) + .tag(Tags.Items.BUCKETS) + .lang("Krypton Bucket") + .properties(properties -> properties.stacksTo(1).craftRemainder(Items.BUCKET)) + .model(ModelProviderUtil::bucketGassy) + .register(); + public static final ItemEntry PIPE = REGISTRUM.item("pipe", PipeBlockItem::new) .model((ctx, provider) -> provider .withExistingParent(ctx.getId().toString(), AnvilCraft.of("block/pipe")) diff --git a/src/main/java/dev/dubhe/anvilcraft/init/item/tabs/Ingredients.java b/src/main/java/dev/dubhe/anvilcraft/init/item/tabs/Ingredients.java index e42c14f532..af5107353d 100644 --- a/src/main/java/dev/dubhe/anvilcraft/init/item/tabs/Ingredients.java +++ b/src/main/java/dev/dubhe/anvilcraft/init/item/tabs/Ingredients.java @@ -116,6 +116,8 @@ public void accept() { this.plain(ModItems.OXYGEN_BUCKET); this.plain(ModItems.HELIUM_BUCKET); this.plain(ModItems.DEUTERIUM_BUCKET); + this.plain(ModItems.XENON_BUCKET); + this.plain(ModItems.KRYPTON_BUCKET); ModItems.CEMENT_BUCKETS.forEach((color, bucketItem) -> this.plain(bucketItem)); } } diff --git a/src/main/java/dev/dubhe/anvilcraft/init/item/tabs/ItemsSections.java b/src/main/java/dev/dubhe/anvilcraft/init/item/tabs/ItemsSections.java index 6d61158832..cf3d21ea4d 100644 --- a/src/main/java/dev/dubhe/anvilcraft/init/item/tabs/ItemsSections.java +++ b/src/main/java/dev/dubhe/anvilcraft/init/item/tabs/ItemsSections.java @@ -279,6 +279,8 @@ public void accept() { content.accept(ModItems.OXYGEN_BUCKET); content.accept(ModItems.HELIUM_BUCKET); content.accept(ModItems.DEUTERIUM_BUCKET); + content.accept(ModItems.XENON_BUCKET); + content.accept(ModItems.KRYPTON_BUCKET); ModItems.CEMENT_BUCKETS.forEach((color, bucketItem) -> this.acceptFolded(content, bucketItem)); } ); From 6f5feadd29f43bbe58d770608b86cb3c6a3e42ac Mon Sep 17 00:00:00 2001 From: Pigeon_Nian Date: Thu, 20 Aug 2026 21:49:35 +0800 Subject: [PATCH 6/8] =?UTF-8?q?refactor(api):=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E6=B0=94=E4=BD=93=E5=AD=98=E5=82=A8=E5=AE=B9=E9=87=8F=E8=AE=A1?= =?UTF-8?q?=E7=AE=97=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修改gasStorage方法,改为根据剩余容量计算总容量 - 处理多流体槽情况下的容量判断问题,避免容量被占用槽位误判 - 采用总存储量和总容量差值计算剩余空间再加已存气体量 - 提升多流体处理的准确性和代码可读性 - 增加方法注释详细说明容量计算规则 --- .../api/fluid/network/FluidPipeNetwork.java | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/main/java/dev/dubhe/anvilcraft/api/fluid/network/FluidPipeNetwork.java b/src/main/java/dev/dubhe/anvilcraft/api/fluid/network/FluidPipeNetwork.java index b9c397d13e..6df9302b33 100644 --- a/src/main/java/dev/dubhe/anvilcraft/api/fluid/network/FluidPipeNetwork.java +++ b/src/main/java/dev/dubhe/anvilcraft/api/fluid/network/FluidPipeNetwork.java @@ -462,20 +462,27 @@ private int transferGas( return actuallyFilled; } - /** Current stock and total capacity of this gas in a handler (empty slots count as capacity). */ + /** + * Current stock and total capacity of this gas in a handler. Capacity is derived from the + * handler's remaining free space (total capacity minus all stored fluids) plus the amount + * already holding this gas, so multi-fluid tanks can host several gases without each one + * appearing full merely because other fluids occupy other slots. + */ private static int[] gasStorage(IFluidHandler handler, FluidStack fluidType) { int stored = 0; - int capacity = 0; + int totalStored = 0; + int totalCapacity = 0; for (int i = 0; i < handler.getTanks(); i++) { FluidStack tankFluid = handler.getFluidInTank(i); + int amount = tankFluid.getAmount(); + totalStored += amount; + totalCapacity += handler.getTankCapacity(i); if (!tankFluid.isEmpty() && FluidStack.isSameFluidSameComponents(tankFluid, fluidType)) { - stored += tankFluid.getAmount(); - capacity += handler.getTankCapacity(i); - } else if (tankFluid.isEmpty()) { - capacity += handler.getTankCapacity(i); + stored += amount; } } - return new int[]{stored, capacity}; + int freeSpace = Math.max(0, totalCapacity - totalStored); + return new int[]{stored, stored + freeSpace}; } /** From a57fce7d981046c43e0b0d43eefc458d2387342d Mon Sep 17 00:00:00 2001 From: Pigeon_Nian Date: Thu, 20 Aug 2026 21:53:03 +0800 Subject: [PATCH 7/8] =?UTF-8?q?fix(fluid-network):=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E6=B0=94=E4=BD=93=E8=BD=AC=E7=A7=BB=E9=A2=84=E7=AE=97=E5=85=B1?= =?UTF-8?q?=E4=BA=AB=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修改气体均衡转移预算为所有气体共享的全网预算 - 在气体均衡方法中引入可变预算参数以实现预算共享 - 调整气体转移循环逻辑,确保总预算正确减少 - 清理相关变量定义,简化预算管理逻辑 --- .../api/fluid/network/FluidPipeNetwork.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/main/java/dev/dubhe/anvilcraft/api/fluid/network/FluidPipeNetwork.java b/src/main/java/dev/dubhe/anvilcraft/api/fluid/network/FluidPipeNetwork.java index 6df9302b33..f5037e28e0 100644 --- a/src/main/java/dev/dubhe/anvilcraft/api/fluid/network/FluidPipeNetwork.java +++ b/src/main/java/dev/dubhe/anvilcraft/api/fluid/network/FluidPipeNetwork.java @@ -60,7 +60,7 @@ public class FluidPipeNetwork { private static final long GAS_INFINITE_PRESSURE = Long.MAX_VALUE / 4; /** 空创造流体储罐的压力杠标:无穷小,确保任意气体都能浇入并被销毁。 */ private static final long GAS_INFINITE_SINK_PRESSURE = Long.MIN_VALUE / 4; - /** 单种气体单 tick 全网均衡的总移动预算(mB)。 */ + /** 气体每 tick 全网气体转移的总预算(mB),由所有气体类型共享。*/ private static final int GAS_EQUILIBRIUM_BUDGET = MAX_SPEED; /** 判定气压已均衡的差值阈值(低于该值不再转移)。 */ private static final long GAS_PRESSURE_EPSILON = 1; @@ -292,12 +292,13 @@ private void equilibrateGases() { } } } + int[] budget = new int[]{GAS_EQUILIBRIUM_BUDGET}; for (FluidStack gasType : gasTypes) { List candidates = gasCandidates(gasType); if (candidates.size() < 2) { continue; } - equilibrateGasType(gasType, candidates); + equilibrateGasType(gasType, candidates, budget); } } @@ -320,14 +321,15 @@ private List gasCandidates(FluidStack fluidType) { * move gas to the lowest-pressure reachable target that still has room, until pressure * differences converge or the global budget is exhausted. */ - private void equilibrateGasType(FluidStack fluidType, List candidates) { - int globalBudget = GAS_EQUILIBRIUM_BUDGET; - for (int round = 0; round < GAS_MAX_ROUNDS && globalBudget > 0; round++) { + private void equilibrateGasType( + FluidStack fluidType, List candidates, int[] budget + ) { + for (int round = 0; round < GAS_MAX_ROUNDS && budget[0] > 0; round++) { boolean progressed = false; candidates.sort(Comparator .comparingLong((FluidEndpoint e) -> gasPressure(e, fluidType)) .reversed()); - for (int i = 0; i < candidates.size() && globalBudget > 0; i++) { + for (int i = 0; i < candidates.size() && budget[0] > 0; i++) { FluidEndpoint hi = candidates.get(i); int[] hiSc = gasStorage(hi.handler(), fluidType); if (hiSc[0] <= 0 || hiSc[1] <= 0) { @@ -380,7 +382,7 @@ private void equilibrateGasType(FluidStack fluidType, List candid } int moved = transferGas(hi, bestTarget, fluidType, pathValves, reach); if (moved > 0) { - globalBudget -= moved; + budget[0] -= moved; progressed = true; } } From 290658ef26d14663774aa3f44ba9c8d7d750a36d Mon Sep 17 00:00:00 2001 From: Pigeon_Nian Date: Fri, 21 Aug 2026 16:13:09 +0800 Subject: [PATCH 8/8] =?UTF-8?q?feat(lang):=20=E6=B7=BB=E5=8A=A0=E6=B0=99?= =?UTF-8?q?=E5=92=8C=E6=B0=AA=E6=B5=81=E4=BD=93=E7=9A=84=E8=AF=AD=E8=A8=80?= =?UTF-8?q?=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在流体语言文件中新增氙(Xenon)条目 - 在流体语言文件中新增氪(Krypton)条目 --- src/main/java/dev/dubhe/anvilcraft/data/lang/FluidLang.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/dev/dubhe/anvilcraft/data/lang/FluidLang.java b/src/main/java/dev/dubhe/anvilcraft/data/lang/FluidLang.java index 888eed95b4..21dc119189 100644 --- a/src/main/java/dev/dubhe/anvilcraft/data/lang/FluidLang.java +++ b/src/main/java/dev/dubhe/anvilcraft/data/lang/FluidLang.java @@ -20,5 +20,7 @@ public static void init(RegistrumLangProvider provider) { provider.add("block.anvilcraft.oxygen", "Oxygen"); provider.add("block.anvilcraft.helium", "Helium"); provider.add("block.anvilcraft.deuterium", "Deuterium"); + provider.add("block.anvilcraft.xenon", "Xenon"); + provider.add("block.anvilcraft.krypton", "Krypton"); } }