Speed limit

- Added a means to control the throttle/max speed of a controlled train
- Train Controls now show an xp bar overlay
This commit is contained in:
simibubi 2022-06-09 15:00:04 +02:00
parent 70b8d2e998
commit 091812b42d
14 changed files with 297 additions and 165 deletions

View file

@ -2,6 +2,7 @@ package com.simibubi.create;
import com.simibubi.create.content.contraptions.base.KineticTileEntityRenderer; import com.simibubi.create.content.contraptions.base.KineticTileEntityRenderer;
import com.simibubi.create.content.contraptions.components.structureMovement.glue.SuperGlueSelectionHandler; import com.simibubi.create.content.contraptions.components.structureMovement.glue.SuperGlueSelectionHandler;
import com.simibubi.create.content.contraptions.components.structureMovement.interaction.controls.TrainHUD;
import com.simibubi.create.content.contraptions.components.structureMovement.render.ContraptionRenderDispatcher; import com.simibubi.create.content.contraptions.components.structureMovement.render.ContraptionRenderDispatcher;
import com.simibubi.create.content.contraptions.components.structureMovement.render.SBBContraptionManager; import com.simibubi.create.content.contraptions.components.structureMovement.render.SBBContraptionManager;
import com.simibubi.create.content.contraptions.goggles.GoggleOverlayRenderer; import com.simibubi.create.content.contraptions.goggles.GoggleOverlayRenderer;
@ -102,6 +103,7 @@ public class CreateClient {
private static void registerOverlays() { private static void registerOverlays() {
// Register overlays in reverse order // Register overlays in reverse order
OverlayRegistry.registerOverlayAbove(ForgeIngameGui.AIR_LEVEL_ELEMENT, "Create's Remaining Air", CopperBacktankArmorLayer.REMAINING_AIR_OVERLAY); OverlayRegistry.registerOverlayAbove(ForgeIngameGui.AIR_LEVEL_ELEMENT, "Create's Remaining Air", CopperBacktankArmorLayer.REMAINING_AIR_OVERLAY);
OverlayRegistry.registerOverlayAbove(ForgeIngameGui.EXPERIENCE_BAR_ELEMENT, "Create's Train Driver HUD", TrainHUD.OVERLAY);
OverlayRegistry.registerOverlayAbove(ForgeIngameGui.HOTBAR_ELEMENT, "Create's Toolboxes", ToolboxHandlerClient.OVERLAY); OverlayRegistry.registerOverlayAbove(ForgeIngameGui.HOTBAR_ELEMENT, "Create's Toolboxes", ToolboxHandlerClient.OVERLAY);
OverlayRegistry.registerOverlayAbove(ForgeIngameGui.HOTBAR_ELEMENT, "Create's Goggle Information", GoggleOverlayRenderer.OVERLAY); OverlayRegistry.registerOverlayAbove(ForgeIngameGui.HOTBAR_ELEMENT, "Create's Goggle Information", GoggleOverlayRenderer.OVERLAY);
OverlayRegistry.registerOverlayAbove(ForgeIngameGui.HOTBAR_ELEMENT, "Create's Blueprints", BlueprintOverlayRenderer.OVERLAY); OverlayRegistry.registerOverlayAbove(ForgeIngameGui.HOTBAR_ELEMENT, "Create's Blueprints", BlueprintOverlayRenderer.OVERLAY);

View file

@ -106,11 +106,11 @@ public class ControlsHandler {
// Keepalive Pressed Keys // Keepalive Pressed Keys
if (packetCooldown == 0) { if (packetCooldown == 0) {
if (!pressedKeys.isEmpty()) { // if (!pressedKeys.isEmpty()) {
AllPackets.channel AllPackets.channel
.sendToServer(new ControlsInputPacket(pressedKeys, true, entity.getId(), controlsPos, false)); .sendToServer(new ControlsInputPacket(pressedKeys, true, entity.getId(), controlsPos, false));
packetCooldown = PACKET_RATE; packetCooldown = PACKET_RATE;
} // }
} }
currentlyPressed = pressedKeys; currentlyPressed = pressedKeys;

View file

@ -0,0 +1,153 @@
package com.simibubi.create.content.contraptions.components.structureMovement.interaction.controls;
import com.mojang.blaze3d.vertex.PoseStack;
import com.simibubi.create.content.logistics.trains.entity.Carriage;
import com.simibubi.create.content.logistics.trains.entity.CarriageContraptionEntity;
import com.simibubi.create.content.logistics.trains.entity.Train;
import com.simibubi.create.foundation.config.AllConfigs;
import com.simibubi.create.foundation.gui.AllGuiTextures;
import com.simibubi.create.foundation.networking.AllPackets;
import com.simibubi.create.foundation.utility.AngleHelper;
import com.simibubi.create.foundation.utility.animation.LerpedFloat;
import com.simibubi.create.foundation.utility.animation.LerpedFloat.Chaser;
import com.simibubi.create.foundation.utility.placement.PlacementHelpers;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiComponent;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate.StructureBlockInfo;
import net.minecraftforge.client.gui.ForgeIngameGui;
import net.minecraftforge.client.gui.IIngameOverlay;
public class TrainHUD {
public static final IIngameOverlay OVERLAY = TrainHUD::renderOverlay;
static LerpedFloat displayedSpeed = LerpedFloat.linear();
static LerpedFloat displayedThrottle = LerpedFloat.linear();
static Double editedThrottle = null;
static int hudPacketCooldown = 5;
public static void tick() {
Carriage carriage = getCarriage();
if (carriage == null)
return;
Train train = carriage.train;
double value =
Math.abs(train.speed) / (train.maxSpeed() * AllConfigs.SERVER.trains.manualTrainSpeedModifier.getF());
value = Mth.clamp(value + 0.05f, 0, 1);
displayedSpeed.chase((int) (value * 18) / 18f, .5f, Chaser.EXP);
displayedSpeed.tickChaser();
displayedThrottle.chase(editedThrottle != null ? editedThrottle : train.throttle, .75f, Chaser.EXP);
displayedThrottle.tickChaser();
if (editedThrottle == null)
return;
if (Mth.equal(editedThrottle, train.throttle)) {
editedThrottle = null;
hudPacketCooldown = 5;
return;
}
if (hudPacketCooldown-- > 0)
return;
AllPackets.channel.sendToServer(new TrainHUDUpdatePacket.Serverbound(train, editedThrottle));
hudPacketCooldown = 5;
}
private static Carriage getCarriage() {
if (!(ControlsHandler.entityRef.get() instanceof CarriageContraptionEntity cce))
return null;
return cce.getCarriage();
}
public static void renderOverlay(ForgeIngameGui gui, PoseStack poseStack, float partialTicks, int width,
int height) {
if (!(ControlsHandler.entityRef.get() instanceof CarriageContraptionEntity cce))
return;
Carriage carriage = cce.getCarriage();
if (carriage == null)
return;
Entity cameraEntity = Minecraft.getInstance()
.getCameraEntity();
if (cameraEntity == null)
return;
BlockPos localPos = ControlsHandler.controlsPos;
if (localPos == null)
return;
poseStack.pushPose();
poseStack.translate(width / 2 - 91, height - 29, 0);
// Speed, Throttle
AllGuiTextures.TRAIN_HUD_FRAME.render(poseStack, -2, 1);
AllGuiTextures.TRAIN_HUD_SPEED_BG.render(poseStack, 0, 0);
int w = (int) (AllGuiTextures.TRAIN_HUD_SPEED.width * displayedSpeed.getValue(partialTicks));
int h = AllGuiTextures.TRAIN_HUD_SPEED.height;
AllGuiTextures.TRAIN_HUD_SPEED.bind();
GuiComponent.blit(poseStack, 0, 0, 0, AllGuiTextures.TRAIN_HUD_SPEED.startX,
AllGuiTextures.TRAIN_HUD_SPEED.startY, w, h, 256, 256);
AllGuiTextures.TRAIN_HUD_DIRECTION.render(poseStack, 77, -20);
w = (int) (AllGuiTextures.TRAIN_HUD_THROTTLE.width * (1 - displayedThrottle.getValue(partialTicks)));
AllGuiTextures.TRAIN_HUD_THROTTLE.bind();
int invW = AllGuiTextures.TRAIN_HUD_THROTTLE.width - w;
GuiComponent.blit(poseStack, invW, 0, 0, AllGuiTextures.TRAIN_HUD_THROTTLE.startX + invW,
AllGuiTextures.TRAIN_HUD_THROTTLE.startY, w, h, 256, 256);
AllGuiTextures.TRAIN_HUD_THROTTLE_POINTER.render(poseStack,
Math.max(1, AllGuiTextures.TRAIN_HUD_THROTTLE.width - w) - 3, -2);
// Direction
StructureBlockInfo info = cce.getContraption()
.getBlocks()
.get(localPos);
Direction initialOrientation = cce.getInitialOrientation()
.getCounterClockWise();
boolean inverted = false;
if (info != null && info.state.hasProperty(ControlsBlock.FACING))
inverted = !info.state.getValue(ControlsBlock.FACING)
.equals(initialOrientation);
boolean reversing = ControlsHandler.currentlyPressed.contains(1);
inverted ^= reversing;
int angleOffset = (ControlsHandler.currentlyPressed.contains(2) ? -45 : 0)
+ (ControlsHandler.currentlyPressed.contains(3) ? 45 : 0);
if (reversing)
angleOffset *= -1;
float snapSize = 22.5f;
float diff = AngleHelper.getShortestAngleDiff(cameraEntity.getYRot(), cce.yaw) + (inverted ? -90 : 90);
if (Math.abs(diff) < 60)
diff = 0;
float angle = diff + angleOffset;
float snappedAngle = (snapSize * Math.round(angle / snapSize)) % 360f;
poseStack.translate(91, -9, 0);
poseStack.scale(0.925f, 0.925f, 1);
PlacementHelpers.textured(poseStack, 0, 0, 1, snappedAngle);
poseStack.popPose();
}
public static boolean onScroll(double delta) {
Carriage carriage = getCarriage();
if (carriage == null)
return false;
double prevThrottle = editedThrottle == null ? carriage.train.throttle : editedThrottle;
editedThrottle = Mth.clamp(prevThrottle + (delta > 0 ? 1 : -1) / 18f, 1 / 18f, 1);
return true;
}
}

View file

@ -0,0 +1,82 @@
package com.simibubi.create.content.contraptions.components.structureMovement.interaction.controls;
import java.util.UUID;
import java.util.function.Supplier;
import com.simibubi.create.Create;
import com.simibubi.create.content.logistics.trains.entity.Train;
import com.simibubi.create.foundation.networking.SimplePacketBase;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.server.level.ServerPlayer;
import net.minecraftforge.network.NetworkEvent.Context;
public class TrainHUDUpdatePacket extends SimplePacketBase {
UUID trainId;
Double throttle;
double speed;
int fuelTicks;
public TrainHUDUpdatePacket() {}
public TrainHUDUpdatePacket(Train train) {
trainId = train.id;
throttle = train.throttle;
speed = train.speedBeforeStall == null ? train.speed : train.speedBeforeStall;
fuelTicks = train.fuelTicks;
}
public TrainHUDUpdatePacket(FriendlyByteBuf buffer) {
trainId = buffer.readUUID();
if (buffer.readBoolean())
throttle = buffer.readDouble();
speed = buffer.readDouble();
fuelTicks = buffer.readInt();
}
@Override
public void write(FriendlyByteBuf buffer) {
buffer.writeUUID(trainId);
buffer.writeBoolean(throttle != null);
if (throttle != null)
buffer.writeDouble(throttle);
buffer.writeDouble(speed);
buffer.writeInt(fuelTicks);
}
@Override
public void handle(Supplier<Context> context) {
Context c = context.get();
c.enqueueWork(() -> {
ServerPlayer sender = c.getSender();
boolean clientSide = sender == null;
Train train = Create.RAILWAYS.sided(clientSide ? null : sender.level).trains.get(trainId);
if (train == null)
return;
if (throttle != null)
train.throttle = throttle;
if (clientSide) {
train.speed = speed;
train.fuelTicks = fuelTicks;
}
});
c.setPacketHandled(true);
}
public static class Serverbound extends TrainHUDUpdatePacket {
public Serverbound(FriendlyByteBuf buffer) {
super(buffer);
}
public Serverbound(Train train, Double sendThrottle) {
trainId = train.id;
throttle = sendThrottle;
}
}
}

View file

@ -11,7 +11,6 @@ import java.util.UUID;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import com.jozufozu.flywheel.repack.joml.Math;
import com.simibubi.create.AllEntityDataSerializers; import com.simibubi.create.AllEntityDataSerializers;
import com.simibubi.create.AllEntityTypes; import com.simibubi.create.AllEntityTypes;
import com.simibubi.create.Create; import com.simibubi.create.Create;
@ -21,6 +20,7 @@ import com.simibubi.create.content.contraptions.components.structureMovement.Mov
import com.simibubi.create.content.contraptions.components.structureMovement.MovementContext; import com.simibubi.create.content.contraptions.components.structureMovement.MovementContext;
import com.simibubi.create.content.contraptions.components.structureMovement.OrientedContraptionEntity; import com.simibubi.create.content.contraptions.components.structureMovement.OrientedContraptionEntity;
import com.simibubi.create.content.contraptions.components.structureMovement.interaction.controls.ControlsBlock; import com.simibubi.create.content.contraptions.components.structureMovement.interaction.controls.ControlsBlock;
import com.simibubi.create.content.contraptions.components.structureMovement.interaction.controls.TrainHUDUpdatePacket;
import com.simibubi.create.content.contraptions.particle.CubeParticleData; import com.simibubi.create.content.contraptions.particle.CubeParticleData;
import com.simibubi.create.content.logistics.trains.TrackGraph; import com.simibubi.create.content.logistics.trains.TrackGraph;
import com.simibubi.create.content.logistics.trains.entity.Carriage.DimensionalCarriageEntity; import com.simibubi.create.content.logistics.trains.entity.Carriage.DimensionalCarriageEntity;
@ -45,6 +45,7 @@ import net.minecraft.network.chat.TextComponent;
import net.minecraft.network.syncher.EntityDataAccessor; import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.EntityDataSerializers; import net.minecraft.network.syncher.EntityDataSerializers;
import net.minecraft.network.syncher.SynchedEntityData; import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.Mth; import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.EntityType;
@ -503,6 +504,7 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity {
} }
double navDistanceTotal = 0; double navDistanceTotal = 0;
int hudPacketCooldown = 0;
@Override @Override
public boolean control(BlockPos controlsLocalPos, Collection<Integer> heldControls, Player player) { public boolean control(BlockPos controlsLocalPos, Collection<Integer> heldControls, Player player) {
@ -527,6 +529,11 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity {
inverted = !info.state.getValue(ControlsBlock.FACING) inverted = !info.state.getValue(ControlsBlock.FACING)
.equals(initialOrientation); .equals(initialOrientation);
if (hudPacketCooldown-- <= 0 && player instanceof ServerPlayer sp) {
AllPackets.channel.send(PacketDistributor.PLAYER.with(() -> sp), new TrainHUDUpdatePacket(carriage.train));
hudPacketCooldown = 5;
}
int targetSpeed = 0; int targetSpeed = 0;
if (heldControls.contains(0)) if (heldControls.contains(0))
targetSpeed++; targetSpeed++;
@ -584,6 +591,7 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity {
int targetColor = arrived ? 0x00_91EA44 : 0x00_ffffff; int targetColor = arrived ? 0x00_91EA44 : 0x00_ffffff;
player.displayClientMessage(greenComponent.withStyle(st -> st.withColor(mixedColor)) player.displayClientMessage(greenComponent.withStyle(st -> st.withColor(mixedColor))
.append(whiteComponent.withStyle(st -> st.withColor(targetColor))), true); .append(whiteComponent.withStyle(st -> st.withColor(targetColor))), true);
carriage.train.manualTick = true;
return true; return true;
} }
} }
@ -609,14 +617,15 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity {
targetSteer < 0 ? SteerDirection.RIGHT : targetSteer > 0 ? SteerDirection.LEFT : SteerDirection.NONE; targetSteer < 0 ? SteerDirection.RIGHT : targetSteer > 0 ? SteerDirection.LEFT : SteerDirection.NONE;
double topSpeed = carriage.train.maxSpeed() * AllConfigs.SERVER.trains.manualTrainSpeedModifier.getF(); double topSpeed = carriage.train.maxSpeed() * AllConfigs.SERVER.trains.manualTrainSpeedModifier.getF();
double cappedTopSpeed = topSpeed * carriage.train.throttle;
if (carriage.getLeadingPoint().edge != null && carriage.getLeadingPoint().edge.isTurn() if (carriage.getLeadingPoint().edge != null && carriage.getLeadingPoint().edge.isTurn()
|| carriage.getTrailingPoint().edge != null && carriage.getTrailingPoint().edge.isTurn()) || carriage.getTrailingPoint().edge != null && carriage.getTrailingPoint().edge.isTurn())
topSpeed = carriage.train.maxTurnSpeed(); topSpeed = carriage.train.maxTurnSpeed();
carriage.train.targetSpeed = topSpeed * targetSpeed;
if (slow) if (slow)
carriage.train.targetSpeed /= 6; topSpeed /= 4;
carriage.train.targetSpeed = Math.min(topSpeed, cappedTopSpeed) * targetSpeed;
boolean counteringAcceleration = Math.abs(Math.signum(targetSpeed) - Math.signum(carriage.train.speed)) > 1.5f; boolean counteringAcceleration = Math.abs(Math.signum(targetSpeed) - Math.signum(carriage.train.speed)) > 1.5f;
carriage.train.manualTick = true; carriage.train.manualTick = true;

View file

@ -17,7 +17,6 @@ import javax.annotation.Nullable;
import org.apache.commons.lang3.mutable.MutableDouble; import org.apache.commons.lang3.mutable.MutableDouble;
import org.apache.commons.lang3.mutable.MutableObject; import org.apache.commons.lang3.mutable.MutableObject;
import com.jozufozu.flywheel.repack.joml.Math;
import com.simibubi.create.Create; import com.simibubi.create.Create;
import com.simibubi.create.content.logistics.trains.DimensionPalette; import com.simibubi.create.content.logistics.trains.DimensionPalette;
import com.simibubi.create.content.logistics.trains.TrackEdge; import com.simibubi.create.content.logistics.trains.TrackEdge;
@ -158,8 +157,8 @@ public class Navigation {
boolean primary = entering.equals(signal.groups.getFirst()); boolean primary = entering.equals(signal.groups.getFirst());
boolean crossSignal = signal.types.get(primary) == SignalType.CROSS_SIGNAL; boolean crossSignal = signal.types.get(primary) == SignalType.CROSS_SIGNAL;
boolean occupied = boolean occupied = !train.manualTick
signal.isForcedRed(nodes.getSecond()) || signalEdgeGroup.isOccupiedUnless(train); && (signal.isForcedRed(nodes.getSecond()) || signalEdgeGroup.isOccupiedUnless(train));
if (!crossSignalTracked) { if (!crossSignalTracked) {
if (crossSignal) { // Now entering cross signal path if (crossSignal) { // Now entering cross signal path
@ -244,7 +243,6 @@ public class Navigation {
train.burnFuel(); train.burnFuel();
double topSpeed = train.maxSpeed(); double topSpeed = train.maxSpeed();
double turnTopSpeed = train.maxTurnSpeed();
if (targetDistance < 10) { if (targetDistance < 10) {
double target = topSpeed * ((targetDistance) / 10); double target = topSpeed * ((targetDistance) / 10);
@ -254,6 +252,9 @@ public class Navigation {
} }
} }
topSpeed *= train.throttle;
double turnTopSpeed = Math.min(topSpeed, train.maxTurnSpeed());
double targetSpeed = targetDistance > brakingDistance ? topSpeed * speedMod : 0; double targetSpeed = targetDistance > brakingDistance ? topSpeed * speedMod : 0;
if (distanceToNextCurve != -1) { if (distanceToNextCurve != -1) {
@ -279,6 +280,8 @@ public class Navigation {
} }
private boolean currentSignalResolved() { private boolean currentSignalResolved() {
if (train.manualTick)
return true;
if (distanceToDestination < .5f) if (distanceToDestination < .5f)
return true; return true;
SignalBoundary signal = train.graph.getPoint(EdgePointType.SIGNAL, waitingForSignal.getFirst()); SignalBoundary signal = train.graph.getPoint(EdgePointType.SIGNAL, waitingForSignal.getFirst());

View file

@ -78,6 +78,9 @@ public class Train {
public double targetSpeed = 0; public double targetSpeed = 0;
public Double speedBeforeStall = null; public Double speedBeforeStall = null;
public double throttle = 1;
public boolean honk = false;
public UUID id; public UUID id;
public UUID owner; public UUID owner;
public TrackGraph graph; public TrackGraph graph;
@ -886,15 +889,16 @@ public class Train {
} }
public void approachTargetSpeed(float accelerationMod) { public void approachTargetSpeed(float accelerationMod) {
if (Mth.equal(targetSpeed, speed)) double actualTarget = targetSpeed;
if (Mth.equal(actualTarget, speed))
return; return;
if (manualTick) if (manualTick)
leaveStation(); leaveStation();
double acceleration = acceleration(); double acceleration = acceleration();
if (speed < targetSpeed) if (speed < actualTarget)
speed = Math.min(speed + acceleration * accelerationMod, targetSpeed); speed = Math.min(speed + acceleration * accelerationMod, actualTarget);
else if (speed > targetSpeed) else if (speed > actualTarget)
speed = Math.max(speed - acceleration * accelerationMod, targetSpeed); speed = Math.max(speed - acceleration * accelerationMod, actualTarget);
} }
public void collectInitiallyOccupiedSignalBlocks() { public void collectInitiallyOccupiedSignalBlocks() {
@ -1060,6 +1064,7 @@ public class Train {
tag.putIntArray("CarriageSpacing", carriageSpacing); tag.putIntArray("CarriageSpacing", carriageSpacing);
tag.putBoolean("DoubleEnded", doubleEnded); tag.putBoolean("DoubleEnded", doubleEnded);
tag.putDouble("Speed", speed); tag.putDouble("Speed", speed);
tag.putDouble("Throttle", throttle);
if (speedBeforeStall != null) if (speedBeforeStall != null)
tag.putDouble("SpeedBeforeStall", speedBeforeStall); tag.putDouble("SpeedBeforeStall", speedBeforeStall);
tag.putInt("Fuel", fuelTicks); tag.putInt("Fuel", fuelTicks);
@ -1113,6 +1118,7 @@ public class Train {
Train train = new Train(id, owner, graph, carriages, carriageSpacing, doubleEnded); Train train = new Train(id, owner, graph, carriages, carriageSpacing, doubleEnded);
train.speed = tag.getDouble("Speed"); train.speed = tag.getDouble("Speed");
train.throttle = tag.getDouble("Throttle");
if (tag.contains("SpeedBeforeStall")) if (tag.contains("SpeedBeforeStall"))
train.speedBeforeStall = tag.getDouble("SpeedBeforeStall"); train.speedBeforeStall = tag.getDouble("SpeedBeforeStall");
train.targetSpeed = tag.getDouble("TargetSpeed"); train.targetSpeed = tag.getDouble("TargetSpeed");

View file

@ -1,139 +0,0 @@
package com.simibubi.create.content.logistics.trains.track;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.simibubi.create.AllItems;
import com.simibubi.create.content.logistics.trains.ITrackBlock;
import com.simibubi.create.content.logistics.trains.TrackNodeLocation.DiscoveredLocation;
import com.simibubi.create.content.logistics.trains.entity.TrainRelocator;
import com.simibubi.create.foundation.networking.AllPackets;
import net.minecraft.ChatFormatting;
import net.minecraft.client.Minecraft;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.core.BlockPos;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.HitResult.Type;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
public class TrackRemoval {
static BlockPos startPos;
static BlockPos hoveringPos;
static Set<BlockPos> toRemove;
// TODO localisation
public static void sneakWrenched(BlockPos pos) {
LocalPlayer player = Minecraft.getInstance().player;
if (startPos != null) {
startPos = null;
player.displayClientMessage(new TextComponent("Track removal aborted").withStyle(ChatFormatting.RED), true);
return;
}
startPos = pos;
}
public static void wrenched(BlockPos pos) {
if (startPos == null || hoveringPos == null || toRemove == null)
return;
if (TrainRelocator.isRelocating())
return;
AllPackets.channel.sendToServer(new TrackRemovalPacket(toRemove));
startPos = null;
}
@OnlyIn(Dist.CLIENT)
public static void clientTick() {
if (startPos == null)
return;
LocalPlayer player = Minecraft.getInstance().player;
ItemStack stack = player.getMainHandItem();
HitResult hitResult = Minecraft.getInstance().hitResult;
if (hitResult == null)
return;
if (hitResult.getType() != Type.BLOCK)
return;
if (!AllItems.WRENCH.isIn(stack)) {
hoveringPos = null;
startPos = null;
player.displayClientMessage(new TextComponent("Track removal aborted").withStyle(ChatFormatting.RED), true);
return;
}
BlockHitResult result = (BlockHitResult) hitResult;
BlockPos blockPos = result.getBlockPos();
Level level = player.level;
BlockState blockState = level.getBlockState(blockPos);
if (!(blockState.getBlock()instanceof ITrackBlock track)) {
player.displayClientMessage(new TextComponent("Select a second track piece, Unequip Wrench to abort"),
true);
return;
}
if (blockPos.equals(hoveringPos)) {
if (hoveringPos.equals(startPos)) {
player.displayClientMessage(
new TextComponent("Starting point selected. Right-Click a second Track piece"), true);
} else if (toRemove == null) {
player.displayClientMessage(new TextComponent("Starting point not reachable, Sneak-Click to abort")
.withStyle(ChatFormatting.RED), true);
} else
player.displayClientMessage(new TextComponent("Right-Click to confirm").withStyle(ChatFormatting.GREEN),
true);
//
return;
}
hoveringPos = blockPos;
toRemove = new HashSet<>();
List<BlockPos> frontier = new ArrayList<>();
Set<BlockPos> visited = new HashSet<>();
if (search(level, hoveringPos, frontier, visited, 0)) {
toRemove.add(hoveringPos);
toRemove.add(startPos);
return;
}
toRemove = null;
}
private static boolean search(Level level, BlockPos pos, List<BlockPos> frontier, Set<BlockPos> visited,
int depth) {
if (pos.equals(startPos))
return true;
if (depth > 32)
return false;
if (!visited.add(pos))
return false;
BlockState blockState = level.getBlockState(pos);
if (!(blockState.getBlock()instanceof ITrackBlock track))
return false;
for (DiscoveredLocation discoveredLocation : track.getConnected(level, pos, blockState, false, null)) {
for (BlockPos blockPos : discoveredLocation.allAdjacent()) {
if (!search(level, blockPos, frontier, visited, depth + 1))
continue;
toRemove.add(pos);
return true;
}
}
return false;
}
}

View file

@ -16,6 +16,7 @@ import com.simibubi.create.content.contraptions.components.steam.SteamEngineBloc
import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionHandler; import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionHandler;
import com.simibubi.create.content.contraptions.components.structureMovement.chassis.ChassisRangeDisplay; import com.simibubi.create.content.contraptions.components.structureMovement.chassis.ChassisRangeDisplay;
import com.simibubi.create.content.contraptions.components.structureMovement.interaction.controls.ControlsHandler; import com.simibubi.create.content.contraptions.components.structureMovement.interaction.controls.ControlsHandler;
import com.simibubi.create.content.contraptions.components.structureMovement.interaction.controls.TrainHUD;
import com.simibubi.create.content.contraptions.components.structureMovement.render.ContraptionRenderDispatcher; import com.simibubi.create.content.contraptions.components.structureMovement.render.ContraptionRenderDispatcher;
import com.simibubi.create.content.contraptions.components.structureMovement.train.CouplingHandlerClient; import com.simibubi.create.content.contraptions.components.structureMovement.train.CouplingHandlerClient;
import com.simibubi.create.content.contraptions.components.structureMovement.train.CouplingPhysics; import com.simibubi.create.content.contraptions.components.structureMovement.train.CouplingPhysics;
@ -44,7 +45,6 @@ import com.simibubi.create.content.logistics.trains.management.schedule.TrainHat
import com.simibubi.create.content.logistics.trains.track.CurvedTrackInteraction; import com.simibubi.create.content.logistics.trains.track.CurvedTrackInteraction;
import com.simibubi.create.content.logistics.trains.track.TrackBlockOutline; import com.simibubi.create.content.logistics.trains.track.TrackBlockOutline;
import com.simibubi.create.content.logistics.trains.track.TrackPlacement; import com.simibubi.create.content.logistics.trains.track.TrackPlacement;
import com.simibubi.create.content.logistics.trains.track.TrackRemoval;
import com.simibubi.create.foundation.config.AllConfigs; import com.simibubi.create.foundation.config.AllConfigs;
import com.simibubi.create.foundation.config.ui.BaseConfigScreen; import com.simibubi.create.foundation.config.ui.BaseConfigScreen;
import com.simibubi.create.foundation.fluid.FluidHelper; import com.simibubi.create.foundation.fluid.FluidHelper;
@ -164,11 +164,11 @@ public class ClientEvents {
ToolboxHandlerClient.clientTick(); ToolboxHandlerClient.clientTick();
TrackTargetingClient.clientTick(); TrackTargetingClient.clientTick();
TrackPlacement.clientTick(); TrackPlacement.clientTick();
TrackRemoval.clientTick();
TrainRelocator.clientTick(); TrainRelocator.clientTick();
DisplayLinkBlockItem.clientTick(); DisplayLinkBlockItem.clientTick();
CurvedTrackInteraction.clientTick(); CurvedTrackInteraction.clientTick();
CameraDistanceModifier.tick(); CameraDistanceModifier.tick();
TrainHUD.tick();
} }
@SubscribeEvent @SubscribeEvent

View file

@ -1,6 +1,7 @@
package com.simibubi.create.events; package com.simibubi.create.events;
import com.simibubi.create.CreateClient; import com.simibubi.create.CreateClient;
import com.simibubi.create.content.contraptions.components.structureMovement.interaction.controls.TrainHUD;
import com.simibubi.create.content.curiosities.toolbox.ToolboxHandlerClient; import com.simibubi.create.content.curiosities.toolbox.ToolboxHandlerClient;
import com.simibubi.create.content.logistics.item.LinkedControllerClientHandler; import com.simibubi.create.content.logistics.item.LinkedControllerClientHandler;
import com.simibubi.create.content.logistics.trains.entity.TrainRelocator; import com.simibubi.create.content.logistics.trains.entity.TrainRelocator;
@ -42,7 +43,7 @@ public class InputEvents {
// CollisionDebugger.onScroll(delta); // CollisionDebugger.onScroll(delta);
boolean cancelled = CreateClient.SCHEMATIC_HANDLER.mouseScrolled(delta) boolean cancelled = CreateClient.SCHEMATIC_HANDLER.mouseScrolled(delta)
|| CreateClient.SCHEMATIC_AND_QUILL_HANDLER.mouseScrolled(delta) || FilteringHandler.onScroll(delta) || CreateClient.SCHEMATIC_AND_QUILL_HANDLER.mouseScrolled(delta) || FilteringHandler.onScroll(delta)
|| ScrollValueHandler.onScroll(delta); || ScrollValueHandler.onScroll(delta) || TrainHUD.onScroll(delta);
event.setCanceled(cancelled); event.setCanceled(cancelled);
} }

View file

@ -162,6 +162,13 @@ public enum AllGuiTextures implements ScreenElement {
SPEECH_TOOLTIP_BACKGROUND("widgets", 0, 24, 8, 8), SPEECH_TOOLTIP_BACKGROUND("widgets", 0, 24, 8, 8),
SPEECH_TOOLTIP_COLOR("widgets", 8, 24, 8, 8), SPEECH_TOOLTIP_COLOR("widgets", 8, 24, 8, 8),
TRAIN_HUD_SPEED_BG("widgets", 0, 190, 182, 5),
TRAIN_HUD_SPEED("widgets", 0, 185, 182, 5),
TRAIN_HUD_THROTTLE("widgets", 0, 195, 182, 5),
TRAIN_HUD_THROTTLE_POINTER("widgets", 0, 209, 6, 9),
TRAIN_HUD_FRAME("widgets", 0, 200, 186, 7),
TRAIN_HUD_DIRECTION("widgets", 77, 165, 28, 20),
// PlacementIndicator // PlacementIndicator
PLACEMENT_INDICATOR_SHEET("placement_indicator", 0, 0, 16, 256); PLACEMENT_INDICATOR_SHEET("placement_indicator", 0, 0, 16, 256);

View file

@ -18,6 +18,7 @@ import com.simibubi.create.content.contraptions.components.structureMovement.glu
import com.simibubi.create.content.contraptions.components.structureMovement.glue.SuperGlueSelectionPacket; import com.simibubi.create.content.contraptions.components.structureMovement.glue.SuperGlueSelectionPacket;
import com.simibubi.create.content.contraptions.components.structureMovement.interaction.controls.ControlsInputPacket; import com.simibubi.create.content.contraptions.components.structureMovement.interaction.controls.ControlsInputPacket;
import com.simibubi.create.content.contraptions.components.structureMovement.interaction.controls.ControlsStopControllingPacket; import com.simibubi.create.content.contraptions.components.structureMovement.interaction.controls.ControlsStopControllingPacket;
import com.simibubi.create.content.contraptions.components.structureMovement.interaction.controls.TrainHUDUpdatePacket;
import com.simibubi.create.content.contraptions.components.structureMovement.sync.ClientMotionPacket; import com.simibubi.create.content.contraptions.components.structureMovement.sync.ClientMotionPacket;
import com.simibubi.create.content.contraptions.components.structureMovement.sync.ContraptionFluidPacket; import com.simibubi.create.content.contraptions.components.structureMovement.sync.ContraptionFluidPacket;
import com.simibubi.create.content.contraptions.components.structureMovement.sync.ContraptionInteractionPacket; import com.simibubi.create.content.contraptions.components.structureMovement.sync.ContraptionInteractionPacket;
@ -114,10 +115,12 @@ public enum AllPackets {
EJECTOR_ELYTRA(EjectorElytraPacket.class, EjectorElytraPacket::new, PLAY_TO_SERVER), EJECTOR_ELYTRA(EjectorElytraPacket.class, EjectorElytraPacket::new, PLAY_TO_SERVER),
LINKED_CONTROLLER_INPUT(LinkedControllerInputPacket.class, LinkedControllerInputPacket::new, PLAY_TO_SERVER), LINKED_CONTROLLER_INPUT(LinkedControllerInputPacket.class, LinkedControllerInputPacket::new, PLAY_TO_SERVER),
LINKED_CONTROLLER_BIND(LinkedControllerBindPacket.class, LinkedControllerBindPacket::new, PLAY_TO_SERVER), LINKED_CONTROLLER_BIND(LinkedControllerBindPacket.class, LinkedControllerBindPacket::new, PLAY_TO_SERVER),
LINKED_CONTROLLER_USE_LECTERN(LinkedControllerStopLecternPacket.class, LinkedControllerStopLecternPacket::new, PLAY_TO_SERVER), LINKED_CONTROLLER_USE_LECTERN(LinkedControllerStopLecternPacket.class, LinkedControllerStopLecternPacket::new,
PLAY_TO_SERVER),
C_CONFIGURE_CONFIG(CConfigureConfigPacket.class, CConfigureConfigPacket::new, PLAY_TO_SERVER), C_CONFIGURE_CONFIG(CConfigureConfigPacket.class, CConfigureConfigPacket::new, PLAY_TO_SERVER),
SUBMIT_GHOST_ITEM(GhostItemSubmitPacket.class, GhostItemSubmitPacket::new, PLAY_TO_SERVER), SUBMIT_GHOST_ITEM(GhostItemSubmitPacket.class, GhostItemSubmitPacket::new, PLAY_TO_SERVER),
BLUEPRINT_COMPLETE_RECIPE(BlueprintAssignCompleteRecipePacket.class, BlueprintAssignCompleteRecipePacket::new, PLAY_TO_SERVER), BLUEPRINT_COMPLETE_RECIPE(BlueprintAssignCompleteRecipePacket.class, BlueprintAssignCompleteRecipePacket::new,
PLAY_TO_SERVER),
CONFIGURE_SYMMETRY_WAND(ConfigureSymmetryWandPacket.class, ConfigureSymmetryWandPacket::new, PLAY_TO_SERVER), CONFIGURE_SYMMETRY_WAND(ConfigureSymmetryWandPacket.class, ConfigureSymmetryWandPacket::new, PLAY_TO_SERVER),
CONFIGURE_WORLDSHAPER(ConfigureWorldshaperPacket.class, ConfigureWorldshaperPacket::new, PLAY_TO_SERVER), CONFIGURE_WORLDSHAPER(ConfigureWorldshaperPacket.class, ConfigureWorldshaperPacket::new, PLAY_TO_SERVER),
TOOLBOX_EQUIP(ToolboxEquipPacket.class, ToolboxEquipPacket::new, PLAY_TO_SERVER), TOOLBOX_EQUIP(ToolboxEquipPacket.class, ToolboxEquipPacket::new, PLAY_TO_SERVER),
@ -135,6 +138,7 @@ public enum AllPackets {
GLUE_IN_AREA(SuperGlueSelectionPacket.class, SuperGlueSelectionPacket::new, PLAY_TO_SERVER), GLUE_IN_AREA(SuperGlueSelectionPacket.class, SuperGlueSelectionPacket::new, PLAY_TO_SERVER),
GLUE_REMOVED(SuperGlueRemovalPacket.class, SuperGlueRemovalPacket::new, PLAY_TO_SERVER), GLUE_REMOVED(SuperGlueRemovalPacket.class, SuperGlueRemovalPacket::new, PLAY_TO_SERVER),
TRAIN_COLLISION(TrainCollisionPacket.class, TrainCollisionPacket::new, PLAY_TO_SERVER), TRAIN_COLLISION(TrainCollisionPacket.class, TrainCollisionPacket::new, PLAY_TO_SERVER),
C_TRAIN_HUD(TrainHUDUpdatePacket.Serverbound.class, TrainHUDUpdatePacket.Serverbound::new, PLAY_TO_SERVER),
// Server to Client // Server to Client
SYMMETRY_EFFECT(SymmetryEffectPacket.class, SymmetryEffectPacket::new, PLAY_TO_CLIENT), SYMMETRY_EFFECT(SymmetryEffectPacket.class, SymmetryEffectPacket::new, PLAY_TO_CLIENT),
@ -156,14 +160,17 @@ public enum AllPackets {
FUNNEL_FLAP(FunnelFlapPacket.class, FunnelFlapPacket::new, PLAY_TO_CLIENT), FUNNEL_FLAP(FunnelFlapPacket.class, FunnelFlapPacket::new, PLAY_TO_CLIENT),
POTATO_CANNON(PotatoCannonPacket.class, PotatoCannonPacket::new, PLAY_TO_CLIENT), POTATO_CANNON(PotatoCannonPacket.class, PotatoCannonPacket::new, PLAY_TO_CLIENT),
SOUL_PULSE(SoulPulseEffectPacket.class, SoulPulseEffectPacket::new, PLAY_TO_CLIENT), SOUL_PULSE(SoulPulseEffectPacket.class, SoulPulseEffectPacket::new, PLAY_TO_CLIENT),
PERSISTENT_DATA(ISyncPersistentData.PersistentDataPacket.class, ISyncPersistentData.PersistentDataPacket::new, PLAY_TO_CLIENT), PERSISTENT_DATA(ISyncPersistentData.PersistentDataPacket.class, ISyncPersistentData.PersistentDataPacket::new,
SYNC_POTATO_PROJECTILE_TYPES(PotatoProjectileTypeManager.SyncPacket.class, PotatoProjectileTypeManager.SyncPacket::new, PLAY_TO_CLIENT), PLAY_TO_CLIENT),
SYNC_POTATO_PROJECTILE_TYPES(PotatoProjectileTypeManager.SyncPacket.class,
PotatoProjectileTypeManager.SyncPacket::new, PLAY_TO_CLIENT),
SYNC_RAIL_GRAPH(TrackGraphSyncPacket.class, TrackGraphSyncPacket::new, PLAY_TO_CLIENT), SYNC_RAIL_GRAPH(TrackGraphSyncPacket.class, TrackGraphSyncPacket::new, PLAY_TO_CLIENT),
SYNC_EDGE_GROUP(SignalEdgeGroupPacket.class, SignalEdgeGroupPacket::new, PLAY_TO_CLIENT), SYNC_EDGE_GROUP(SignalEdgeGroupPacket.class, SignalEdgeGroupPacket::new, PLAY_TO_CLIENT),
SYNC_TRAIN(TrainPacket.class, TrainPacket::new, PLAY_TO_CLIENT), SYNC_TRAIN(TrainPacket.class, TrainPacket::new, PLAY_TO_CLIENT),
REMOVE_TE(RemoveTileEntityPacket.class, RemoveTileEntityPacket::new, PLAY_TO_CLIENT), REMOVE_TE(RemoveTileEntityPacket.class, RemoveTileEntityPacket::new, PLAY_TO_CLIENT),
S_CONFIGURE_TRAIN(TrainEditReturnPacket.class, TrainEditReturnPacket::new, PLAY_TO_CLIENT), S_CONFIGURE_TRAIN(TrainEditReturnPacket.class, TrainEditReturnPacket::new, PLAY_TO_CLIENT),
CONTROLS_ABORT(ControlsStopControllingPacket.class, ControlsStopControllingPacket::new, PLAY_TO_CLIENT), CONTROLS_ABORT(ControlsStopControllingPacket.class, ControlsStopControllingPacket::new, PLAY_TO_CLIENT),
S_TRAIN_HUD(TrainHUDUpdatePacket.class, TrainHUDUpdatePacket::new, PLAY_TO_CLIENT),
; ;
@ -190,8 +197,9 @@ public enum AllPackets {
} }
public static void sendToNear(Level world, BlockPos pos, int range, Object message) { public static void sendToNear(Level world, BlockPos pos, int range, Object message) {
channel.send(PacketDistributor.NEAR channel.send(
.with(TargetPoint.p(pos.getX(), pos.getY(), pos.getZ(), range, world.dimension())), message); PacketDistributor.NEAR.with(TargetPoint.p(pos.getX(), pos.getY(), pos.getZ(), range, world.dimension())),
message);
} }
private static class LoadedPacket<T extends SimplePacketBase> { private static class LoadedPacket<T extends SimplePacketBase> {

View file

@ -245,7 +245,7 @@ public class PlacementHelpers {
ms.popPose(); ms.popPose();
} }
private static void textured(PoseStack ms, float centerX, float centerY, float alpha, float snappedAngle) { public static void textured(PoseStack ms, float centerX, float centerY, float alpha, float snappedAngle) {
RenderSystem.enableTexture(); RenderSystem.enableTexture();
AllGuiTextures.PLACEMENT_INDICATOR_SHEET.bind(); AllGuiTextures.PLACEMENT_INDICATOR_SHEET.bind();
RenderSystem.enableDepthTest(); RenderSystem.enableDepthTest();

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB