diff --git a/src/generated/resources/assets/anvilcraft/lang/en_ud.json b/src/generated/resources/assets/anvilcraft/lang/en_ud.json index 9f595874c5..ce1ec7e16b 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", @@ -515,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": "ǝʇɐɹƆ ǝᵷɹɐꞀ", @@ -561,6 +565,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ᴉԀ", @@ -738,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ꞁᗺ", @@ -988,6 +994,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 +1049,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 ɐ ʞɔᴉꞁɔ-ʇɥᵷᴉᴚ", @@ -1049,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ɐǝꞀ", @@ -1072,6 +1082,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": "ꞁꞁᴉԀ", @@ -1154,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 9881914221..f4821183c8 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", @@ -515,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", @@ -561,6 +565,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", @@ -738,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", @@ -988,6 +994,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 +1049,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", @@ -1049,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", @@ -1072,6 +1082,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", @@ -1154,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/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/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/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/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 6e504011e9..b4b558437b 100644 --- a/src/generated/resources/data/c/tags/item/buckets.json +++ b/src/generated/resources/data/c/tags/item/buckets.json @@ -18,6 +18,12 @@ "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", + "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 7d78acfbb5..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 @@ -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; @@ -49,6 +50,22 @@ 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; + /** 气体压力排序的分辨率(在 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 long GAS_PRESSURE_EPSILON = 1; + /** 气体均衡每 tick 的最大轮数,限制 O(n²) 遍历成本。 */ + private static final int GAS_MAX_ROUNDS = 16; /** * 按高度差计算流速(线性增长): @@ -168,6 +185,8 @@ public void tick() { } distributeFromSource(source); } + // 气体由压强驱动在网络内均衡,不走重力/扬程分配 + equilibrateGases(); } /** @@ -256,6 +275,258 @@ 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)); + } + } + } + int[] budget = new int[]{GAS_EQUILIBRIUM_BUDGET}; + for (FluidStack gasType : gasTypes) { + List candidates = gasCandidates(gasType); + if (candidates.size() < 2) { + continue; + } + equilibrateGasType(gasType, candidates, budget); + } + } + + /** 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[] 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() && budget[0] > 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; + } + long hiPressure = gasPressure(hi, fluidType); + Reachability reach = directionalConstraints + ? computeReachableCached(hi.fromPipePos(), hiStored) + : null; + Map> pathValves = reach == null ? Map.of() : reach.pathValves(); + FluidEndpoint bestTarget = null; + long 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; + } + long loPressure = gasPressure(lo, fluidType); + long 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, pathValves, reach); + if (moved > 0) { + budget[0] -= moved; + progressed = true; + } + } + if (!progressed) { + break; + } + } + } + + /** + * Transfer driven by a single pressure difference: move exactly enough to equalize the two + * pressures, capped only by valve allowance, source stock and target free space. + */ + private int transferGas( + FluidEndpoint hi, FluidEndpoint lo, FluidStack fluidType, + 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 valveLimit = minValveRemaining(pathValves.get(lo.fromPipePos())); + int want; + if (isInfiniteGasSink(lo.handler())) { + // 空创造流体储罐(无限汇):吸收源端全部可转移气体,至源排空或被阀门限制 + want = Math.min(hiStored, valveLimit); + } else 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; + } + 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. 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 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 += amount; + } + } + int freeSpace = Math.max(0, totalCapacity - totalStored); + return new int[]{stored, stored + freeSpace}; + } + + /** + * 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 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; + } + 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; + } + + /** + * 创造流体储罐(InfinityFluidTank)且当前搭载该气体时视为无限气体源: + * 存量无限,压力设为最大,持续向网络外扩散气体。 + */ + private static boolean isInfiniteGasSource(IFluidHandler handler, FluidStack fluidType) { + if (!(handler instanceof InfinityFluidTank endless) || endless.isEmpty()) { + return false; + } + return FluidStack.isSameFluidSameComponents(endless.getFluid(), fluidType); + } + + /** + * 空创造流体储罐(InfinityFluidTank 且未搭载任何流体)视为无限气体汇: + * 压力无穷小,任意相连的源都会把气体扩散进去并被销毁。 + */ + private static boolean isInfiniteGasSink(IFluidHandler handler) { + return handler instanceof InfinityFluidTank endless && endless.isEmpty(); + } + /** 从单个源端点向所有更低的端点分配其持有的流体。 */ private void distributeFromSource(FluidEndpoint source) { // 源接管口朝本源容器那一面若装止逆阀,其允许方向必须朝网络(背离容器),否则本源无法向网络排液 @@ -271,6 +542,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..21dc119189 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,13 @@ 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"); + provider.add("block.anvilcraft.xenon", "Xenon"); + provider.add("block.anvilcraft.krypton", "Krypton"); } } diff --git a/src/main/java/dev/dubhe/anvilcraft/event/RegisterColorHandlersEventListener.java b/src/main/java/dev/dubhe/anvilcraft/event/RegisterColorHandlersEventListener.java index 0907115f45..ccbb0b5880 100644 --- a/src/main/java/dev/dubhe/anvilcraft/event/RegisterColorHandlersEventListener.java +++ b/src/main/java/dev/dubhe/anvilcraft/event/RegisterColorHandlersEventListener.java @@ -4,15 +4,18 @@ 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; 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) { @@ -40,5 +43,13 @@ 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, + ModItems.XENON_BUCKET, + ModItems.KRYPTON_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..2ebccc6e68 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,122 @@ 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) + ); + + 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") + .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) + ); + } + + 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(); @@ -324,6 +441,67 @@ 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 + ); + 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( 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..390ffbba38 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,51 @@ 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 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 465a76d091..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 @@ -112,6 +112,12 @@ 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); + 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 8c1c939d77..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 @@ -275,7 +275,13 @@ 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); + content.accept(ModItems.XENON_BUCKET); + content.accept(ModItems.KRYPTON_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(),