diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/station/GlobalStation.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/station/GlobalStation.java index bbdf259e5..39fbf7b73 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/station/GlobalStation.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/station/GlobalStation.java @@ -45,6 +45,8 @@ public class GlobalStation extends SingleTileEdgePoint { super.read(buffer); name = buffer.readUtf(); assembling = buffer.readBoolean(); + if (buffer.readBoolean()) + tilePos = buffer.readBlockPos(); } @Override @@ -59,6 +61,9 @@ public class GlobalStation extends SingleTileEdgePoint { super.write(buffer); buffer.writeUtf(name); buffer.writeBoolean(assembling); + buffer.writeBoolean(tilePos != null); + if (tilePos != null) + buffer.writeBlockPos(tilePos); } public boolean canApproachFrom(TrackNode side) { diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/DestinationSuggestions.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/DestinationSuggestions.java new file mode 100644 index 000000000..43ff18a2b --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/DestinationSuggestions.java @@ -0,0 +1,85 @@ +package com.simibubi.create.content.logistics.trains.management.schedule; + +import java.util.ArrayList; +import java.util.List; + +import com.mojang.brigadier.context.StringRange; +import com.mojang.brigadier.suggestion.Suggestion; +import com.simibubi.create.foundation.utility.IntAttached; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.components.CommandSuggestions; +import net.minecraft.client.gui.components.EditBox; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.util.Mth; + +public class DestinationSuggestions extends CommandSuggestions { + + private EditBox textBox; + private List> viableStations; + private String previous = "<>"; + private Font font; + private boolean active; + + List currentSuggestions; + private int yOffset; + + public DestinationSuggestions(Minecraft pMinecraft, Screen pScreen, EditBox pInput, Font pFont, + List> viableStations, int yOffset) { + super(pMinecraft, pScreen, pInput, pFont, true, true, 0, 7, false, 0xee_303030); + this.textBox = pInput; + this.font = pFont; + this.viableStations = viableStations; + this.yOffset = yOffset; + currentSuggestions = new ArrayList<>(); + active = false; + } + + public void tick() { + if (suggestions == null) + textBox.setSuggestion(""); + if (active == textBox.isFocused()) + return; + active = textBox.isFocused(); + updateCommandInfo(); + } + + @Override + public void updateCommandInfo() { + String value = this.textBox.getValue(); + if (value.equals(previous)) + return; + if (!active) { + suggestions = null; + return; + } + + previous = value; + currentSuggestions = viableStations.stream() + .filter(ia -> !ia.getValue() + .equals(value) && ia.getValue() + .toLowerCase() + .startsWith(value.toLowerCase())) + .sorted((ia1, ia2) -> Integer.compare(ia1.getFirst(), ia2.getFirst())) + .map(IntAttached::getValue) + .map(s -> new Suggestion(new StringRange(0, s.length()), s)) + .toList(); + + showSuggestions(false); + } + + public void showSuggestions(boolean pNarrateFirstSuggestion) { + if (currentSuggestions.isEmpty()) { + suggestions = null; + return; + } + + int width = 0; + for (Suggestion suggestion : currentSuggestions) + width = Math.max(width, this.font.width(suggestion.getText())); + int x = Mth.clamp(textBox.getScreenX(0), 0, textBox.getScreenX(0) + textBox.getInnerWidth() - width); + suggestions = new CommandSuggestions.SuggestionsList(x, 72 + yOffset, width, currentSuggestions, false); + } + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/ScheduleScreen.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/ScheduleScreen.java index abeb14452..61a973f9a 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/ScheduleScreen.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/ScheduleScreen.java @@ -2,8 +2,11 @@ package com.simibubi.create.content.logistics.trains.management.schedule; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Optional; +import java.util.Set; import java.util.function.BiConsumer; import java.util.function.Consumer; @@ -16,6 +19,11 @@ import com.mojang.blaze3d.platform.InputConstants; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.math.Matrix4f; +import com.simibubi.create.Create; +import com.simibubi.create.content.logistics.trains.GlobalRailwayManager; +import com.simibubi.create.content.logistics.trains.TrackGraph; +import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgePointType; +import com.simibubi.create.content.logistics.trains.management.edgePoint.station.GlobalStation; import com.simibubi.create.content.logistics.trains.management.schedule.condition.ScheduleWaitCondition; import com.simibubi.create.content.logistics.trains.management.schedule.condition.ScheduledDelay; import com.simibubi.create.content.logistics.trains.management.schedule.condition.TimedWaitCondition.TimeUnit; @@ -33,6 +41,7 @@ import com.simibubi.create.foundation.gui.widget.Indicator.State; import com.simibubi.create.foundation.gui.widget.Label; import com.simibubi.create.foundation.gui.widget.SelectionScrollInput; import com.simibubi.create.foundation.networking.AllPackets; +import com.simibubi.create.foundation.utility.IntAttached; import com.simibubi.create.foundation.utility.Lang; import com.simibubi.create.foundation.utility.Pair; import com.simibubi.create.foundation.utility.animation.LerpedFloat; @@ -53,6 +62,7 @@ import net.minecraft.util.Mth; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; +import net.minecraft.world.phys.Vec3; import net.minecraftforge.client.gui.GuiUtils; public class ScheduleScreen extends AbstractSimiContainerScreen { @@ -84,6 +94,8 @@ public class ScheduleScreen extends AbstractSimiContainerScreen>> editorSubWidgets; private List editorDividers; + private DestinationSuggestions destinationSuggestions; + public ScheduleScreen(ScheduleContainer container, Inventory inv, Component title) { super(container, inv, title); schedule = new Schedule(); @@ -225,6 +237,11 @@ public class ScheduleScreen extends AbstractSimiContainerScreen removeWidget(p.getFirst())); editorSubWidgets.clear(); @@ -268,6 +288,17 @@ public class ScheduleScreen extends AbstractSimiContainerScreen { GuiEventListener e = pair.getFirst(); if (e instanceof AbstractSimiWidget) @@ -277,6 +308,42 @@ public class ScheduleScreen extends AbstractSimiContainerScreen> getViableStations(IScheduleInput field) { + GlobalRailwayManager railwayManager = Create.RAILWAYS.sided(null); + Set viableGraphs = new HashSet<>(railwayManager.trackNetworks.values()); + + for (ScheduleEntry entry : schedule.entries) { + if (!(entry.instruction instanceof DestinationInstruction destination)) + continue; + if (destination == field) + continue; + String filter = destination.getFilter() + .replace("*", ".*"); + if (filter.isBlank()) + continue; + Graphs: for (Iterator iterator = viableGraphs.iterator(); iterator.hasNext();) { + TrackGraph trackGraph = iterator.next(); + for (GlobalStation station : trackGraph.getPoints(EdgePointType.STATION)) { + if (station.name.matches(filter)) + continue Graphs; + } + iterator.remove(); + } + } + + Vec3 position = minecraft.player.position(); + Set visited = new HashSet<>(); + + return viableGraphs.stream() + .flatMap(g -> g.getPoints(EdgePointType.STATION) + .stream()) + .filter(station -> station.tilePos != null) + .filter(station -> visited.add(station.name)) + .map(station -> IntAttached.with((int) Vec3.atBottomCenterOf(station.tilePos) + .distanceTo(position), station.name)) + .toList(); + } + @Override protected void containerTick() { super.containerTick(); @@ -284,6 +351,9 @@ public class ScheduleScreen extends AbstractSimiContainerScreen 0; @@ -813,6 +883,9 @@ public class ScheduleScreen extends AbstractSimiContainerScreenat(leftPos + AllGuiTextures.SCHEDULE.width, topPos + AllGuiTextures.SCHEDULE.height - 56, -200) 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 4d3f1cf60..c4e480e3d 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 @@ -197,34 +197,36 @@ public class TrackPlacement { if (parallel) { double[] sTest = VecHelper.intersect(end1, end2, normedAxis1, cross2, Axis.Y); - double t = Math.abs(sTest[0]); - double u = Math.abs(sTest[1]); + if (sTest != null) { + double t = Math.abs(sTest[0]); + double u = Math.abs(sTest[1]); - skipCurve = Mth.equal(u, 0); + skipCurve = Mth.equal(u, 0); - if (!skipCurve && sTest[0] < 0) - return info.withMessage("perpendicular") - .tooJumbly(); + if (!skipCurve && sTest[0] < 0) + return info.withMessage("perpendicular") + .tooJumbly(); - if (skipCurve) { - dist = VecHelper.getCenterOf(pos1) - .distanceTo(VecHelper.getCenterOf(pos2)); - info.end1Extent = (int) Math.round((dist + 1) / axis1.length()); + if (skipCurve) { + dist = VecHelper.getCenterOf(pos1) + .distanceTo(VecHelper.getCenterOf(pos2)); + info.end1Extent = (int) Math.round((dist + 1) / axis1.length()); - } else { - if (!Mth.equal(ascend, 0)) - return info.withMessage("ascending_s_curve"); + } else { + if (!Mth.equal(ascend, 0)) + return info.withMessage("ascending_s_curve"); - double targetT = u <= 1 ? 3 : u * 2; + double targetT = u <= 1 ? 3 : u * 2; - if (t < targetT) - return info.withMessage("too_sharp"); + if (t < targetT) + return info.withMessage("too_sharp"); - // This is for standardising s curve sizes - if (t > targetT) { - int correction = (int) ((t - targetT) / axis1.length()); - info.end1Extent = maximiseTurn ? 0 : correction / 2 + (correction % 2); - info.end2Extent = maximiseTurn ? 0 : correction / 2; + // This is for standardising s curve sizes + if (t > targetT) { + int correction = (int) ((t - targetT) / axis1.length()); + info.end1Extent = maximiseTurn ? 0 : correction / 2 + (correction % 2); + info.end2Extent = maximiseTurn ? 0 : correction / 2; + } } } } diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg index 098382e16..b543a5b53 100644 --- a/src/main/resources/META-INF/accesstransformer.cfg +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -37,4 +37,7 @@ public net.minecraft.client.model.AgeableListModel f_170338_ # babyYHeadOffset public net.minecraft.client.model.AgeableListModel f_170339_ # babyZHeadOffset public net.minecraft.client.model.AgeableListModel f_102010_ # babyHeadScale public net.minecraft.client.model.AgeableListModel f_102011_ # babyBodyScale -public net.minecraft.client.model.AgeableListModel f_102012_ # bodyYOffset \ No newline at end of file +public net.minecraft.client.model.AgeableListModel f_102012_ # bodyYOffset + +public net.minecraft.client.gui.components.CommandSuggestions f_93866_ # suggestions +public net.minecraft.client.gui.components.CommandSuggestions$SuggestionsList (Lnet/minecraft/client/gui/components/CommandSuggestions;IIILjava/util/List;Z)V # \ No newline at end of file