Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,24 @@ short for that radius curves tighter, down to the sharpest turn that

Other clicks lay a single arc, as before. Existing track is unchanged.

## Junctions in reverse

Hold A or D to choose a turnout within `junction-arm-distance` of the leading
wheels. Left and right are viewed in the direction the train is travelling.
While stopped, the throttle selects the approach direction; at zero throttle,
the train remembers its last direction.

Choose before the first wheels enter. The points stay locked until the whole
train clears, including when the last carriage leads while reversing. Stopping,
reversing midway, or saving and loading keeps every coupled car on the same
route. Closely spaced junctions retain their own choices while the train spans
them. Coming out of a branch follows its connection back onto the main track.

Train facing and occupied junction choices are saved with the consist. Existing
saves and throttle tapes remain readable. Before downgrading this version,
restore the matching vehicle-data backup: older versions cannot represent a
train facing the opposite way along a track.

## Track ends

Set `behaviour.train.wheel-bones` to the model bones at the frontmost and rearmost
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ public void onEnable() {
trackRegistry = new TrackRegistry(getDataFolder());
trackRegistry.writeInBackground();
trackRegistry.onRebuilt(TrainHandler::retrackTrains);
trackRegistry.onJunctionOccupied(TrainHandler::junctionOccupied);
trackRegistry.occupiedBy(TrainHandler::anyTrainOn);
log = new LogWriter(getDataFolder());
VFLogger.info("Running checks...");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package net.tfminecraft.vehicleframework.database;

import java.util.LinkedHashMap;
import java.util.Map;

import org.json.simple.JSONObject;

public final class ConsistData {
Expand All @@ -10,6 +13,8 @@ public final class ConsistData {
private final Integer travelSign;
private final String junctionId;
private final Boolean diverge;
private final int orientation;
private final Map<String, Boolean> junctions;

public ConsistData(String parent, String child, String splineId, Double s) {
this(parent, child, splineId, s, null, null, null);
Expand All @@ -27,13 +32,27 @@ public ConsistData(
Integer travelSign,
String junctionId,
Boolean diverge) {
this(parent, child, splineId, s, travelSign, junctionId, diverge, 1, Map.of());
}

public ConsistData(String parent, String child, String splineId, Double s, Integer travelSign,
String junctionId, Boolean diverge, int orientation, Map<String, Boolean> junctions) {
this.parent = blankToNull(parent);
this.child = blankToNull(child);
this.splineId = blankToNull(splineId);
this.s = this.splineId == null ? null : s;
this.travelSign = this.splineId == null ? null : normalizeSign(travelSign);
this.junctionId = this.splineId == null ? null : blankToNull(junctionId);
this.diverge = this.junctionId == null ? null : diverge;
this.orientation = normalizeSign(orientation);
Map<String, Boolean> routes = new LinkedHashMap<>();
if (this.junctionId != null) {
routes.put(this.junctionId, Boolean.TRUE.equals(diverge));
}
if (this.splineId != null && junctions != null) {
routes.putAll(junctions);
}
this.junctions = Map.copyOf(routes);
}

public static ConsistData unbound() {
Expand All @@ -51,7 +70,9 @@ public static ConsistData fromJson(JSONObject json) {
numberOrNull(json, "s"),
intOrNull(json, "travelSign"),
stringOrNull(json, "junction"),
boolOrNull(json, "diverge"));
boolOrNull(json, "diverge"),
normalizeSign(intOrNull(json, "orientation")),
readJunctions(json));
}

@SuppressWarnings("unchecked")
Expand All @@ -71,6 +92,14 @@ public void put(JSONObject json) {
json.put("s", s);
}
json.put("travelSign", (long) (travelSign == null ? 1 : travelSign));
if (orientation < 0) {
json.put("orientation", -1L);
}
if (!junctions.isEmpty()) {
JSONObject routes = new JSONObject();
routes.putAll(junctions);
json.put("junctions", routes);
}
if (junctionId != null) {
json.put("junction", junctionId);
if (Boolean.TRUE.equals(diverge)) {
Expand Down Expand Up @@ -112,6 +141,26 @@ public boolean isDiverge() {
return Boolean.TRUE.equals(diverge);
}

public int getOrientation() {
return orientation;
}

public Map<String, Boolean> getJunctions() {
return junctions;
}

private static Map<String, Boolean> readJunctions(JSONObject json) {
Map<String, Boolean> routes = new LinkedHashMap<>();
if (json.get("junctions") instanceof JSONObject raw) {
for (Object key : raw.keySet()) {
if (key instanceof String id && raw.get(key) instanceof Boolean choice) {
routes.put(id, choice);
}
}
}
return routes;
}

private static Integer intOrNull(JSONObject json, String key) {
Double n = numberOrNull(json, key);
if (n == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public static final class Sample {
public final double s;
public final int sign;
public final int throttle;
public final int orientation;
public int holdTicks;
public final String splineId;
public final String junctionId;
Expand All @@ -29,6 +30,11 @@ public Sample(double s, int sign, int throttle, int holdTicks) {
}

public Sample(double s, int sign, int throttle, int holdTicks, String splineId, String junctionId) {
this(s, sign, throttle, holdTicks, splineId, junctionId, 1);
}

public Sample(double s, int sign, int throttle, int holdTicks, String splineId, String junctionId, int orientation) {
this.orientation = orientation < 0 ? -1 : 1;
this.s = s;
this.sign = sign < 0 ? -1 : 1;
this.throttle = throttle;
Expand Down Expand Up @@ -120,21 +126,21 @@ static int lookup(List<Sample> samples, double s, int sign) {
}
matched.sort(Comparator.comparingDouble(a -> a.s));
if (s <= matched.get(0).s) {
return matched.get(0).throttle;
return matched.get(0).throttle * matched.get(0).orientation;
}
Sample last = matched.get(matched.size() - 1);
if (s >= last.s) {
return last.throttle;
return last.throttle * last.orientation;
}
for (int i = 0; i < matched.size() - 1; i++) {
Sample a = matched.get(i);
Sample b = matched.get(i + 1);
if (s >= a.s && s <= b.s) {
if (b.s == a.s) {
return a.throttle;
return a.throttle * a.orientation;
}
double t = (s - a.s) / (b.s - a.s);
return (int) Math.round(a.throttle + t * (b.throttle - a.throttle));
return (int) Math.round(a.throttle * a.orientation + t * (b.throttle * b.orientation - a.throttle * a.orientation));
}
}
return 0;
Expand Down Expand Up @@ -173,6 +179,11 @@ public int targetWithDwell(double s, int sign, DwellState dwell, UUID currentSpl
return targetWithDwell(s, sign, dwell, currentSpline == null ? splineId : currentSpline.toString());
}

/** Converts each recorded sample from its body frame, then into the current body's frame. */
public int targetWithDwell(double s, int sign, DwellState dwell, UUID currentSpline, int orientation) {
return targetWithDwell(s, sign, dwell, currentSpline) * (orientation < 0 ? -1 : 1);
}

public int targetWithDwell(double s, int sign, DwellState dwell, String currentSpline) {
int target = lookup(s, sign, currentSpline);
int hold = holdAt(s, sign, currentSpline);
Expand Down Expand Up @@ -200,6 +211,10 @@ public AppendResult tryAppend(double s, int sign, int throttle) {
}

public AppendResult tryAppend(double s, int sign, int throttle, String sampleSpline, String junctionId) {
return tryAppend(s, sign, throttle, sampleSpline, junctionId, 1);
}

public AppendResult tryAppend(double s, int sign, int throttle, String sampleSpline, String junctionId, int orientation) {
int nsign = sign < 0 ? -1 : 1;
String spline = blankToNull(sampleSpline);
if (spline == null) {
Expand All @@ -211,7 +226,7 @@ public AppendResult tryAppend(double s, int sign, int throttle, String sampleSpl
String lastSpline = last.resolvedSpline(this.splineId);
boolean splineChanged = !sameId(lastSpline, spline);
boolean junctionChanged = !sameId(last.junctionId, junction);
boolean throttleChanged = last.throttle != throttle || last.sign != nsign;
boolean throttleChanged = last.throttle != throttle || last.sign != nsign || last.orientation != orientation;
boolean far = Math.abs(s - last.s) >= HOLD_S;
if (!splineChanged && !junctionChanged && !throttleChanged && !far) {
last.holdTicks++;
Expand All @@ -221,7 +236,7 @@ public AppendResult tryAppend(double s, int sign, int throttle, String sampleSpl
if (samples.size() >= MAX_SAMPLES) {
return AppendResult.CAPPED;
}
samples.add(new Sample(s, nsign, throttle, 1, spline, junction));
samples.add(new Sample(s, nsign, throttle, 1, spline, junction, orientation));
if (samples.size() >= MAX_SAMPLES) {
return AppendResult.CAPPED;
}
Expand All @@ -238,6 +253,7 @@ public JSONObject toJson() {
o.put("s", sample.s);
o.put("sign", (long) sample.sign);
o.put("throttle", (long) sample.throttle);
if (sample.orientation < 0) { o.put("orientation", -1L); }
o.put("hold", (long) sample.holdTicks);
if (sample.splineId != null && !sample.splineId.equalsIgnoreCase(splineId)) {
o.put("splineId", sample.splineId);
Expand Down Expand Up @@ -277,7 +293,8 @@ public static ThrottleTape fromJson(JSONObject root) {
(int) asDouble(o.get("throttle")),
hold,
stringOrNull(o.get("splineId")),
stringOrNull(o.get("junction"))));
stringOrNull(o.get("junction")),
o.get("orientation") == null ? 1 : (int) asDouble(o.get("orientation"))));
}
}
return new ThrottleTape(splineId, loaded);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,20 @@ public final class TrackRegistry {
private BiConsumer<TrackSpline, List<TrackSpline>> rebuilt = (old, next) -> {
};
private Predicate<UUID> occupied = id -> false;
private Predicate<UUID> junctionOccupied = id -> false;

public TrackRegistry(File dataFolder) {
this.store = new TrackStore(dataFolder);
}

public void onJunctionOccupied(Predicate<UUID> occupied) {
junctionOccupied = occupied == null ? id -> false : occupied;
}

public boolean junctionOccupied(UUID id) {
return junctionOccupied.test(id);
}

/**
* Called when a spline's geometry is rebuilt, with the old spline and the
* splines that now carry its track. Arc lengths are not preserved, so
Expand Down Expand Up @@ -836,7 +845,7 @@ public boolean setThrown(UUID id, boolean thrown) {
if (junction == null) {
return false;
}
if (junction.thrown == thrown) {
if (junction.thrown == thrown || junctionOccupied(id)) {
return false;
}
TrackJunction next = junction.withThrown(thrown);
Expand Down
Loading
Loading