Boiling it down

- Implemented new power dynamics of the steam engine
This commit is contained in:
simibubi 2022-05-02 17:14:51 +02:00
parent 147c2ff9fa
commit 1da8b6e538
14 changed files with 362 additions and 169 deletions

View file

@ -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"),

View file

@ -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();

View file

@ -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);

View file

@ -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<BlockPos, Integer> 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<Component> tooltip, boolean isPlayerSneaking) {
return false;
}
public boolean addToEngineTooltip(List<Component> tooltip, boolean isPlayerSneaking) {
return super.addToGoggleTooltip(tooltip, isPlayerSneaking);
}
}

View file

@ -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<SteamEngineTileEntity> {
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<ItemStack> getItemPredicate() {
return AllBlocks.SHAFT::isIn;
}
@Override
public Predicate<BlockState> 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));
}
}
}

View file

@ -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<PoweredShaftTileEntity> target;
public WeakReference<FluidTankTileEntity> 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<Component> tooltip, boolean isPlayerSneaking) {
PoweredShaftTileEntity shaft = getShaft();
return shaft == null ? false : shaft.addToEngineTooltip(tooltip, isPlayerSneaking);
}
}

View file

@ -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();

View file

@ -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<Component> tooltip, boolean isPlayerSneaking) {
public boolean addToGoggleTooltip(List<Component> 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;
}

View file

@ -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;
}

View file

@ -86,7 +86,7 @@ public class FluidTankRenderer extends SafeTileEntityRenderer<FluidTankTileEntit
msr.translate(te.width / 2f, 0.5, te.width / 2f);
float dialPivot = 5.75f / 16;
float progress = te.boiler.pressure.getValue(partialTicks);
float progress = te.boiler.gauge.getValue(partialTicks);
for (Direction d : Iterate.horizontalDirections) {
ms.pushPose();
@ -96,7 +96,7 @@ public class FluidTankRenderer extends SafeTileEntityRenderer<FluidTankTileEntit
.translate(te.width / 2f - 6 / 16f, 0, 0)
.light(light)
.renderInto(ms, vb);
CachedBufferer.partial(AllBlockPartials.GAUGE_DIAL, blockState)
CachedBufferer.partial(AllBlockPartials.BOILER_GAUGE_DIAL, blockState)
.rotateY(d.toYRot())
.unCentre()
.translate(te.width / 2f - 6 / 16f, 0, 0)

View file

@ -59,7 +59,7 @@ public class FluidTankTileEntity extends SmartTileEntity implements IHaveGoggleI
// For rendering purposes only
private LerpedFloat fluidLevel;
public FluidTankTileEntity(BlockEntityType<?> 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()

View file

@ -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<BlockState> 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;
}
}
}

View file

@ -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"}
}
}
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 360 B

After

Width:  |  Height:  |  Size: 363 B