Skip to content

Commit f2af3af

Browse files
authored
Merge pull request #59 from CubesterYT/fluid-vessel
Platinum Fluid Vessel
2 parents 9d564e1 + 202c20c commit f2af3af

91 files changed

Lines changed: 8351 additions & 308 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

build.gradle

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,9 +152,10 @@ dependencies {
152152
compileOnly "curse.maven:createindustry-693815:7236535"
153153
//CC
154154
compileOnly "cc.tweaked:cc-tweaked-${minecraft_version}-forge:${cc_version}"
155-
//JEI, create addition, Jade
155+
//JEI, create addition, Create: Connected, Jade
156156
runtimeOnly "mezz.jei:jei-${minecraft_version}-neoforge:19.25.0.322"
157157
runtimeOnly files("libs/createaddition-1.5.10.jar")
158+
runtimeOnly files("libs/create_connected-1.3.2-mc${minecraft_version}.jar")
158159
runtimeOnly files("libs/Jade-${minecraft_version}-NeoForge-15.10.5.jar")
159160
//KFF
160161
implementation "thedarkcolour:kotlinforforge-neoforge:5.9.0"

error log.txt

Lines changed: 0 additions & 303 deletions
This file was deleted.
Lines changed: 359 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,359 @@
1+
package dev.propulsionteam.propulsionsimulated.content.platinum;
2+
3+
import com.simibubi.create.api.connectivity.ConnectivityHandler;
4+
import com.simibubi.create.content.equipment.wrench.IWrenchable;
5+
import com.simibubi.create.content.fluids.transfer.GenericItemEmptying;
6+
import com.simibubi.create.content.fluids.transfer.GenericItemFilling;
7+
import com.simibubi.create.foundation.advancement.AdvancementBehaviour;
8+
import com.simibubi.create.foundation.block.IBE;
9+
import com.simibubi.create.foundation.blockEntity.ComparatorUtil;
10+
import com.simibubi.create.foundation.fluid.FluidHelper;
11+
import com.simibubi.create.foundation.fluid.FluidHelper.FluidExchange;
12+
import dev.propulsionteam.propulsionsimulated.registries.PropulsionBlockEntities;
13+
import net.minecraft.core.BlockPos;
14+
import net.minecraft.core.Direction;
15+
import net.minecraft.core.particles.BlockParticleOption;
16+
import net.minecraft.core.particles.ParticleTypes;
17+
import net.minecraft.sounds.SoundEvent;
18+
import net.minecraft.sounds.SoundEvents;
19+
import net.minecraft.sounds.SoundSource;
20+
import net.minecraft.util.Mth;
21+
import net.minecraft.util.StringRepresentable;
22+
import net.minecraft.world.InteractionHand;
23+
import net.minecraft.world.InteractionResult;
24+
import net.minecraft.world.ItemInteractionResult;
25+
import net.minecraft.world.entity.Entity;
26+
import net.minecraft.world.entity.LivingEntity;
27+
import net.minecraft.world.entity.player.Player;
28+
import net.minecraft.world.item.ItemStack;
29+
import net.minecraft.world.item.context.BlockPlaceContext;
30+
import net.minecraft.world.item.context.UseOnContext;
31+
import net.minecraft.world.level.BlockGetter;
32+
import net.minecraft.world.level.Level;
33+
import net.minecraft.world.level.LevelAccessor;
34+
import net.minecraft.world.level.LevelReader;
35+
import net.minecraft.world.level.block.Block;
36+
import net.minecraft.world.level.block.Mirror;
37+
import net.minecraft.world.level.block.Rotation;
38+
import net.minecraft.world.level.block.SoundType;
39+
import net.minecraft.world.level.block.entity.BlockEntity;
40+
import net.minecraft.world.level.block.entity.BlockEntityType;
41+
import net.minecraft.world.level.block.state.BlockState;
42+
import net.minecraft.world.level.block.state.StateDefinition.Builder;
43+
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
44+
import net.minecraft.world.level.block.state.properties.BooleanProperty;
45+
import net.minecraft.world.level.block.state.properties.EnumProperty;
46+
import net.minecraft.world.level.material.Fluid;
47+
import net.minecraft.world.phys.BlockHitResult;
48+
import net.minecraft.world.phys.Vec3;
49+
import net.minecraft.world.phys.shapes.CollisionContext;
50+
import net.minecraft.world.phys.shapes.Shapes;
51+
import net.minecraft.world.phys.shapes.VoxelShape;
52+
import net.neoforged.neoforge.capabilities.Capabilities;
53+
import net.neoforged.neoforge.common.util.DeferredSoundType;
54+
import net.neoforged.neoforge.fluids.FluidStack;
55+
import net.neoforged.neoforge.fluids.capability.IFluidHandler;
56+
57+
import java.util.Locale;
58+
59+
public class PlatinumFluidVesselBlock extends Block implements IWrenchable, IBE<PlatinumFluidVesselBlockEntity> {
60+
61+
public static final BooleanProperty POSITIVE = BooleanProperty.create("positive");
62+
public static final BooleanProperty NEGATIVE = BooleanProperty.create("negative");
63+
public static final EnumProperty<Direction.Axis> AXIS = BlockStateProperties.HORIZONTAL_AXIS;
64+
public static final EnumProperty<Shape> SHAPE = EnumProperty.create("shape", Shape.class);
65+
66+
public PlatinumFluidVesselBlock(Properties properties) {
67+
super(properties);
68+
registerDefaultState(defaultBlockState().setValue(POSITIVE, true)
69+
.setValue(NEGATIVE, true)
70+
.setValue(AXIS, Direction.Axis.X)
71+
.setValue(SHAPE, Shape.WINDOW));
72+
}
73+
74+
@Override
75+
public void setPlacedBy(Level level, BlockPos pos, BlockState state, LivingEntity placer, ItemStack stack) {
76+
super.setPlacedBy(level, pos, state, placer, stack);
77+
AdvancementBehaviour.setPlacedBy(level, pos, placer);
78+
}
79+
80+
public static boolean isVessel(BlockState state) {
81+
return state.getBlock() instanceof PlatinumFluidVesselBlock;
82+
}
83+
84+
@Override
85+
public void onPlace(BlockState state, Level world, BlockPos pos, BlockState oldState, boolean moved) {
86+
if (oldState.getBlock() == state.getBlock() || moved) {
87+
return;
88+
}
89+
withBlockEntityDo(world, pos, PlatinumFluidVesselBlockEntity::updateConnectivity);
90+
}
91+
92+
@Override
93+
protected void createBlockStateDefinition(Builder<Block, BlockState> builder) {
94+
builder.add(POSITIVE, NEGATIVE, AXIS, SHAPE);
95+
}
96+
97+
@Override
98+
public BlockState getStateForPlacement(BlockPlaceContext context) {
99+
if (context.getPlayer() == null || !context.getPlayer().isShiftKeyDown()) {
100+
BlockState placedOn = context.getLevel()
101+
.getBlockState(context.getClickedPos().relative(context.getClickedFace().getOpposite()));
102+
Direction.Axis preferredAxis = placedOn.getOptionalValue(AXIS).orElse(null);
103+
if (preferredAxis != null) {
104+
return this.defaultBlockState().setValue(AXIS, preferredAxis);
105+
}
106+
}
107+
return this.defaultBlockState().setValue(AXIS, context.getHorizontalDirection().getAxis());
108+
}
109+
110+
@Override
111+
public int getLightEmission(BlockState state, BlockGetter world, BlockPos pos) {
112+
PlatinumFluidVesselBlockEntity vesselAt = ConnectivityHandler.partAt(getBlockEntityType(), world, pos);
113+
if (vesselAt == null) {
114+
return 0;
115+
}
116+
PlatinumFluidVesselBlockEntity controllerBE = vesselAt.getControllerBE();
117+
if (controllerBE == null || !controllerBE.hasWindow()) {
118+
return 0;
119+
}
120+
return vesselAt.getLuminosity();
121+
}
122+
123+
@Override
124+
public InteractionResult onWrenched(BlockState state, UseOnContext context) {
125+
withBlockEntityDo(context.getLevel(), context.getClickedPos(), PlatinumFluidVesselBlockEntity::toggleWindows);
126+
return InteractionResult.SUCCESS;
127+
}
128+
129+
static final VoxelShape CAMPFIRE_SMOKE_CLIP = Block.box(0, 4, 0, 16, 16, 16);
130+
131+
@Override
132+
public VoxelShape getCollisionShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
133+
if (context == CollisionContext.empty()) {
134+
return CAMPFIRE_SMOKE_CLIP;
135+
}
136+
return state.getShape(level, pos);
137+
}
138+
139+
@Override
140+
public VoxelShape getBlockSupportShape(BlockState state, BlockGetter level, BlockPos pos) {
141+
return Shapes.block();
142+
}
143+
144+
@Override
145+
public BlockState updateShape(BlockState state, Direction direction, BlockState neighborState,
146+
LevelAccessor level, BlockPos currentPos, BlockPos neighborPos) {
147+
if (direction == Direction.DOWN && neighborState.getBlock() != this) {
148+
withBlockEntityDo(level, currentPos, PlatinumFluidVesselBlockEntity::updateBoilerTemperature);
149+
}
150+
return state;
151+
}
152+
153+
@Override
154+
protected ItemInteractionResult useItemOn(ItemStack stack, BlockState state, Level level, BlockPos pos,
155+
Player player, InteractionHand hand, BlockHitResult hitResult) {
156+
boolean onClient = level.isClientSide;
157+
158+
if (stack.isEmpty()) {
159+
return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION;
160+
}
161+
162+
FluidExchange exchange = null;
163+
PlatinumFluidVesselBlockEntity be = ConnectivityHandler.partAt(getBlockEntityType(), level, pos);
164+
if (be == null) {
165+
return ItemInteractionResult.FAIL;
166+
}
167+
168+
IFluidHandler vesselCapability = level.getCapability(Capabilities.FluidHandler.BLOCK, be.getBlockPos(), null);
169+
if (vesselCapability == null) {
170+
return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION;
171+
}
172+
FluidStack prevFluidInVessel = vesselCapability.getFluidInTank(0).copy();
173+
174+
if (FluidHelper.tryEmptyItemIntoBE(level, player, hand, stack, be)) {
175+
exchange = FluidExchange.ITEM_TO_TANK;
176+
} else if (FluidHelper.tryFillItemFromBE(level, player, hand, stack, be)) {
177+
exchange = FluidExchange.TANK_TO_ITEM;
178+
}
179+
180+
if (exchange == null) {
181+
if (GenericItemEmptying.canItemBeEmptied(level, stack) || GenericItemFilling.canItemBeFilled(level, stack)) {
182+
return ItemInteractionResult.SUCCESS;
183+
}
184+
return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION;
185+
}
186+
187+
SoundEvent soundEvent = null;
188+
BlockState fluidState = null;
189+
FluidStack fluidInVessel = vesselCapability.getFluidInTank(0);
190+
191+
if (exchange == FluidExchange.ITEM_TO_TANK) {
192+
Fluid fluid = fluidInVessel.getFluid();
193+
fluidState = fluid.defaultFluidState().createLegacyBlock();
194+
soundEvent = FluidHelper.getEmptySound(fluidInVessel);
195+
}
196+
197+
if (exchange == FluidExchange.TANK_TO_ITEM) {
198+
Fluid fluid = prevFluidInVessel.getFluid();
199+
fluidState = fluid.defaultFluidState().createLegacyBlock();
200+
soundEvent = FluidHelper.getFillSound(prevFluidInVessel);
201+
}
202+
203+
if (soundEvent != null && !onClient) {
204+
float pitch = Mth.clamp(1 - (1f * fluidInVessel.getAmount() / (PlatinumFluidVesselBlockEntity.getCapacityMultiplier() * 16 * 2f)), 0, 1);
205+
pitch /= 1.5f;
206+
pitch += .5f;
207+
pitch += (level.random.nextFloat() - .5f) / 4f;
208+
level.playSound(null, pos, soundEvent, SoundSource.BLOCKS, .5f, pitch);
209+
}
210+
211+
if (!FluidStack.isSameFluidSameComponents(fluidInVessel, prevFluidInVessel)) {
212+
PlatinumFluidVesselBlockEntity controllerBE = be.getControllerBE();
213+
if (controllerBE != null) {
214+
if (fluidState != null && onClient) {
215+
BlockParticleOption blockParticleData = new BlockParticleOption(ParticleTypes.BLOCK, fluidState);
216+
float fluidLevel = (float) fluidInVessel.getAmount() / vesselCapability.getTankCapacity(0);
217+
218+
boolean reversed = fluidInVessel.getFluid().getFluidType().isLighterThanAir();
219+
if (reversed) {
220+
fluidLevel = 1 - fluidLevel;
221+
}
222+
223+
Vec3 vec = hitResult.getLocation();
224+
vec = new Vec3(vec.x, controllerBE.getBlockPos().getY() + fluidLevel * (controllerBE.getHeight() - .5f) + .25f, vec.z);
225+
Vec3 motion = player.position().subtract(vec).scale(1 / 20f);
226+
vec = vec.add(motion);
227+
level.addParticle(blockParticleData, vec.x, vec.y, vec.z, motion.x, motion.y, motion.z);
228+
return ItemInteractionResult.SUCCESS;
229+
}
230+
231+
controllerBE.sendDataImmediately();
232+
controllerBE.setChanged();
233+
}
234+
}
235+
236+
return ItemInteractionResult.SUCCESS;
237+
}
238+
239+
@Override
240+
public void onRemove(BlockState state, Level world, BlockPos pos, BlockState newState, boolean isMoving) {
241+
if (state.hasBlockEntity() && (state.getBlock() != newState.getBlock() || !newState.hasBlockEntity())) {
242+
BlockEntity be = world.getBlockEntity(pos);
243+
if (!(be instanceof PlatinumFluidVesselBlockEntity vesselBE)) {
244+
return;
245+
}
246+
world.removeBlockEntity(pos);
247+
ConnectivityHandler.splitMulti(vesselBE);
248+
}
249+
}
250+
251+
@Override
252+
public Class<PlatinumFluidVesselBlockEntity> getBlockEntityClass() {
253+
return PlatinumFluidVesselBlockEntity.class;
254+
}
255+
256+
@Override
257+
public BlockEntityType<? extends PlatinumFluidVesselBlockEntity> getBlockEntityType() {
258+
return PropulsionBlockEntities.PLATINUM_FLUID_VESSEL_BLOCK_ENTITY.get();
259+
}
260+
261+
@Override
262+
public BlockState mirror(BlockState state, Mirror mirror) {
263+
if (mirror == Mirror.NONE) {
264+
return state;
265+
}
266+
Direction.Axis mirrorAxis = mirror == Mirror.FRONT_BACK ? Direction.Axis.X : Direction.Axis.Z;
267+
Direction.Axis axis = state.getValue(AXIS);
268+
if (axis == mirrorAxis) {
269+
return state.setValue(POSITIVE, state.getValue(NEGATIVE))
270+
.setValue(NEGATIVE, state.getValue(POSITIVE));
271+
}
272+
return state;
273+
}
274+
275+
@Override
276+
public BlockState rotate(BlockState state, Rotation rotation) {
277+
for (int i = 0; i < rotation.ordinal(); i++) {
278+
state = rotateOnce(state);
279+
}
280+
return state;
281+
}
282+
283+
private BlockState rotateOnce(BlockState state) {
284+
Direction.Axis axis = state.getValue(AXIS);
285+
if (axis == Direction.Axis.X) {
286+
return state.setValue(AXIS, Direction.Axis.Z);
287+
}
288+
if (axis == Direction.Axis.Z) {
289+
return state.setValue(AXIS, Direction.Axis.X)
290+
.setValue(POSITIVE, state.getValue(NEGATIVE))
291+
.setValue(NEGATIVE, state.getValue(POSITIVE));
292+
}
293+
return state;
294+
}
295+
296+
public enum Shape implements StringRepresentable {
297+
PLAIN,
298+
WINDOW,
299+
WINDOW_TOP,
300+
WINDOW_MIDDLE,
301+
WINDOW_BOTTOM,
302+
WINDOW_SINGLE,
303+
WINDOW_TOP_SINGLE,
304+
WINDOW_MIDDLE_SINGLE,
305+
WINDOW_BOTTOM_SINGLE;
306+
307+
@Override
308+
public String getSerializedName() {
309+
return name().toLowerCase(Locale.ROOT);
310+
}
311+
312+
public Shape nonSingleVariant() {
313+
return switch (this) {
314+
case WINDOW_SINGLE -> WINDOW;
315+
case WINDOW_TOP_SINGLE -> WINDOW_TOP;
316+
case WINDOW_MIDDLE_SINGLE -> WINDOW_MIDDLE;
317+
case WINDOW_BOTTOM_SINGLE -> WINDOW_BOTTOM;
318+
default -> this;
319+
};
320+
}
321+
}
322+
323+
public enum WindowType implements StringRepresentable {
324+
SIDE_WIDE,
325+
SIDE_NARROW_ENDS,
326+
SIDE_NARROW_THIRDS,
327+
SIDE_HORIZONTAL;
328+
329+
@Override
330+
public String getSerializedName() {
331+
return name().toLowerCase(Locale.ROOT);
332+
}
333+
}
334+
335+
public static final SoundType SILENCED_METAL =
336+
new DeferredSoundType(0.1F, 1.5F, () -> SoundEvents.METAL_BREAK, () -> SoundEvents.METAL_STEP,
337+
() -> SoundEvents.METAL_PLACE, () -> SoundEvents.METAL_HIT, () -> SoundEvents.METAL_FALL);
338+
339+
@Override
340+
public SoundType getSoundType(BlockState state, LevelReader world, BlockPos pos, Entity entity) {
341+
SoundType soundType = super.getSoundType(state, world, pos, entity);
342+
if (entity != null && entity.getPersistentData().contains("SilenceVesselSound")) {
343+
return SILENCED_METAL;
344+
}
345+
return soundType;
346+
}
347+
348+
@Override
349+
public boolean hasAnalogOutputSignal(BlockState state) {
350+
return true;
351+
}
352+
353+
@Override
354+
public int getAnalogOutputSignal(BlockState blockState, Level world, BlockPos pos) {
355+
return getBlockEntityOptional(world, pos).map(PlatinumFluidVesselBlockEntity::getControllerBE)
356+
.map(be -> ComparatorUtil.fractionToRedstoneLevel(be.getFillState()))
357+
.orElse(0);
358+
}
359+
}

0 commit comments

Comments
 (0)