diff --git a/src/main/java/com/simibubi/create/AllBlockPartials.java b/src/main/java/com/simibubi/create/AllBlockPartials.java index d0698ee60..69ffdecd2 100644 --- a/src/main/java/com/simibubi/create/AllBlockPartials.java +++ b/src/main/java/com/simibubi/create/AllBlockPartials.java @@ -131,6 +131,7 @@ public class AllBlockPartials { ENGINE_PISTON = block("steam_engine/piston"), ENGINE_LINKAGE = block("steam_engine/linkage"), ENGINE_CONNECTOR = block("steam_engine/shaft_connector"), BOILER_GAUGE = block("steam_engine/gauge"), + BOILER_GAUGE_DIAL = block("steam_engine/gauge_dial"), SIGNAL_ON = block("track_signal/indicator_on"), SIGNAL_OFF = block("track_signal/indicator_off"), DATA_GATHERER_TUBE = block("data_gatherer/tube"), DATA_GATHERER_GLOW = block("data_gatherer/glow"), diff --git a/src/main/java/com/simibubi/create/AllBlocks.java b/src/main/java/com/simibubi/create/AllBlocks.java index 90d12832f..6bee716a9 100644 --- a/src/main/java/com/simibubi/create/AllBlocks.java +++ b/src/main/java/com/simibubi/create/AllBlocks.java @@ -875,6 +875,7 @@ public class AllBlocks { .initialProperties(SharedProperties::copperMetal) .transform(pickaxeOnly()) .blockstate((c, p) -> p.horizontalFaceBlock(c.get(), AssetLookup.partialBaseModel(c, p))) + .transform(BlockStressDefaults.setCapacity(1024.0)) .item() .transform(customItemModel()) .register(); @@ -883,7 +884,6 @@ public class AllBlocks { REGISTRATE.block("powered_shaft", PoweredShaftBlock::new) .initialProperties(SharedProperties::stone) .transform(pickaxeOnly()) - .transform(BlockStressDefaults.setCapacity(64.0)) .blockstate(BlockStateGen.axisBlockProvider(false)) .loot((lt, block) -> lt.dropOther(block, AllBlocks.SHAFT.get())) .register(); diff --git a/src/main/java/com/simibubi/create/AllTags.java b/src/main/java/com/simibubi/create/AllTags.java index a01ac8354..02b14a225 100644 --- a/src/main/java/com/simibubi/create/AllTags.java +++ b/src/main/java/com/simibubi/create/AllTags.java @@ -122,8 +122,7 @@ public class AllTags { WRENCH_PICKUP, PASSIVE_BOILER_HEATERS, - ACTIVE_BOILER_HEATERS, - + RELOCATION_NOT_SUPPORTED(FORGE), WG_STONE(FORGE), @@ -358,7 +357,6 @@ public class AllTags { AllBlockTags.FAN_HEATERS.add(Blocks.MAGMA_BLOCK, Blocks.CAMPFIRE, Blocks.LAVA, Blocks.FIRE, Blocks.SOUL_FIRE, Blocks.SOUL_CAMPFIRE); AllBlockTags.FAN_HEATERS.includeIn(AllBlockTags.PASSIVE_BOILER_HEATERS); - AllBlockTags.ACTIVE_BOILER_HEATERS.add(Blocks.FURNACE, Blocks.BLAST_FURNACE, Blocks.SMOKER); AllBlockTags.SAFE_NBT.includeAll(BlockTags.SIGNS); AllBlockTags.WRENCH_PICKUP.includeAll(BlockTags.RAILS); diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/steam/PoweredShaftTileEntity.java b/src/main/java/com/simibubi/create/content/contraptions/components/steam/PoweredShaftTileEntity.java index 6c7a56449..ccdd87c7a 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/steam/PoweredShaftTileEntity.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/steam/PoweredShaftTileEntity.java @@ -1,71 +1,111 @@ package com.simibubi.create.content.contraptions.components.steam; -import java.util.HashMap; -import java.util.Map; +import java.util.List; -import com.jozufozu.flywheel.repack.joml.Math; import com.simibubi.create.content.contraptions.base.GeneratingKineticTileEntity; -import com.simibubi.create.foundation.utility.NBTHelper; +import com.simibubi.create.foundation.block.BlockStressValues; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction.Axis; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtUtils; -import net.minecraft.nbt.Tag; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.Mth; +import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; +import net.minecraftforge.registries.ForgeRegistries; public class PoweredShaftTileEntity extends GeneratingKineticTileEntity { - public Map sources; + public BlockPos enginePos; + public float engineEfficiency; + public int movementDirection; + public Block capacityKey; public PoweredShaftTileEntity(BlockEntityType typeIn, BlockPos pos, BlockState state) { super(typeIn, pos, state); - sources = new HashMap<>(); + movementDirection = 1; } - public void update(BlockPos sourcePos, int score) { + public void update(BlockPos sourcePos, int direction, float efficiency) { BlockPos key = worldPosition.subtract(sourcePos); - Integer prev = sources.put(key, score); - if (prev != null && prev.intValue() == score) + enginePos = key; + float prev = engineEfficiency; + engineEfficiency = efficiency; + int prevDirection = this.movementDirection; + if (Mth.equal(efficiency, prev) && prevDirection == direction) return; + + capacityKey = level.getBlockState(sourcePos) + .getBlock(); + this.movementDirection = direction; updateGeneratedRotation(); } public void remove(BlockPos sourcePos) { - BlockPos key = worldPosition.subtract(sourcePos); - Integer prev = sources.remove(key); - if (prev == null) + if (!isPoweredBy(sourcePos)) return; + + enginePos = null; + engineEfficiency = 0; + movementDirection = 0; + capacityKey = null; updateGeneratedRotation(); } + public boolean canBePoweredBy(BlockPos globalPos) { + return enginePos == null || isPoweredBy(globalPos); + } + + public boolean isPoweredBy(BlockPos globalPos) { + BlockPos key = worldPosition.subtract(globalPos); + return key.equals(enginePos); + } + @Override protected void write(CompoundTag compound, boolean clientPacket) { - compound.put("Sources", NBTHelper.writeCompoundList(sources.entrySet(), e -> { - CompoundTag nbt = new CompoundTag(); - nbt.put("Pos", NbtUtils.writeBlockPos(e.getKey())); - nbt.putInt("Value", e.getValue()); - return nbt; - })); + compound.putInt("Direction", movementDirection); + if (enginePos != null) { + compound.put("EnginePos", NbtUtils.writeBlockPos(enginePos)); + compound.putFloat("EnginePower", engineEfficiency); + compound.putString("EngineType", capacityKey.getRegistryName().toString()); + } super.write(compound, clientPacket); } @Override protected void read(CompoundTag compound, boolean clientPacket) { super.read(compound, clientPacket); - sources.clear(); - NBTHelper.iterateCompoundList(compound.getList("Sources", Tag.TAG_COMPOUND), - c -> sources.put(NbtUtils.readBlockPos(c.getCompound("Pos")), c.getInt("Value"))); + movementDirection = compound.getInt("Direction"); + enginePos = null; + engineEfficiency = 0; + if (compound.contains("EnginePos")) { + enginePos = NbtUtils.readBlockPos(compound.getCompound("EnginePos")); + engineEfficiency = compound.getFloat("EnginePower"); + capacityKey = ForgeRegistries.BLOCKS.getValue(new ResourceLocation(compound.getString("EngineType"))); + } } @Override public float getGeneratedSpeed() { - int max = 0; - for (Integer integer : sources.values()) - if (Math.abs(integer) > max) - max = integer; - return 8 * max; + return getCombinedCapacity() > 0 ? movementDirection * 16 * getSpeedModifier() : 0; + } + + private float getCombinedCapacity() { + return capacityKey == null ? 0 : (float) (engineEfficiency * BlockStressValues.getCapacity(capacityKey)); + } + + private int getSpeedModifier() { + return (int) (1 + (engineEfficiency >= 1 ? 3 : Math.min(2, Math.floor(engineEfficiency * 4)))); + } + + @Override + public float calculateAddedStressCapacity() { + float capacity = getCombinedCapacity() / getSpeedModifier(); + this.lastCapacityProvided = capacity; + return capacity; } @Override @@ -73,5 +113,14 @@ public class PoweredShaftTileEntity extends GeneratingKineticTileEntity { int combinedCoords = axis.choose(worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); return super.getRotationAngleOffset(axis) + (combinedCoords % 2 == 0 ? 180 : 0); } + + @Override + public boolean addToGoggleTooltip(List tooltip, boolean isPlayerSneaking) { + return false; + } + + public boolean addToEngineTooltip(List tooltip, boolean isPlayerSneaking) { + return super.addToGoggleTooltip(tooltip, isPlayerSneaking); + } } diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/steam/SteamEngineBlock.java b/src/main/java/com/simibubi/create/content/contraptions/components/steam/SteamEngineBlock.java index cfc85c572..3b99e2cfa 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/steam/SteamEngineBlock.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/steam/SteamEngineBlock.java @@ -2,6 +2,8 @@ package com.simibubi.create.content.contraptions.components.steam; import static net.minecraft.world.level.block.state.properties.BlockStateProperties.WATERLOGGED; +import java.util.function.Predicate; + import com.simibubi.create.AllBlocks; import com.simibubi.create.AllShapes; import com.simibubi.create.AllTileEntities; @@ -11,9 +13,20 @@ import com.simibubi.create.content.contraptions.fluids.tank.FluidTankTileEntity; import com.simibubi.create.content.contraptions.relays.elementary.ShaftBlock; import com.simibubi.create.content.contraptions.wrench.IWrenchable; import com.simibubi.create.foundation.block.ITE; +import com.simibubi.create.foundation.utility.BlockHelper; +import com.simibubi.create.foundation.utility.placement.IPlacementHelper; +import com.simibubi.create.foundation.utility.placement.PlacementHelpers; +import com.simibubi.create.foundation.utility.placement.PlacementOffset; +import net.minecraft.MethodsReturnNonnullByDefault; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; +import net.minecraft.core.Direction.Axis; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Level; @@ -29,12 +42,15 @@ import net.minecraft.world.level.block.state.properties.AttachFace; import net.minecraft.world.level.material.FluidState; import net.minecraft.world.level.material.Fluids; import net.minecraft.world.level.pathfinder.PathComputationType; +import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.shapes.CollisionContext; import net.minecraft.world.phys.shapes.VoxelShape; public class SteamEngineBlock extends FaceAttachedHorizontalDirectionalBlock implements SimpleWaterloggedBlock, IWrenchable, ITE { + private static final int placementHelperId = PlacementHelpers.register(new PlacementHelper()); + public SteamEngineBlock(Properties p_53182_) { super(p_53182_); } @@ -60,6 +76,18 @@ public class SteamEngineBlock extends FaceAttachedHorizontalDirectionalBlock return state.getValue(WATERLOGGED) ? Fluids.WATER.getSource(false) : Fluids.EMPTY.defaultFluidState(); } + @Override + public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, + BlockHitResult ray) { + ItemStack heldItem = player.getItemInHand(hand); + + IPlacementHelper placementHelper = PlacementHelpers.get(placementHelperId); + if (placementHelper.matchesItem(heldItem)) + return placementHelper.getOffset(player, world, state, pos, ray) + .placeInWorld(world, (BlockItem) heldItem.getItem(), player, hand, ray); + return InteractionResult.PASS; + } + @Override public BlockState updateShape(BlockState state, Direction direction, BlockState neighbourState, LevelAccessor world, BlockPos pos, BlockPos neighbourPos) { @@ -137,7 +165,8 @@ public class SteamEngineBlock extends FaceAttachedHorizontalDirectionalBlock } public static boolean isShaftValid(BlockState state, BlockState shaft) { - return AllBlocks.SHAFT.has(shaft) && shaft.getValue(ShaftBlock.AXIS) != getFacing(state).getAxis(); + return (AllBlocks.SHAFT.has(shaft) || AllBlocks.POWERED_SHAFT.has(shaft)) + && shaft.getValue(ShaftBlock.AXIS) != getFacing(state).getAxis(); } @Override @@ -150,4 +179,38 @@ public class SteamEngineBlock extends FaceAttachedHorizontalDirectionalBlock return AllTileEntities.STEAM_ENGINE.get(); } + @MethodsReturnNonnullByDefault + private static class PlacementHelper implements IPlacementHelper { + @Override + public Predicate getItemPredicate() { + return AllBlocks.SHAFT::isIn; + } + + @Override + public Predicate getStatePredicate() { + return s -> s.getBlock() instanceof SteamEngineBlock; + } + + @Override + public PlacementOffset getOffset(Player player, Level world, BlockState state, BlockPos pos, + BlockHitResult ray) { + BlockPos shaftPos = SteamEngineBlock.getShaftPos(state, pos); + BlockState shaft = AllBlocks.SHAFT.getDefaultState(); + for (Direction direction : Direction.orderedByNearest(player)) { + shaft = shaft.setValue(ShaftBlock.AXIS, direction.getAxis()); + if (isShaftValid(state, shaft)) + break; + } + + BlockState newState = world.getBlockState(shaftPos); + if (!newState.getMaterial().isReplaceable()) + return PlacementOffset.fail(); + + Axis axis = shaft.getValue(ShaftBlock.AXIS); + return PlacementOffset.success(shaftPos, + s -> BlockHelper.copyProperties(s, AllBlocks.POWERED_SHAFT.getDefaultState()) + .setValue(PoweredShaftBlock.AXIS, axis)); + } + } + } diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/steam/SteamEngineTileEntity.java b/src/main/java/com/simibubi/create/content/contraptions/components/steam/SteamEngineTileEntity.java index 6a6c356d1..e4fddbbb0 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/steam/SteamEngineTileEntity.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/steam/SteamEngineTileEntity.java @@ -11,16 +11,17 @@ import com.simibubi.create.content.contraptions.base.IRotate; import com.simibubi.create.content.contraptions.base.KineticTileEntityRenderer; import com.simibubi.create.content.contraptions.fluids.tank.FluidTankConnectivityHandler; import com.simibubi.create.content.contraptions.fluids.tank.FluidTankTileEntity; +import com.simibubi.create.content.contraptions.goggles.IHaveGoggleInformation; import com.simibubi.create.foundation.tileEntity.SmartTileEntity; import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour; import com.simibubi.create.foundation.utility.AngleHelper; -import com.simibubi.create.foundation.utility.Debug; import com.simibubi.create.foundation.utility.VecHelper; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.Direction.Axis; import net.minecraft.core.Direction.AxisDirection; +import net.minecraft.network.chat.Component; import net.minecraft.util.Mth; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityType; @@ -31,7 +32,7 @@ import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.fml.DistExecutor; -public class SteamEngineTileEntity extends SmartTileEntity { +public class SteamEngineTileEntity extends SmartTileEntity implements IHaveGoggleInformation { public WeakReference target; public WeakReference source; @@ -67,12 +68,12 @@ public class SteamEngineTileEntity extends SmartTileEntity { if (facing.getAxis() == Axis.Y) facing = blockState.getValue(SteamEngineBlock.FACING); - int score = Math.max(0, tank.boiler.engineScore); + float efficiency = Mth.clamp(tank.boiler.getEngineEfficiency(tank.getTotalTankSize()), 0, 1); int conveyedSpeedLevel = - verticalTarget ? score : (int) GeneratingKineticTileEntity.convertToDirection(score, facing); + efficiency == 0 ? 1 : verticalTarget ? 1 : (int) GeneratingKineticTileEntity.convertToDirection(1, facing); if (targetAxis == Axis.Z) conveyedSpeedLevel *= -1; - shaft.update(worldPosition, conveyedSpeedLevel); + shaft.update(worldPosition, conveyedSpeedLevel, efficiency); if (!level.isClientSide) return; @@ -89,18 +90,19 @@ public class SteamEngineTileEntity extends SmartTileEntity { } @Override + @OnlyIn(Dist.CLIENT) public AABB getRenderBoundingBox() { return super.getRenderBoundingBox().inflate(2); } public PoweredShaftTileEntity getShaft() { PoweredShaftTileEntity shaft = target.get(); - if (shaft == null || shaft.isRemoved()) { + if (shaft == null || shaft.isRemoved() || !shaft.canBePoweredBy(worldPosition)) { if (shaft != null) target = new WeakReference<>(null); Direction facing = SteamEngineBlock.getFacing(getBlockState()); BlockEntity anyShaftAt = level.getBlockEntity(worldPosition.relative(facing, 2)); - if (anyShaftAt instanceof PoweredShaftTileEntity ps) + if (anyShaftAt instanceof PoweredShaftTileEntity ps && ps.canBePoweredBy(worldPosition)) target = new WeakReference<>(shaft = ps); } return shaft; @@ -127,6 +129,11 @@ public class SteamEngineTileEntity extends SmartTileEntity { @OnlyIn(Dist.CLIENT) private void spawnParticles() { Float targetAngle = getTargetAngle(); + PoweredShaftTileEntity ste = target.get(); + if (ste == null) + return; + if (!ste.isPoweredBy(worldPosition) || ste.engineEfficiency == 0) + return; if (targetAngle == null) return; @@ -192,5 +199,11 @@ public class SteamEngineTileEntity extends SmartTileEntity { angle *= -1; return angle; } + + @Override + public boolean addToGoggleTooltip(List tooltip, boolean isPlayerSneaking) { + PoweredShaftTileEntity shaft = getShaft(); + return shaft == null ? false : shaft.addToEngineTooltip(tooltip, isPlayerSneaking); + } } diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/Contraption.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/Contraption.java index 9420331a1..11302a44c 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/Contraption.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/Contraption.java @@ -28,10 +28,12 @@ import org.apache.commons.lang3.tuple.Pair; import com.simibubi.create.AllBlocks; import com.simibubi.create.AllInteractionBehaviours; import com.simibubi.create.AllMovementBehaviours; +import com.simibubi.create.AllTileEntities; import com.simibubi.create.content.contraptions.base.IRotate; import com.simibubi.create.content.contraptions.base.KineticTileEntity; import com.simibubi.create.content.contraptions.components.actors.SeatBlock; import com.simibubi.create.content.contraptions.components.actors.SeatEntity; +import com.simibubi.create.content.contraptions.components.steam.PoweredShaftTileEntity; import com.simibubi.create.content.contraptions.components.structureMovement.bearing.MechanicalBearingBlock; import com.simibubi.create.content.contraptions.components.structureMovement.bearing.StabilizedContraption; import com.simibubi.create.content.contraptions.components.structureMovement.bearing.WindmillBearingBlock; @@ -55,6 +57,7 @@ import com.simibubi.create.content.contraptions.components.structureMovement.ren import com.simibubi.create.content.contraptions.fluids.tank.FluidTankTileEntity; import com.simibubi.create.content.contraptions.relays.advanced.GantryShaftBlock; import com.simibubi.create.content.contraptions.relays.belt.BeltBlock; +import com.simibubi.create.content.contraptions.relays.elementary.ShaftBlock; import com.simibubi.create.content.logistics.block.inventories.CreativeCrateTileEntity; import com.simibubi.create.content.logistics.block.redstone.RedstoneContactBlock; import com.simibubi.create.content.logistics.block.vault.ItemVaultTileEntity; @@ -64,6 +67,7 @@ import com.simibubi.create.foundation.fluid.CombinedTankWrapper; import com.simibubi.create.foundation.tileEntity.IMultiTileContainer; import com.simibubi.create.foundation.tileEntity.behaviour.filtering.FilteringBehaviour; import com.simibubi.create.foundation.utility.BlockFace; +import com.simibubi.create.foundation.utility.BlockHelper; import com.simibubi.create.foundation.utility.ICoordinate; import com.simibubi.create.foundation.utility.Iterate; import com.simibubi.create.foundation.utility.NBTHelper; @@ -612,6 +616,8 @@ public abstract class Contraption { BlockState blockstate = world.getBlockState(pos); if (AllBlocks.REDSTONE_CONTACT.has(blockstate)) blockstate = blockstate.setValue(RedstoneContactBlock.POWERED, true); + if (AllBlocks.POWERED_SHAFT.has(blockstate)) + blockstate = BlockHelper.copyProperties(blockstate, AllBlocks.SHAFT.getDefaultState()); if (AllBlocks.CONTROLS.has(blockstate)) blockstate = blockstate.setValue(ControlsBlock.OPEN, true); if (blockstate.getBlock() instanceof ButtonBlock) { @@ -624,6 +630,8 @@ public abstract class Contraption { } CompoundTag compoundnbt = getTileEntityNBT(world, pos); BlockEntity tileentity = world.getBlockEntity(pos); + if (tileentity instanceof PoweredShaftTileEntity) + tileentity = AllTileEntities.BRACKETED_KINETIC.create(pos, blockstate); return Pair.of(new StructureBlockInfo(pos, blockstate, compoundnbt), tileentity); } @@ -985,7 +993,9 @@ public abstract class Contraption { continue; BlockState oldState = world.getBlockState(add); Block blockIn = oldState.getBlock(); - if (block.state.getBlock() != blockIn) + boolean blockMismatch = block.state.getBlock() != blockIn; + blockMismatch &= !AllBlocks.POWERED_SHAFT.is(blockIn) || !AllBlocks.SHAFT.has(block.state); + if (blockMismatch) iterator.remove(); world.removeBlockEntity(add); int flags = Block.UPDATE_MOVE_BY_PISTON | Block.UPDATE_SUPPRESS_DROPS | Block.UPDATE_KNOWN_SHAPE @@ -1064,6 +1074,10 @@ public abstract class Contraption { } world.destroyBlock(targetPos, true); + + if (AllBlocks.SHAFT.has(state)) + state = ShaftBlock.pickCorrectShaftType(state, world, targetPos); + world.setBlock(targetPos, state, Block.UPDATE_MOVE_BY_PISTON | Block.UPDATE_ALL); boolean verticalRotation = transform.rotationAxis == null || transform.rotationAxis.isHorizontal(); diff --git a/src/main/java/com/simibubi/create/content/contraptions/fluids/tank/BoilerData.java b/src/main/java/com/simibubi/create/content/contraptions/fluids/tank/BoilerData.java index d3d0bae21..e78955a6b 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/fluids/tank/BoilerData.java +++ b/src/main/java/com/simibubi/create/content/contraptions/fluids/tank/BoilerData.java @@ -7,8 +7,8 @@ import com.simibubi.create.AllBlocks; import com.simibubi.create.Create; import com.simibubi.create.content.contraptions.components.steam.SteamEngineBlock; import com.simibubi.create.content.contraptions.goggles.IHaveGoggleInformation; +import com.simibubi.create.foundation.block.BlockStressValues; import com.simibubi.create.foundation.fluid.FluidHelper; -import com.simibubi.create.foundation.utility.Debug; import com.simibubi.create.foundation.utility.Iterate; import com.simibubi.create.foundation.utility.Lang; import com.simibubi.create.foundation.utility.animation.LerpedFloat; @@ -21,7 +21,6 @@ import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; import net.minecraft.network.chat.TextComponent; -import net.minecraft.network.chat.TranslatableComponent; import net.minecraft.util.Mth; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.state.BlockState; @@ -31,34 +30,34 @@ import net.minecraftforge.fluids.capability.IFluidHandler; public class BoilerData { static final int SAMPLE_RATE = 5; - public int gatheredSupply; - public float[] supplyOverTime = new float[10]; + + // pooled water supply + int gatheredSupply; + float[] supplyOverTime = new float[10]; int ticksUntilNextSample; int currentIndex; - boolean needsTemperatureUpdate; - public float currentTemperature; - public float targetTemperature; + // heat score + public boolean needsHeatLevelUpdate; + public boolean passiveHeat; + public int activeHeat; + public float waterSupply; - public float steamUnits; public int attachedEngines; - public int engineScore; - public LerpedFloat pressure = LerpedFloat.linear(); - - static final float MAX_ENGINE_USAGE = 32; + public LerpedFloat gauge = LerpedFloat.linear(); public void tick(FluidTankTileEntity controller) { if (!isActive()) return; if (controller.getLevel().isClientSide) { - pressure.tickChaser(); - float current = pressure.getValue(1); + gauge.tickChaser(); + float current = gauge.getValue(1); if (current > 1 && Create.RANDOM.nextFloat() < 1 / 2f) - pressure.setValueNoUpdate(current + Math.min(-(current - 1) * Create.RANDOM.nextFloat(), 0)); + gauge.setValueNoUpdate(current + Math.min(-(current - 1) * Create.RANDOM.nextFloat(), 0)); return; } - if (needsTemperatureUpdate && updateTemperature(controller)) + if (needsHeatLevelUpdate && updateTemperature(controller)) controller.notifyUpdate(); ticksUntilNextSample--; if (ticksUntilNextSample > 0) @@ -68,98 +67,107 @@ public class BoilerData { return; ticksUntilNextSample = SAMPLE_RATE; - waterSupply -= supplyOverTime[currentIndex]; +// waterSupply -= supplyOverTime[currentIndex] / supplyOverTime.length; supplyOverTime[currentIndex] = gatheredSupply / (float) SAMPLE_RATE; - waterSupply += supplyOverTime[currentIndex]; + waterSupply = Math.max(waterSupply, supplyOverTime[currentIndex]); currentIndex = (currentIndex + 1) % supplyOverTime.length; gatheredSupply = 0; if (currentIndex == 0) { waterSupply = 0; for (float i : supplyOverTime) - waterSupply += i; - } - - currentTemperature = Mth.clamp(currentTemperature + Math.signum(targetTemperature - currentTemperature) - * (0.5f + (targetTemperature - currentTemperature) * .125f), 0, targetTemperature); - - float steamPerTick = Math.min(waterSupply / 2, currentTemperature - 100); - steamUnits += steamPerTick; - - float pressure = steamUnits / capacity; - float engineEfficiency = (float) (Math.max(0, pressure - 0.5) * 2); - float usagePerEngine = engineEfficiency * MAX_ENGINE_USAGE; - float consumedSteam = Math.min(steamUnits, attachedEngines * usagePerEngine); - float equilibrium = steamPerTick / (attachedEngines * MAX_ENGINE_USAGE * 2) + .5f; - -// if (Math.abs(engineEfficiency - equilibrium) < 1 / 8f) // Anti-flicker at balance point -// engineEfficiency = equilibrium; - - engineScore = Mth.floor(engineEfficiency * 8); - steamUnits -= consumedSteam; - - if (steamUnits > capacity * 1.25f) { - Debug.debugChat("Boiler exploding: Bang. " + controller.getBlockPos()); - steamUnits = 0; +// waterSupply += i; + waterSupply = Math.max(i, waterSupply); } controller.notifyUpdate(); } + public int getTheoreticalHeatLevel() { + return activeHeat; + } + + public int getMaxHeatLevelForBoilerSize(int boilerSize) { + return boilerSize / 4; + } + + public int getMaxHeatLevelForWaterSupply() { + return Math.min(activeHeat, (int) Math.min(18, Mth.ceil(waterSupply) / 20)); + } + + public boolean isPassive(int boilerSize) { + return passiveHeat || activeHeat != 0 && getActualHeat(boilerSize) == 0; + } + + public float getEngineEfficiency(int boilerSize) { + if (isPassive(boilerSize)) + return 1 / 16f / attachedEngines; + if (activeHeat == 0) + return 0; + int actualHeat = getActualHeat(boilerSize); + return attachedEngines <= actualHeat ? 1 : (float) actualHeat / attachedEngines; + } + + private int getActualHeat(int boilerSize) { + int forBoilerSize = getMaxHeatLevelForBoilerSize(boilerSize); + int forWaterSupply = getMaxHeatLevelForWaterSupply(); + int actualHeat = Math.min(activeHeat, Math.min(forWaterSupply, forBoilerSize)); + return actualHeat; + } + String spacing = " "; Component componentSpacing = new TextComponent(spacing); - public boolean addToGoggleTooltip(List tooltip, boolean isPlayerSneaking) { + public boolean addToGoggleTooltip(List tooltip, boolean isPlayerSneaking, int boilerSize) { if (!isActive()) return false; - float steamPerTick = Math.min(waterSupply / 2, currentTemperature - 100); - float equilibrium = steamPerTick / (attachedEngines * MAX_ENGINE_USAGE * 2) + .5f; + int forBoilerSize = getMaxHeatLevelForBoilerSize(boilerSize); + int forWaterSupply = getMaxHeatLevelForWaterSupply(); + int actualHeat = Math.min(activeHeat, Math.min(forWaterSupply, forBoilerSize)); tooltip.add(componentSpacing.plainCopy() - .append(Lang.translate("gui.goggles.fluid_container"))); - TranslatableComponent mb = Lang.translate("generic.unit.millibuckets"); + .append(new TextComponent("Boiler Information:"))); - Component engines = new TextComponent("Engines: ").withStyle(ChatFormatting.GRAY) - .append(new TextComponent(attachedEngines + "").withStyle(ChatFormatting.GOLD)); - Component power = new TextComponent("Temperature: ").withStyle(ChatFormatting.GRAY) - .append(new TextComponent(IHaveGoggleInformation.format(currentTemperature) + "") - .withStyle(ChatFormatting.GOLD)); - Component score = new TextComponent("Engine Efficiency: ").withStyle(ChatFormatting.GRAY) - .append(new TextComponent(engineScore + "").withStyle(ChatFormatting.GOLD)); - Component supply = new TextComponent("Water Supply: ").withStyle(ChatFormatting.GRAY) - .append(new TextComponent(IHaveGoggleInformation.format(waterSupply)).append(mb) - .withStyle(ChatFormatting.GOLD)); - Component steam = new TextComponent("Steam Volume: ").withStyle(ChatFormatting.GRAY) - .append(new TextComponent(IHaveGoggleInformation.format(steamUnits)).append(mb) - .withStyle(ChatFormatting.GOLD)); + Component h = new TextComponent("Heat: ").withStyle(ChatFormatting.GRAY) + .append(new TextComponent(IHaveGoggleInformation.format(activeHeat)).withStyle(ChatFormatting.GOLD)); + Component w = new TextComponent(", Water: ").withStyle(ChatFormatting.GRAY) + .append(new TextComponent(IHaveGoggleInformation.format(forWaterSupply)).withStyle(ChatFormatting.GOLD)); + Component s = new TextComponent(", Size: ").withStyle(ChatFormatting.GRAY) + .append(new TextComponent(IHaveGoggleInformation.format(forBoilerSize)).withStyle(ChatFormatting.GOLD)); - int approachingPressure = (int) (equilibrium * 100); - int actualPressure = - (int) ((this.pressure.getChaseTarget() > 1 ? this.pressure.getChaseTarget() : this.pressure.getValue()) - * 100); - MutableComponent pressure = new TextComponent("Pressure: ").withStyle(ChatFormatting.GRAY) - .append(new TextComponent(IHaveGoggleInformation.format(actualPressure)).append(new TextComponent("%")) - .withStyle(ChatFormatting.GOLD)); - if (actualPressure != approachingPressure) - pressure.append(new TextComponent(" >> ").append( - new TextComponent(IHaveGoggleInformation.format(approachingPressure)).append(new TextComponent("%")) - .withStyle(ChatFormatting.GREEN))); + TextComponent heatLevel = isPassive(boilerSize) ? new TextComponent("Passive") + : (activeHeat == 0 ? new TextComponent("No Heat") + : new TextComponent(IHaveGoggleInformation.format(actualHeat))); + MutableComponent heatLabel = heatLevel.withStyle(ChatFormatting.GREEN); + Component level = new TextComponent("Boiler Level: ").append(heatLabel); - Component indent = new TextComponent(spacing + " "); + double totalSU = getEngineEfficiency(boilerSize) * 16 * Math.max(actualHeat, attachedEngines) + * BlockStressValues.getCapacity(AllBlocks.STEAM_ENGINE.get()); + Component capacity = + new TextComponent(IHaveGoggleInformation.format(totalSU)).append(Lang.translate("generic.unit.stress")) + .withStyle(ChatFormatting.AQUA); + Component engines = + new TextComponent(" via " + attachedEngines + " engine(s)").withStyle(ChatFormatting.DARK_GRAY); + Component indent = new TextComponent(spacing); + Component indent2 = new TextComponent(spacing + " "); + + Component stats = indent.plainCopy() + .append(h) + .append(w) + .append(s); + + if (activeHeat > 0) + tooltip.add(stats); + tooltip.add(new TextComponent(" -> ").append(level)); tooltip.add(indent.plainCopy() + .append(Lang.translate("tooltip.capacityProvided") + .withStyle(ChatFormatting.GRAY))); + tooltip.add(indent2.plainCopy() + .append(capacity) .append(engines)); - tooltip.add(indent.plainCopy() - .append(score)); - tooltip.add(indent.plainCopy() - .append(power)); - tooltip.add(indent.plainCopy() - .append(supply)); - tooltip.add(indent.plainCopy() - .append(steam)); - tooltip.add(indent.plainCopy() - .append(pressure)); + return true; } @@ -190,29 +198,36 @@ public class BoilerData { } } - needsTemperatureUpdate = true; + needsHeatLevelUpdate = true; return prev != attachedEngines; } public boolean updateTemperature(FluidTankTileEntity controller) { BlockPos controllerPos = controller.getBlockPos(); Level level = controller.getLevel(); - float prev = targetTemperature; - targetTemperature = 0; - needsTemperatureUpdate = false; + needsHeatLevelUpdate = false; + + boolean prevPassive = passiveHeat; + int prevActive = activeHeat; + passiveHeat = false; + activeHeat = 0; for (int xOffset = 0; xOffset < controller.width; xOffset++) { for (int zOffset = 0; zOffset < controller.width; zOffset++) { BlockPos pos = controllerPos.offset(xOffset, -1, zOffset); BlockState blockState = level.getBlockState(pos); - targetTemperature += BoilerHeaters.getAddedHeatOf(blockState); + float heat = BoilerHeaters.getActiveHeatOf(blockState); + if (heat == 0) { + passiveHeat |= BoilerHeaters.canHeatPassively(blockState); + continue; + } + activeHeat += heat; } } - if (targetTemperature != 0) - targetTemperature += 100; + passiveHeat &= activeHeat == 0; - return prev != attachedEngines; + return prevActive != activeHeat || prevPassive != passiveHeat; } public boolean isActive() { @@ -221,36 +236,35 @@ public class BoilerData { public void clear() { waterSupply = 0; - targetTemperature = 0; + activeHeat = 0; + passiveHeat = false; attachedEngines = 0; - steamUnits = 0; - engineScore = 0; Arrays.fill(supplyOverTime, 0); } public CompoundTag write() { CompoundTag nbt = new CompoundTag(); nbt.putFloat("Supply", waterSupply); - nbt.putFloat("Temperature", currentTemperature); - nbt.putFloat("Power", targetTemperature); - nbt.putFloat("Pressure", steamUnits); + nbt.putInt("ActiveHeat", activeHeat); + nbt.putBoolean("PassiveHeat", passiveHeat); nbt.putInt("Engines", attachedEngines); - nbt.putBoolean("Update", needsTemperatureUpdate); - nbt.putInt("Score", engineScore); + nbt.putBoolean("Update", needsHeatLevelUpdate); return nbt; } - public void read(CompoundTag nbt, int capacity) { + public void read(CompoundTag nbt, int boilerSize) { waterSupply = nbt.getFloat("Supply"); - currentTemperature = nbt.getFloat("Temperature"); - targetTemperature = nbt.getFloat("Power"); - steamUnits = nbt.getFloat("Pressure"); - engineScore = nbt.getInt("Score"); + activeHeat = nbt.getInt("ActiveHeat"); + passiveHeat = nbt.getBoolean("PassiveHeat"); attachedEngines = nbt.getInt("Engines"); - needsTemperatureUpdate = nbt.getBoolean("Update"); + needsHeatLevelUpdate = nbt.getBoolean("Update"); Arrays.fill(supplyOverTime, (int) waterSupply); - if (capacity > 0) - pressure.chase(steamUnits / capacity, 0.125f, Chaser.EXP); + + int forBoilerSize = getMaxHeatLevelForBoilerSize(boilerSize); + int forWaterSupply = getMaxHeatLevelForWaterSupply(); + int actualHeat = Math.min(activeHeat, Math.min(forWaterSupply, forBoilerSize)); + float target = isPassive(boilerSize) ? 1 / 8f : forBoilerSize == 0 ? 0 : actualHeat / (forBoilerSize * 1f); + gauge.chase(target, 0.125f, Chaser.EXP); } public BoilerFluidHandler createHandler() { @@ -283,11 +297,14 @@ public class BoilerData { public int fill(FluidStack resource, FluidAction action) { if (!isFluidValid(0, resource)) return 0; - if (targetTemperature == 0) + int maxAccepted = (int) ((passiveHeat ? 1 : activeHeat + 1) * 20) * SAMPLE_RATE; + if (maxAccepted == 0) return 0; - int amount = resource.getAmount(); + int amount = Math.min(maxAccepted - gatheredSupply, resource.getAmount()); if (action.execute()) gatheredSupply += amount; + if (action.simulate()) + return Math.max(amount, 1); return amount; } diff --git a/src/main/java/com/simibubi/create/content/contraptions/fluids/tank/BoilerHeaters.java b/src/main/java/com/simibubi/create/content/contraptions/fluids/tank/BoilerHeaters.java index acf03c379..fa8909f32 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/fluids/tank/BoilerHeaters.java +++ b/src/main/java/com/simibubi/create/content/contraptions/fluids/tank/BoilerHeaters.java @@ -5,33 +5,33 @@ import com.simibubi.create.AllTags.AllBlockTags; import com.simibubi.create.content.contraptions.processing.burner.BlazeBurnerBlock; import com.simibubi.create.content.contraptions.processing.burner.BlazeBurnerBlock.HeatLevel; -import net.minecraft.world.level.block.AbstractFurnaceBlock; import net.minecraft.world.level.block.state.BlockState; public class BoilerHeaters { // API? - public static float getAddedHeatOf(BlockState state) { + public static boolean canHeatPassively(BlockState state) { + if (AllBlocks.BLAZE_BURNER.has(state) && state.getValue(BlazeBurnerBlock.HEAT_LEVEL) != HeatLevel.NONE) + return true; + if (AllBlockTags.PASSIVE_BOILER_HEATERS.matches(state)) + return true; + return false; + } + + public static int getActiveHeatOf(BlockState state) { if (AllBlocks.BLAZE_BURNER.has(state)) { HeatLevel value = state.getValue(BlazeBurnerBlock.HEAT_LEVEL); switch (value) { - case SMOULDERING: - return 24; case FADING: case KINDLED: - return 96; + return 1; case SEETHING: - return 32 * 8; + return 2; default: + case SMOULDERING: case NONE: return 0; } } - if (state.hasProperty(AbstractFurnaceBlock.LIT) && !state.getValue(AbstractFurnaceBlock.LIT)) - return 0; - if (AllBlockTags.ACTIVE_BOILER_HEATERS.matches(state)) - return 48; - if (AllBlockTags.PASSIVE_BOILER_HEATERS.matches(state)) - return 12; return 0; } diff --git a/src/main/java/com/simibubi/create/content/contraptions/fluids/tank/FluidTankRenderer.java b/src/main/java/com/simibubi/create/content/contraptions/fluids/tank/FluidTankRenderer.java index a247708f4..e4f2ae7e7 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/fluids/tank/FluidTankRenderer.java +++ b/src/main/java/com/simibubi/create/content/contraptions/fluids/tank/FluidTankRenderer.java @@ -86,7 +86,7 @@ public class FluidTankRenderer extends SafeTileEntityRenderer type, BlockPos pos, BlockState state) { super(type, pos, state); tankInventory = createInventory(); @@ -235,14 +235,14 @@ public class FluidTankTileEntity extends SmartTileEntity implements IHaveGoggleI return; te.setWindows(!te.window); } - + public void updateBoilerTemperature() { FluidTankTileEntity te = getControllerTE(); if (te == null) return; if (!te.boiler.isActive()) return; - te.boiler.needsTemperatureUpdate = true; + te.boiler.needsHeatLevelUpdate = true; } public void sendDataImmediately() { @@ -361,7 +361,7 @@ public class FluidTankTileEntity extends SmartTileEntity implements IHaveGoggleI FluidTankTileEntity controllerTE = getControllerTE(); if (controllerTE == null) return false; - if (controllerTE.boiler.addToGoggleTooltip(tooltip, isPlayerSneaking)) + if (controllerTE.boiler.addToGoggleTooltip(tooltip, isPlayerSneaking, controllerTE.getTotalTankSize())) return true; return containedFluidTooltip(tooltip, isPlayerSneaking, controllerTE.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY)); @@ -395,8 +395,8 @@ public class FluidTankTileEntity extends SmartTileEntity implements IHaveGoggleI if (tankInventory.getSpace() < 0) tankInventory.drain(-tankInventory.getSpace(), FluidAction.EXECUTE); } - - boiler.read(compound.getCompound("Boiler"), tankInventory.getCapacity()); + + boiler.read(compound.getCompound("Boiler"), width * width * height); if (compound.contains("ForceFluidLevel") || fluidLevel == null) fluidLevel = LerpedFloat.linear() diff --git a/src/main/java/com/simibubi/create/content/contraptions/relays/elementary/ShaftBlock.java b/src/main/java/com/simibubi/create/content/contraptions/relays/elementary/ShaftBlock.java index b6f589da3..c17278c9b 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/relays/elementary/ShaftBlock.java +++ b/src/main/java/com/simibubi/create/content/contraptions/relays/elementary/ShaftBlock.java @@ -13,6 +13,7 @@ import com.simibubi.create.content.curiosities.girder.GirderEncasedShaftBlock; import com.simibubi.create.foundation.advancement.AllTriggers; import com.simibubi.create.foundation.utility.placement.IPlacementHelper; import com.simibubi.create.foundation.utility.placement.PlacementHelpers; +import com.simibubi.create.foundation.utility.placement.PlacementOffset; import com.simibubi.create.foundation.utility.placement.util.PoleHelper; import net.minecraft.MethodsReturnNonnullByDefault; @@ -47,7 +48,11 @@ public class ShaftBlock extends AbstractSimpleShaftBlock { @Override public BlockState getStateForPlacement(BlockPlaceContext context) { BlockState stateForPlacement = super.getStateForPlacement(context); - if (PoweredShaftBlock.stillValid(stateForPlacement, context.getLevel(), context.getClickedPos())) + return pickCorrectShaftType(stateForPlacement, context.getLevel(), context.getClickedPos()); + } + + public static BlockState pickCorrectShaftType(BlockState stateForPlacement, Level level, BlockPos pos) { + if (PoweredShaftBlock.stillValid(stateForPlacement, level, pos)) return PoweredShaftBlock.getEquivalent(stateForPlacement); return stateForPlacement; } @@ -129,5 +134,16 @@ public class ShaftBlock extends AbstractSimpleShaftBlock { public Predicate getStatePredicate() { return AllBlocks.SHAFT::has; } + + @Override + public PlacementOffset getOffset(Player player, Level world, BlockState state, BlockPos pos, + BlockHitResult ray) { + PlacementOffset offset = super.getOffset(player, world, state, pos, ray); + if (offset.isSuccessful()) + offset.withTransform(offset.getTransform() + .andThen(s -> ShaftBlock.pickCorrectShaftType(s, world, offset.getBlockPos()))); + return offset; + } + } } diff --git a/src/main/resources/assets/create/models/block/steam_engine/gauge_dial.json b/src/main/resources/assets/create/models/block/steam_engine/gauge_dial.json new file mode 100644 index 000000000..fc4549235 --- /dev/null +++ b/src/main/resources/assets/create/models/block/steam_engine/gauge_dial.json @@ -0,0 +1,22 @@ +{ + "credit": "Made with Blockbench", + "textures": { + "2": "create:block/boiler_gauge" + }, + "elements": [ + { + "name": "GaugeDial", + "from": [15.75, 5.25, 5.75], + "to": [16.45, 6.25, 10.75], + "rotation": {"angle": 0, "axis": "x", "origin": [16.1, 5.75, 8.25]}, + "faces": { + "north": {"uv": [0, 11, 1, 12], "rotation": 180, "texture": "#2"}, + "east": {"uv": [0, 11, 5, 12], "rotation": 180, "texture": "#2"}, + "south": {"uv": [4, 11, 5, 12], "rotation": 180, "texture": "#2"}, + "west": {"uv": [5, 11, 0, 12], "rotation": 180, "texture": "#2"}, + "up": {"uv": [0, 11, 5, 12], "rotation": 90, "texture": "#2"}, + "down": {"uv": [0, 11, 5, 12], "rotation": 270, "texture": "#2"} + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/create/textures/block/boiler_gauge.png b/src/main/resources/assets/create/textures/block/boiler_gauge.png index d03923615..74c6c7b58 100644 Binary files a/src/main/resources/assets/create/textures/block/boiler_gauge.png and b/src/main/resources/assets/create/textures/block/boiler_gauge.png differ