From 7ba4af1bea429748a2b05a8d5bf47e8ec385beec Mon Sep 17 00:00:00 2001 From: simibubi <31564874+simibubi@users.noreply.github.com> Date: Thu, 10 Mar 2022 02:04:44 +0100 Subject: [PATCH] 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 --- .../trains/entity/TrainRelocator.java | 4 + .../logistics/trains/track/TrackBlock.java | 9 ++ .../trains/track/TrackPlacement.java | 17 ++- .../logistics/trains/track/TrackRemoval.java | 139 ++++++++++++++++++ .../trains/track/TrackRemovalPacket.java | 66 +++++++++ .../simibubi/create/events/ClientEvents.java | 2 + .../foundation/networking/AllPackets.java | 2 + 7 files changed, 231 insertions(+), 8 deletions(-) create mode 100644 src/main/java/com/simibubi/create/content/logistics/trains/track/TrackRemoval.java create mode 100644 src/main/java/com/simibubi/create/content/logistics/trains/track/TrackRemovalPacket.java diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/entity/TrainRelocator.java b/src/main/java/com/simibubi/create/content/logistics/trains/entity/TrainRelocator.java index b1775efaa..734b5d864 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/entity/TrainRelocator.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/entity/TrainRelocator.java @@ -60,6 +60,10 @@ public class TrainRelocator { static Boolean lastHoveredResult; static List toVisualise; + public static boolean isRelocating() { + return relocatingTrain != null; + } + @OnlyIn(Dist.CLIENT) public static void onClicked(ClickInputEvent event) { if (relocatingTrain == null) diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackBlock.java b/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackBlock.java index 94d752ebc..c71d2f7c4 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackBlock.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackBlock.java @@ -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; } diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackPlacement.java b/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackPlacement.java index a0ec882aa..6b9f9969b 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackPlacement.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackPlacement.java @@ -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); diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackRemoval.java b/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackRemoval.java new file mode 100644 index 000000000..d629b6598 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackRemoval.java @@ -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 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 frontier = new ArrayList<>(); + Set 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 frontier, Set 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; + } + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackRemovalPacket.java b/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackRemovalPacket.java new file mode 100644 index 000000000..b1a832973 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackRemovalPacket.java @@ -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 tracks; + + public TrackRemovalPacket(Set 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 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); + } + +} diff --git a/src/main/java/com/simibubi/create/events/ClientEvents.java b/src/main/java/com/simibubi/create/events/ClientEvents.java index 424658599..ae01b58bf 100644 --- a/src/main/java/com/simibubi/create/events/ClientEvents.java +++ b/src/main/java/com/simibubi/create/events/ClientEvents.java @@ -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(); } diff --git a/src/main/java/com/simibubi/create/foundation/networking/AllPackets.java b/src/main/java/com/simibubi/create/foundation/networking/AllPackets.java index 03e767e96..63f570650 100644 --- a/src/main/java/com/simibubi/create/foundation/networking/AllPackets.java +++ b/src/main/java/com/simibubi/create/foundation/networking/AllPackets.java @@ -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),