diff --git a/module.yukkuri/src/main/java/dev/anvilcraft/lib/v2/yukkuri/api/vapor/VaporizationManager.java b/module.yukkuri/src/main/java/dev/anvilcraft/lib/v2/yukkuri/api/vapor/VaporizationManager.java index 309c0ecb..e38cba1a 100644 --- a/module.yukkuri/src/main/java/dev/anvilcraft/lib/v2/yukkuri/api/vapor/VaporizationManager.java +++ b/module.yukkuri/src/main/java/dev/anvilcraft/lib/v2/yukkuri/api/vapor/VaporizationManager.java @@ -2,6 +2,7 @@ import dev.anvilcraft.lib.v2.yukkuri.Yukkuri; import dev.anvilcraft.lib.v2.yukkuri.api.event.LargeCauldronProcessEvent; +import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.server.level.ServerLevel; import net.neoforged.neoforge.common.NeoForge; @@ -43,9 +44,28 @@ public static int receiveVapor(VaporizationContext context, VaporStack vapor, Va ); } + /** + * Returns whether all nine cells directly above the cauldron are sealed by full collision blocks. + * Consumers handle their own exhaust path because their input structure may itself occupy these cells. + */ + public static boolean isOutletBlocked(VaporizationContext context) { + BlockPos center = context.outletPos(); + for (int x = -1; x <= 1; x++) { + for (int z = -1; z <= 1; z++) { + BlockPos pos = center.offset(x, 0, z); + if (!context.level().getBlockState(pos).isCollisionShapeFullBlock(context.level(), pos)) { + return false; + } + } + } + return true; + } + private static void processFirstSource(VaporizationContext context) { IVaporConsumer consumer = findConsumer(context); - boolean sealedOutlet = consumer != null && consumer.sealsOutlet(context); + boolean sealedOutlet = consumer == null + ? isOutletBlocked(context) + : consumer.sealsOutlet(context); for (VaporizationSource source : VaporizationSources.getSources()) { FluidStack available = context.topFluid(); if (available.isEmpty()) return; @@ -86,7 +106,7 @@ private static void processFirstSource(VaporizationContext context) { 0, delivery.amount() ); - if (delivered != delivery.amount()) { + if (delivered != delivery.amount() && consumer.sealsOutlet(context)) { Yukkuri.LOGGER.error("Vapor consumer at {} accepted {} mB after simulating {} mB", context.outletPos(), delivered, delivery.amount()); } diff --git a/module.yukkuri/src/main/java/dev/anvilcraft/lib/v2/yukkuri/api/vapor/VaporizationSource.java b/module.yukkuri/src/main/java/dev/anvilcraft/lib/v2/yukkuri/api/vapor/VaporizationSource.java index df7fcdb8..881022fc 100644 --- a/module.yukkuri/src/main/java/dev/anvilcraft/lib/v2/yukkuri/api/vapor/VaporizationSource.java +++ b/module.yukkuri/src/main/java/dev/anvilcraft/lib/v2/yukkuri/api/vapor/VaporizationSource.java @@ -19,7 +19,11 @@ default int priority() { @Nullable VaporizationOffer createOffer(VaporizationContext context, FluidStack availableInput, int maxVapor); - /** Consumes source-specific resources and emits source-specific effects after the cauldron input is drained. */ + /** + * Consumes source-specific resources and emits source-specific effects after the cauldron input is drained. + * The committed input amount is the exact amount vaporized this tick, so effects should derive their density, + * lifetime, and travel distance from {@code offer.input().getAmount()}. + */ default void commit(VaporizationContext context, VaporizationOffer offer) { } }