Bulk Wrenching

- Tracks can now be removed in bulk using the wrench
- Track placement now automatically picks larger radii when connecting at non-equal y levels
This commit is contained in:
simibubi 2022-03-10 02:04:44 +01:00
parent 9e0f35e4d7
commit 7ba4af1bea
7 changed files with 231 additions and 8 deletions

View File

@ -60,6 +60,10 @@ public class TrainRelocator {
static Boolean lastHoveredResult;
static List<Vec3> toVisualise;
public static boolean isRelocating() {
return relocatingTrain != null;
}
@OnlyIn(Dist.CLIENT)
public static void onClicked(ClickInputEvent event) {
if (relocatingTrain == null)

View File

@ -292,6 +292,15 @@ public class TrackBlock extends Block implements EntityBlock, IWrenchable, ITrac
@Override
public InteractionResult onWrenched(BlockState state, UseOnContext context) {
if (context.getLevel().isClientSide)
TrackRemoval.wrenched(context.getClickedPos());
return InteractionResult.SUCCESS;
}
@Override
public InteractionResult onSneakWrenched(BlockState state, UseOnContext context) {
if (context.getLevel().isClientSide)
TrackRemoval.sneakWrenched(context.getClickedPos());
return InteractionResult.SUCCESS;
}

View File

@ -286,20 +286,21 @@ public class TrackPlacement {
return info.withMessage("too_sharp")
.tooJumbly();
int minTurnSize = ninety ? 7 : 3;
int maxAscend = ninety ? 3 : 2;
double minTurnSize = ninety ? 7 : 3.25;
double turnSizeToFitAscend =
minTurnSize + (ninety ? Math.max(0, absAscend - 3) * 2f : Math.max(0, absAscend - 1.5f) * 1.5f);
if (turnSize < minTurnSize)
return info.withMessage("too_sharp");
if (absAscend > maxAscend)
if (turnSize < turnSizeToFitAscend)
return info.withMessage("too_steep");
// This is for standardising curve sizes
ex1 += (turnSize - minTurnSize) / axis1.length();
ex2 += (turnSize - minTurnSize) / axis2.length();
info.end1Extent = Math.round(ex1);
info.end2Extent = Math.round(ex2);
turnSize = minTurnSize;
ex1 += (turnSize - turnSizeToFitAscend) / axis1.length();
ex2 += (turnSize - turnSizeToFitAscend) / axis2.length();
info.end1Extent = Mth.floor(ex1);
info.end2Extent = Mth.floor(ex2);
turnSize = turnSizeToFitAscend;
}
Vec3 offset1 = axis1.scale(info.end1Extent);

View File

@ -0,0 +1,139 @@
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

@ -0,0 +1,66 @@
package com.simibubi.create.content.logistics.trains.track;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Supplier;
import com.simibubi.create.AllItems;
import com.simibubi.create.content.logistics.trains.ITrackBlock;
import com.simibubi.create.foundation.networking.SimplePacketBase;
import net.minecraft.core.BlockPos;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.network.NetworkEvent.Context;
public class TrackRemovalPacket extends SimplePacketBase {
private Set<BlockPos> tracks;
public TrackRemovalPacket(Set<BlockPos> tracks) {
this.tracks = tracks;
}
public TrackRemovalPacket(FriendlyByteBuf buffer) {
tracks = new HashSet<>();
int size = buffer.readVarInt();
for (int i = 0; i < size; i++)
tracks.add(buffer.readBlockPos());
}
@Override
public void write(FriendlyByteBuf buffer) {
buffer.writeVarInt(tracks.size());
tracks.forEach(buffer::writeBlockPos);
}
@Override
public void handle(Supplier<Context> context) {
Context ctx = context.get();
ctx.enqueueWork(() -> {
ServerPlayer sender = ctx.getSender();
Level level = sender.level;
if (!AllItems.WRENCH.isIn(sender.getMainHandItem()))
return;
for (BlockPos blockPos : tracks) {
BlockState blockState = level.getBlockState(blockPos);
if (!blockPos.closerThan(sender.blockPosition(), 48))
continue;
if (!(blockState.getBlock()instanceof ITrackBlock track))
continue;
if (!sender.mayInteract(level, blockPos))
continue;
level.destroyBlock(blockPos, !sender.isCreative());
}
sender.displayClientMessage(new TextComponent("Tracks removed successfully"), true);
});
ctx.setPacketHandled(true);
}
}

View File

@ -37,6 +37,7 @@ import com.simibubi.create.content.logistics.trains.entity.CarriageCouplingRende
import com.simibubi.create.content.logistics.trains.entity.TrainRelocator;
import com.simibubi.create.content.logistics.trains.management.edgePoint.TrackTargetingBlockItem;
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.ui.BaseConfigScreen;
import com.simibubi.create.foundation.fluid.FluidHelper;
@ -153,6 +154,7 @@ public class ClientEvents {
ToolboxHandlerClient.clientTick();
TrackTargetingBlockItem.clientTick();
TrackPlacement.clientTick();
TrackRemoval.clientTick();
TrainRelocator.clientTick();
}

View File

@ -53,6 +53,7 @@ import com.simibubi.create.content.logistics.trains.management.edgePoint.station
import com.simibubi.create.content.logistics.trains.management.edgePoint.station.TrainEditPacket;
import com.simibubi.create.content.logistics.trains.management.edgePoint.station.TrainEditPacket.TrainEditReturnPacket;
import com.simibubi.create.content.logistics.trains.management.schedule.ScheduleEditPacket;
import com.simibubi.create.content.logistics.trains.track.TrackRemovalPacket;
import com.simibubi.create.content.schematics.packet.ConfigureSchematicannonPacket;
import com.simibubi.create.content.schematics.packet.InstantSchematicPacket;
import com.simibubi.create.content.schematics.packet.SchematicPlacePacket;
@ -118,6 +119,7 @@ public enum AllPackets {
C_CONFIGURE_TRAIN(TrainEditPacket.class, TrainEditPacket::new, PLAY_TO_SERVER),
RELOCATE_TRAIN(TrainRelocationPacket.class, TrainRelocationPacket::new, PLAY_TO_SERVER),
CONTROLS_INPUT(ControlsInputPacket.class, ControlsInputPacket::new, PLAY_TO_SERVER),
REMOVE_TRACKS(TrackRemovalPacket.class, TrackRemovalPacket::new, PLAY_TO_SERVER),
// Server to Client
SYMMETRY_EFFECT(SymmetryEffectPacket.class, SymmetryEffectPacket::new, PLAY_TO_CLIENT),