Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/generated/resources/assets/anvilcraft/lang/en_ud.json
Original file line number Diff line number Diff line change
Expand Up @@ -776,6 +776,8 @@
"gui.anvilcraft.category.decay.random_tick": "ʞɔᴉʇ ɯopuɐɹ ɐ uo ʎɐɔǝp ꞁꞁᴉʍ ɹǝʇuǝɔ ʇɐ ʞɔoꞁq ǝɥ⟘",
"gui.anvilcraft.category.energy_weapon": "uodɐǝM ʎᵷɹǝuƎ",
"gui.anvilcraft.category.fast_cooking": "ᵷuᴉʞooƆ ʇsɐℲ",
"gui.anvilcraft.category.fast_cooking.consume_fluid": "%2$s ɟo ᗺɯ %1$d :ǝɯnsuoƆ",
"gui.anvilcraft.category.fast_cooking.produce_fluid": "%2$s ɟo ᗺɯ %1$d :ǝɔnpoɹԀ",
"gui.anvilcraft.category.fluid_mixing": "ᵷuᴉxᴉW pᴉnꞁℲ",
"gui.anvilcraft.category.item_compress": "ssǝɹdɯoƆ ɯǝʇI",
"gui.anvilcraft.category.item_compress.supercapacitor.chance": "sǝpoꞁdxǝ ǝsᴉʍɹǝɥʇo ⸵ɹoʇᴉɔɐdɐɔɹǝdnS ɐ ǝɔnpoɹd oʇ ǝɔuɐɥ% c0ϛ",
Expand Down Expand Up @@ -1710,7 +1712,7 @@
"tooltip.anvilcraft.item.large_cake": "dn noʎ sꞁꞁᴉɟ ǝʇᴉq ɥɔɐǝ 'sǝʇᴉq ㄥᘔ ˙ǝʞɐɔ ᵷᴉq ʎɹǝʌ ɐ 'ǝʞɐɔ Ɐ",
"tooltip.anvilcraft.item.large_cauldron": "uoɹpꞁnɐɔ ǝᵷnɥ ʎꞁǝɯǝɹʇxǝ uⱯ",
"tooltip.anvilcraft.item.large_cauldron.shift": "spᴉnꞁɟ ǝꞁdᴉʇꞁnɯ ǝɹoʇs uɐƆ\nsʞɔoꞁq ǝsɐq ʇuǝɹǝɟɟᴉp ɐᴉʌ sǝdʎʇ ᵷuᴉssǝɔoɹd ǝꞁdᴉʇꞁnɯ ɥʇᴉʍ ǝꞁqᴉʇɐdɯoƆ\nsǝdᴉɔǝɹ ᵷuᴉʇɟɐɹɔ uoɹpꞁnɐɔ ꞁꞁɐ ɯɹoɟɹǝd uɐƆ",
"tooltip.anvilcraft.item.large_fluid_tank": "ɹǝɟsuɐɹʇ pᴉnꞁɟ ɹoɟ sɹǝsuǝdsᴉᗡ ɥʇᴉʍ ʇɔɐɹǝʇuᴉ uɐƆ\nǝʇᴉuᴉɟuᴉ oʇ pǝʇɹǝʌuoɔ ǝq ꞁꞁᴉʍ ᗺ008ᘔ⥝ sǝɥɔɐǝɹ ʇɐɥʇ pᴉnꞁɟ ɟo ǝdʎʇ ɥɔɐǝ 'spᴉnꞁɟ ǝꞁdᴉʇꞁnɯ ǝɹoʇs uɐƆ\nǝᵷɐɹoʇs ꞁɐʇoʇ pǝʇᴉɯᴉꞁun ʞɔoꞁun sǝᵷuodS ɹǝᵷuǝW\nspᴉnꞁɟ ɟo ɹǝqɯnu ʎuɐ ʎq pǝɹɐɥs ᗺ0ᘔƐ sǝɹoʇS",
"tooltip.anvilcraft.item.large_fluid_tank": "ɹǝɟsuɐɹʇ pᴉnꞁɟ ɹoɟ sɹǝsuǝdsᴉᗡ ɥʇᴉʍ ʇɔɐɹǝʇuᴉ uɐƆ\nǝʇᴉuᴉɟuᴉ oʇ pǝʇɹǝʌuoɔ ǝq ꞁꞁᴉʍ ᗺ008ᘔ⥝ sǝɥɔɐǝɹ ʇɐɥʇ pᴉnꞁɟ ɟo ǝdʎʇ ɥɔɐǝ 'spᴉnꞁɟ ǝꞁdᴉʇꞁnɯ ǝɹoʇs uɐƆ\nǝᵷɐɹoʇs ꞁɐʇoʇ pǝʇᴉɯᴉꞁun ʞɔoꞁun sǝᵷuodS ɹǝᵷuǝW\nspᴉnꞁɟ ɟo ɹǝqɯnu ʎuɐ ʎq pǝɹɐɥs ᗺᘔ⥝ϛ sǝɹoʇS",
"tooltip.anvilcraft.item.large_laser": "Mʞ 9ϛᘔ sǝɯnsuoɔ 'ɹǝsɐꞁ ɟo sꞁǝʌǝꞁ ʎʇᴉsuǝʇuᴉ 9⥝ sʇndʇno 'sɹǝsɐꞁ 9⥝ oʇ ʇuǝꞁɐʌᴉnbƎ",
"tooltip.anvilcraft.item.laser_gun": "ꞁnɟɹǝʍod ʎꞁᵷuᴉsɐǝɹɔuᴉ sʍoɹᵷ ʇɐɥʇ ɹǝsɐꞁ ɐ ǝɹᴉɟ puɐ ɹǝʍod ǝɯnsuoɔ oʇ ʞɔᴉꞁɔ-ʇɥᵷᴉɹ pꞁoH",
"tooltip.anvilcraft.item.laser_gun.shift": "ɹoᴉʌɐɥǝq ɯɐǝq sʇᴉ sɹǝʇꞁɐ unᵷ ɹǝsɐꞁ ǝɥʇ ᵷuᴉʇuɐɥɔuƎ\n¡ᵷuᴉʇɐǝɥɹǝʌo ɹoɟ ʇno ɥɔʇɐʍ ʇnq 'ǝᵷɐɯɐp sǝsɐǝɹɔuᴉ ɹǝᵷuoꞁ ᵷuᴉᵷɹɐɥƆ\nsʞɔoꞁq ǝuᴉɯ osꞁɐ uɐɔ puɐ sqoɯ sǝᵷɐɯɐp ɹǝsɐꞁ ǝɥ⟘",
Expand Down
4 changes: 3 additions & 1 deletion src/generated/resources/assets/anvilcraft/lang/en_us.json
Original file line number Diff line number Diff line change
Expand Up @@ -776,6 +776,8 @@
"gui.anvilcraft.category.decay.random_tick": "The block at center will decay on a random tick",
"gui.anvilcraft.category.energy_weapon": "Energy Weapon",
"gui.anvilcraft.category.fast_cooking": "Fast Cooking",
"gui.anvilcraft.category.fast_cooking.consume_fluid": "Consume: %1$d mB of %2$s",
"gui.anvilcraft.category.fast_cooking.produce_fluid": "Produce: %1$d mB of %2$s",
"gui.anvilcraft.category.fluid_mixing": "Fluid Mixing",
"gui.anvilcraft.category.item_compress": "Item Compress",
"gui.anvilcraft.category.item_compress.supercapacitor.chance": "50% chance to produce a Supercapacitor; otherwise explodes",
Expand Down Expand Up @@ -1710,7 +1712,7 @@
"tooltip.anvilcraft.item.large_cake": "A cake, a very big cake. 27 bites, each bite fills you up",
"tooltip.anvilcraft.item.large_cauldron": "An extremely huge cauldron",
"tooltip.anvilcraft.item.large_cauldron.shift": "Can perform all cauldron crafting recipes\nCompatible with multiple processing types via different base blocks\nCan store multiple fluids",
"tooltip.anvilcraft.item.large_fluid_tank": "Stores 320B shared by any number of fluids\nMenger Sponges unlock unlimited total storage\nCan store multiple fluids, each type of fluid that reaches 12800B will be converted to infinite\nCan interact with Dispensers for fluid transfer",
"tooltip.anvilcraft.item.large_fluid_tank": "Stores 512B shared by any number of fluids\nMenger Sponges unlock unlimited total storage\nCan store multiple fluids, each type of fluid that reaches 12800B will be converted to infinite\nCan interact with Dispensers for fluid transfer",
"tooltip.anvilcraft.item.large_laser": "Equivalent to 16 lasers, outputs 16 intensity levels of laser, consumes 256 kW",
"tooltip.anvilcraft.item.laser_gun": "Hold right-click to consume power and fire a laser that grows increasingly powerful",
"tooltip.anvilcraft.item.laser_gun.shift": "The laser damages mobs and can also mine blocks\nCharging longer increases damage, but watch out for overheating!\nEnchanting the laser gun alters its beam behavior",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@
import dev.dubhe.anvilcraft.api.fluid.IFluidHandlerHolder;

public interface IEntityCauldron extends IFluidHandlerHolder {
/**
* 流体管网是否应按原版炼药锅的整容器规则转移此实体中的内容。
* 使用普通储罐实现的实体炼药锅可覆写此方法,以允许任意数量的流体转移。
*/
default boolean anvilcraft$usesWholeCauldronFluidTransfers() {
return true;
}

default boolean anvilcraft$isIgnited() {
return false;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package dev.dubhe.anvilcraft.api.fluid.network;

import dev.dubhe.anvilcraft.api.entity.IEntityCauldron;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import net.neoforged.neoforge.capabilities.Capabilities;
import net.neoforged.neoforge.fluids.CauldronFluidContent;
import net.neoforged.neoforge.fluids.capability.IFluidHandler;
import org.jetbrains.annotations.Nullable;

/** 查找方块单元中由方块或实体提供的流体容器。 */
public final class FluidContainerLookup {
/**
* 水平碰撞判定允许实体偏移约 0.001 个方块距离,同时仍判定两个面相接触。
*/
private static final double ENTITY_PIPE_CONTACT_TOLERANCE = 1.1E-3D;

private FluidContainerLookup() {
}

/**
* 查找占据 {@code pos} 的流体端点。方块能力优先于实体能力。
* 实体归属于其碰撞箱中心所在的方块单元,避免移动实体同时从多个相邻位置暴露能力。
*/
public static @Nullable Result find(Level level, BlockPos pos, @Nullable Direction side) {
BlockState state = level.getBlockState(pos);
BlockEntity blockEntity = level.getBlockEntity(pos);
IFluidHandler blockHandler = level.getCapability(
Capabilities.FluidHandler.BLOCK,
pos,
state,
blockEntity,
side
);
if (blockHandler != null) {
boolean cauldron = CauldronFluidContent.getForBlock(state.getBlock()) != null;
return new Result(blockHandler, cauldron, null);
}

Entity selected = null;
IFluidHandler selectedHandler = null;
for (Entity entity : level.getEntitiesOfClass(
Entity.class,
new AABB(pos),
candidate -> !candidate.isRemoved()
&& BlockPos.containing(candidate.getBoundingBox().getCenter()).equals(pos)
)) {
IFluidHandler handler = entity.getCapability(Capabilities.FluidHandler.ENTITY, side);
if (handler == null || selected != null && selected.getId() <= entity.getId()) {
continue;
}
selected = entity;
selectedHandler = handler;
}
boolean cauldron = selected instanceof IEntityCauldron entityCauldron
&& entityCauldron.anvilcraft$usesWholeCauldronFluidTransfers();
return selectedHandler == null ? null : new Result(selectedHandler, cauldron, selected);
}

/** 返回实体端点是否仍与相邻管道部件实际接触。 */
public static boolean isEntityConnectedToPipe(
Level level, BlockPos containerPos, Direction sideToPipe, Entity entity
) {
if (entity.isRemoved()
|| !BlockPos.containing(entity.getBoundingBox().getCenter()).equals(containerPos)
|| entity.getCapability(Capabilities.FluidHandler.ENTITY, sideToPipe) == null) {
return false;
}

BlockPos pipePos = containerPos.relative(sideToPipe);
if (!level.isLoaded(pipePos)) {
return false;
}
Vec3 towardPipe = Vec3.atLowerCornerOf(sideToPipe.getNormal()).scale(ENTITY_PIPE_CONTACT_TOLERANCE);
AABB contactBox = entity.getBoundingBox().expandTowards(towardPipe);
return level.getBlockState(pipePos).getCollisionShape(level, pipePos).toAabbs().stream()
.map(box -> box.move(pipePos.getX(), pipePos.getY(), pipePos.getZ()))
.anyMatch(contactBox::intersects);
}

/** 在单个网络端点发现的流体处理器及其转移语义。 */
public record Result(IFluidHandler handler, boolean cauldron, @Nullable Entity entity) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.entity.Entity;
import net.neoforged.neoforge.fluids.capability.IFluidHandler;
import org.jetbrains.annotations.Nullable;

/**
* 流体网络中的一个端点容器。
Expand All @@ -16,13 +18,25 @@
* @param handler 容器的 {@link IFluidHandler}
* @param effectiveHeight 等效高度 = 容器 Y + 沿路径累计的泵势场偏移
* @param cauldron 该端点是否为 NeoForge 注册的炼药锅
* @param entity 提供该端点的实体;方块容器为 {@code null}
*/
public record FluidEndpoint(
BlockPos containerPos,
BlockPos fromPipePos,
Direction sideToPipe,
IFluidHandler handler,
int effectiveHeight,
boolean cauldron
boolean cauldron,
@Nullable Entity entity
) {
public FluidEndpoint(
BlockPos containerPos,
BlockPos fromPipePos,
Direction sideToPipe,
IFluidHandler handler,
int effectiveHeight,
boolean cauldron
) {
this(containerPos, fromPipePos, sideToPipe, handler, effectiveHeight, cauldron, null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@
import net.minecraft.core.Direction;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
import net.neoforged.neoforge.capabilities.Capabilities;
import net.neoforged.neoforge.fluids.CauldronFluidContent;
import net.neoforged.neoforge.fluids.capability.IFluidHandler;

import java.util.ArrayDeque;
Expand Down Expand Up @@ -53,8 +51,7 @@ public static boolean isPipePart(BlockState state) {

/** 判断某位置是否为流体容器(提供 IFluidHandler 且非管道部件)。供管理器剔除失效容器用。 */
public static boolean isContainer(Level level, BlockPos pos) {
return level.getCapability(Capabilities.FluidHandler.BLOCK, pos, null) != null
&& !isPipePart(level.getBlockState(pos));
return !isPipePart(level.getBlockState(pos)) && FluidContainerLookup.find(level, pos, null) != null;
}

/**
Expand Down Expand Up @@ -85,11 +82,11 @@ private static int pumpHalfLift(Level level, BlockPos pumpPos) {
? PUMP_HALF_LIFT : 0;
}

private static IFluidHandler containerHandler(Level level, BlockPos pos, Direction sideToPipe) {
private static FluidContainerLookup.Result container(Level level, BlockPos pos, Direction sideToPipe) {
if (isPipePart(level.getBlockState(pos))) {
return null;
}
return level.getCapability(Capabilities.FluidHandler.BLOCK, pos, sideToPipe);
return FluidContainerLookup.find(level, pos, sideToPipe);
}

/**
Expand Down Expand Up @@ -297,16 +294,23 @@ private static void addEndpointIfContainer(
if (endpoints.containsKey(immutablePos)) {
return;
}
IFluidHandler handler = containerHandler(level, containerPos, sideToPipe);
if (handler == null) {
FluidContainerLookup.Result container = container(level, containerPos, sideToPipe);
if (container == null) {
return;
}
IFluidHandler handler = container.handler();
if (!seenHandlers.add(handler)) {
return;
}
int effectiveHeight = containerPos.getY() + phi;
boolean cauldron = CauldronFluidContent.getForBlock(level.getBlockState(containerPos).getBlock()) != null;
endpoints.put(immutablePos, new FluidEndpoint(
immutablePos, attachPipePos.immutable(), sideToPipe, handler, effectiveHeight, cauldron));
immutablePos,
attachPipePos.immutable(),
sideToPipe,
handler,
effectiveHeight,
container.cauldron(),
container.entity()
));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.Level;
import net.neoforged.neoforge.fluids.CauldronFluidContent;
import net.neoforged.neoforge.fluids.FluidStack;
import net.neoforged.neoforge.fluids.capability.IFluidHandler;

Expand Down Expand Up @@ -74,6 +73,8 @@ public static int speedForHeightDiff(int heightDiff) {
private final Map<BlockPos, Map<Direction, Direction>> faceFlow;
private final List<FluidEndpoint> endpoints;
private final List<FluidEndpoint> cauldronEndpoints;
private final List<FluidEndpoint> entityEndpoints;
private final Set<FluidEndpoint> disconnectedEntityEndpoints = new HashSet<>();
private final boolean directionalConstraints;
/** 端点按等效高度<b>降序</b>预排序(作为源的遍历顺序),构建时排一次,避免每 tick 重排。 */
private final List<FluidEndpoint> sourcesByHeightDesc;
Expand Down Expand Up @@ -113,10 +114,14 @@ public FluidPipeNetwork(
this.endpoints = endpoints;
this.directionalConstraints = !valves.isEmpty() || !diodes.isEmpty() || !faceFlow.isEmpty();
this.cauldronEndpoints = new ArrayList<>();
this.entityEndpoints = new ArrayList<>();
for (FluidEndpoint endpoint : endpoints) {
if (endpoint.cauldron()) {
this.cauldronEndpoints.add(endpoint);
}
if (endpoint.entity() != null) {
this.entityEndpoints.add(endpoint);
}
}
// 预排序一次(缓存网络下每 tick 复用)
this.sourcesByHeightDesc = new ArrayList<>(endpoints);
Expand Down Expand Up @@ -146,6 +151,9 @@ public void tick() {
}
// 源已按等效高度降序预排序:高处先流,一 tick 内可级联下泄
for (FluidEndpoint source : sourcesByHeightDesc) {
if (!isEndpointConnected(source)) {
continue;
}
distributeFromSource(source);
}
}
Expand All @@ -167,7 +175,7 @@ public void pushFromExternalSource(
}
reachabilityCache.clear();
distributeFromSource(new FluidEndpoint(
srcPos, entryPipePos, null, srcHandler, sourceEffectiveHeight, false));
srcPos, entryPipePos, null, srcHandler, sourceEffectiveHeight, false, null));
}

/** 从单个源端点向所有更低的端点分配其持有的流体。 */
Expand Down Expand Up @@ -337,6 +345,9 @@ private boolean hasHigherPrioritySource(
FluidEndpoint source, FluidStack stored, TreeMap<Integer, List<FluidEndpoint>> targetsByHeight
) {
for (FluidEndpoint higher : sourcesByHeightDesc) {
if (!isEndpointConnected(higher)) {
continue;
}
if (higher.effectiveHeight() <= source.effectiveHeight()) {
return false;
}
Expand Down Expand Up @@ -383,7 +394,10 @@ private TreeMap<Integer, List<FluidEndpoint>> collectTargetsByHeight(
private boolean canTarget(
FluidEndpoint source, int tankIdx, FluidEndpoint target, FluidStack stored, Reachability reach
) {
if (target == source || target.effectiveHeight() >= source.effectiveHeight()) {
if (!isEndpointConnected(source)
|| !isEndpointConnected(target)
|| target == source
|| target.effectiveHeight() >= source.effectiveHeight()) {
return false;
}
if (target.handler().equals(source.handler())) {
Expand Down Expand Up @@ -437,18 +451,41 @@ private void fillFromFullCauldron(
}

private boolean canTickEndpoints() {
disconnectedEntityEndpoints.clear();
for (FluidEndpoint endpoint : entityEndpoints) {
if (!FluidContainerLookup.isEntityConnectedToPipe(
level,
endpoint.containerPos(),
endpoint.sideToPipe(),
endpoint.entity()
)) {
disconnectedEntityEndpoints.add(endpoint);
}
}
for (FluidEndpoint endpoint : cauldronEndpoints) {
if (endpoint.entity() != null) {
continue;
}
if (!level.isLoaded(endpoint.containerPos())) {
return false;
}
if (CauldronFluidContent.getForBlock(level.getBlockState(endpoint.containerPos()).getBlock()) == null) {
FluidContainerLookup.Result container = FluidContainerLookup.find(
level,
endpoint.containerPos(),
endpoint.sideToPipe()
);
if (container == null || !container.cauldron()) {
FluidNetworkManager.INSTANCE.markDirty(level);
return false;
}
}
return true;
}

private boolean isEndpointConnected(FluidEndpoint endpoint) {
return !disconnectedEntityEndpoints.contains(endpoint);
}

private boolean fillFirstWholeCauldronTarget(
FluidEndpoint source, int tankIdx, List<FluidEndpoint> group,
Map<BlockPos, List<ValveState>> pathValves
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ public class ItemTooltipManager {
);
NORMAL.put(
ModBlocks.LARGE_FLUID_TANK.asItem(), """
Stores 320B shared by any number of fluids
Stores 512B shared by any number of fluids
Menger Sponges unlock unlimited total storage
Can store multiple fluids, each type of fluid that reaches 12800B will be converted to infinite
Can interact with Dispensers for fluid transfer"""
Expand Down
Loading
Loading