mirror of
https://github.com/Creators-of-Create/Create.git
synced 2024-12-15 02:23:42 +01:00
Central sta.. Tab, Enter.
- Added auto-completion functionality to schedules' destination editor
This commit is contained in:
parent
b85c0ed93f
commit
48dcf583c1
5 changed files with 202 additions and 22 deletions
|
@ -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) {
|
||||
|
|
|
@ -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<IntAttached<String>> viableStations;
|
||||
private String previous = "<>";
|
||||
private Font font;
|
||||
private boolean active;
|
||||
|
||||
List<Suggestion> currentSuggestions;
|
||||
private int yOffset;
|
||||
|
||||
public DestinationSuggestions(Minecraft pMinecraft, Screen pScreen, EditBox pInput, Font pFont,
|
||||
List<IntAttached<String>> 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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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<ScheduleContainer> {
|
||||
|
@ -84,6 +94,8 @@ public class ScheduleScreen extends AbstractSimiContainerScreen<ScheduleContaine
|
|||
private List<Pair<GuiEventListener, BiConsumer<IScheduleInput, GuiEventListener>>> editorSubWidgets;
|
||||
private List<Integer> 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<ScheduleContaine
|
|||
addRenderableWidget(editorDelete);
|
||||
}
|
||||
|
||||
private void onDestinationEdited(String text) {
|
||||
if (destinationSuggestions != null)
|
||||
destinationSuggestions.updateCommandInfo();
|
||||
}
|
||||
|
||||
protected void stopEditing() {
|
||||
confirmButton.visible = true;
|
||||
cyclicButton.visible = true;
|
||||
|
@ -235,6 +252,8 @@ public class ScheduleScreen extends AbstractSimiContainerScreen<ScheduleContaine
|
|||
if (editingCondition == null && editingDestination == null)
|
||||
return;
|
||||
|
||||
destinationSuggestions = null;
|
||||
|
||||
removeWidget(scrollInput);
|
||||
removeWidget(scrollInputLabel);
|
||||
removeWidget(editorConfirm);
|
||||
|
@ -259,6 +278,7 @@ public class ScheduleScreen extends AbstractSimiContainerScreen<ScheduleContaine
|
|||
}
|
||||
|
||||
protected void updateEditorSubwidgets(IScheduleInput field) {
|
||||
destinationSuggestions = null;
|
||||
menu.targetSlotActive = field.needsSlot();
|
||||
editorSubWidgets.forEach(p -> removeWidget(p.getFirst()));
|
||||
editorSubWidgets.clear();
|
||||
|
@ -268,6 +288,17 @@ public class ScheduleScreen extends AbstractSimiContainerScreen<ScheduleContaine
|
|||
|
||||
if (editorSubWidgets.isEmpty())
|
||||
editorDividers = null;
|
||||
|
||||
if (field instanceof DestinationInstruction) {
|
||||
EditBox destinationBox = (EditBox) editorSubWidgets.get(0)
|
||||
.getFirst();
|
||||
destinationSuggestions = new DestinationSuggestions(this.minecraft, this, destinationBox, this.font,
|
||||
getViableStations(field), topPos + 33);
|
||||
destinationSuggestions.setAllowSuggestions(true);
|
||||
destinationSuggestions.updateCommandInfo();
|
||||
destinationBox.setResponder(this::onDestinationEdited);
|
||||
}
|
||||
|
||||
editorSubWidgets.forEach(pair -> {
|
||||
GuiEventListener e = pair.getFirst();
|
||||
if (e instanceof AbstractSimiWidget)
|
||||
|
@ -277,6 +308,42 @@ public class ScheduleScreen extends AbstractSimiContainerScreen<ScheduleContaine
|
|||
});
|
||||
}
|
||||
|
||||
private List<IntAttached<String>> getViableStations(IScheduleInput field) {
|
||||
GlobalRailwayManager railwayManager = Create.RAILWAYS.sided(null);
|
||||
Set<TrackGraph> 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<TrackGraph> 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<String> 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<ScheduleContaine
|
|||
for (LerpedFloat lerpedFloat : horizontalScrolls)
|
||||
lerpedFloat.tickChaser();
|
||||
|
||||
if (destinationSuggestions != null)
|
||||
destinationSuggestions.tick();
|
||||
|
||||
schedule.savedProgress =
|
||||
schedule.entries.isEmpty() ? 0 : Mth.clamp(schedule.savedProgress, 0, schedule.entries.size() - 1);
|
||||
resetProgress.active = schedule.savedProgress > 0;
|
||||
|
@ -813,6 +883,9 @@ public class ScheduleScreen extends AbstractSimiContainerScreen<ScheduleContaine
|
|||
|
||||
@Override
|
||||
public boolean mouseClicked(double pMouseX, double pMouseY, int pButton) {
|
||||
if (destinationSuggestions != null
|
||||
&& destinationSuggestions.mouseClicked((int) pMouseX, (int) pMouseY, pButton))
|
||||
return true;
|
||||
if (editorConfirm != null && editorConfirm.isMouseOver(pMouseX, pMouseY) && onEditorClose != null) {
|
||||
onEditorClose.accept(true);
|
||||
stopEditing();
|
||||
|
@ -831,6 +904,8 @@ public class ScheduleScreen extends AbstractSimiContainerScreen<ScheduleContaine
|
|||
|
||||
@Override
|
||||
public boolean keyPressed(int pKeyCode, int pScanCode, int pModifiers) {
|
||||
if (destinationSuggestions != null && destinationSuggestions.keyPressed(pKeyCode, pScanCode, pModifiers))
|
||||
return true;
|
||||
if (editingCondition == null && editingDestination == null)
|
||||
return super.keyPressed(pKeyCode, pScanCode, pModifiers);
|
||||
InputConstants.Key mouseKey = InputConstants.getKey(pKeyCode, pScanCode);
|
||||
|
@ -846,6 +921,8 @@ public class ScheduleScreen extends AbstractSimiContainerScreen<ScheduleContaine
|
|||
|
||||
@Override
|
||||
public boolean mouseScrolled(double pMouseX, double pMouseY, double pDelta) {
|
||||
if (destinationSuggestions != null && destinationSuggestions.mouseScrolled(Mth.clamp(pDelta, -1.0D, 1.0D)))
|
||||
return true;
|
||||
if (editingCondition != null || editingDestination != null)
|
||||
return super.mouseScrolled(pMouseX, pMouseY, pDelta);
|
||||
|
||||
|
@ -910,7 +987,15 @@ public class ScheduleScreen extends AbstractSimiContainerScreen<ScheduleContaine
|
|||
|
||||
@Override
|
||||
protected void renderForeground(PoseStack matrixStack, int mouseX, int mouseY, float partialTicks) {
|
||||
if (destinationSuggestions != null) {
|
||||
matrixStack.pushPose();
|
||||
matrixStack.translate(0, 0, 500);
|
||||
destinationSuggestions.render(matrixStack, mouseX, mouseY);
|
||||
matrixStack.popPose();
|
||||
}
|
||||
|
||||
super.renderForeground(matrixStack, mouseX, mouseY, partialTicks);
|
||||
|
||||
GuiGameElement.of(menu.contentHolder).<GuiGameElement
|
||||
.GuiRenderBuilder>at(leftPos + AllGuiTextures.SCHEDULE.width, topPos + AllGuiTextures.SCHEDULE.height - 56,
|
||||
-200)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
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 <init>(Lnet/minecraft/client/gui/components/CommandSuggestions;IIILjava/util/List;Z)V # <init>
|
Loading…
Reference in a new issue