private void UpdatePotentialTracks() { potentialTracks.Clear(); var num = 1f; while (true) { foreach (var allTrack in TrackFinder.AllTracks) { if (RailTrack.GetPointWithinRangeWithYOffset(allTrack, transform.position, num * 200f) .HasValue) { potentialTracks.Add(allTrack); } } if (potentialTracks.Count <= 0 && num <= 4.0) { Debug.LogWarning( string.Format("No tracks in {0} radius. Expanding radius!", (float)(num * 200.0)), this); num += 0.2f; } else { break; } } if (potentialTracks.Count != 0) { return; } Debug.LogError("No near tracks found. Can't spawn crew vehicle"); }
private static StaticPassengerJobDefinition PopulateTransportJobAndSpawn( JobChainController chainController, Station startStation, Track startTrack, Track destTrack, List <TrainCarType> carTypes, StationsChainData chainData, float timeLimit, float initialPay, bool unifyConsist = false) { // Spawn the cars RailTrack startRT = SingletonBehaviour <LogicController> .Instance.LogicToRailTrack[startTrack]; var spawnedCars = CarSpawner.SpawnCarTypesOnTrack(carTypes, startRT, true, 0, false, true); if (spawnedCars == null) { return(null); } chainController.trainCarsForJobChain = spawnedCars; var logicCars = TrainCar.ExtractLogicCars(spawnedCars); if (logicCars == null) { PassengerJobs.ModEntry.Logger.Error("Couldn't extract logic cars, deleting spawned cars"); SingletonBehaviour <CarSpawner> .Instance.DeleteTrainCars(spawnedCars, true); return(null); } if (unifyConsist && SkinManager_Patch.Enabled) { SkinManager_Patch.UnifyConsist(spawnedCars); } return(PopulateTransportJobExistingCars(chainController, startStation, startTrack, destTrack, logicCars, chainData, timeLimit, initialPay)); }
/// <summary> /// Computes if track without revresing is long enough for given length /// </summary> /// <param name="current"></param> /// <param name="from"></param> /// <param name="length"></param> /// <returns></returns> public static bool IsDirectLengthEnough(this RailTrack current, RailTrack from, double length) { if (current.logicTrack.length > length) { return(true); } bool isFromInJuction = current.inIsConnected && current.GetAllInBranches().Any(b => b.track == from); bool isFromOutJuction = current.outIsConnected && current.GetAllOutBranches().Any(b => b.track == from); if (current.inIsConnected && !isFromInJuction) { foreach (var branch in current.GetAllInBranches()) { if (CanGoToDirectly(current, from, branch.track) && IsDirectLengthEnough(branch.track, current, length - current.logicTrack.length)) { return(true); } } } if (current.outIsConnected && !isFromOutJuction) { foreach (var branch in current.GetAllOutBranches()) { if (CanGoToDirectly(current, from, branch.track) && IsDirectLengthEnough(branch.track, current, length - current.logicTrack.length)) { return(true); } } } return(false); }
public bool Receive(TrainCarInformationPacket packet, ClientId client) { var player = MultiPlayerManager.Instance.RemotePlayers[client]; Logger.LogInfo($"Spawning remote car {packet.CarType} at {packet.Position} moved by {WorldMover.currentMove} for {player}"); var prefab = CarTypes.GetCarPrefab(packet.CarType); var(rail, _) = RailTrack.GetClosest(packet.Position + WorldMover.currentMove); TrainCar train; using (SpawningCar) train = CarSpawner.SpawnCar(prefab, rail, packet.Position + WorldMover.currentMove, packet.Forward); if (train.GetComponent <LocoControllerShunter>()) { train.gameObject.AddComponent <LocoStateShunterSync>().Init(packet.Id); } //else if (train.GetComponent<LocoControllerDiesel>()) train.gameObject.AddComponent<LocoStateDieselSync>().Init(packet.Id); //else if (train.GetComponent<LocoControllerSteam>()) train.gameObject.AddComponent<LocoStateHandcarSync>().Init(packet.Id); //else if (train.GetComponent<LocoControllerHandcar>()) train.gameObject.AddComponent<LocoStateHandcarSync>().Init(packet.Id); //else if (train.GetComponent<LocoControllerBase>()) train.gameObject.AddComponent<LocoStateSync>().Init(packet.Id); else { train.gameObject.AddComponent <TrainCarSync>().Init(packet.Id); } train.logicCar.ID = packet.Name; train.trainPlatesCtrl.trainCarPlates.ForEach(plate => plate.id.text = packet.Name); return(true); }
public static (RailTrack, double, bool) GetAheadTrack(this RailTrack current, double currentCarSpan, bool direction, double aheadDistance) { aheadDistance -= direction ? current.logicTrack.length - currentCarSpan : currentCarSpan; while (aheadDistance >= 0.0f) { RailTrack nextTrack = current.GetNextTrack(direction); if (nextTrack == null) { break; } direction = nextTrack.GetDirectionFromPrev(current); current = nextTrack; aheadDistance -= current.logicTrack.length; } double span = direction ? current.logicTrack.length + aheadDistance : -aheadDistance; if (span > current.logicTrack.length) { span = current.logicTrack.length; } return(current, span, direction); }
private void ClearFlags() { destinationTrack = null; canSpawnAtPoint = false; destHighlighter.TurnOff(); SetState(State.EnterSpawnMode); }
public static IEnumerable <TrackEvent> GetTrackEvents(RailTrack track, bool first, double start) { var allTrackEvents = GetTrackEvents(track); var filtered = allTrackEvents.RelativeFromSpan(start, first); // Debug.Log($"allTrackEvents:\n{string.Join("\n",allTrackEvents)}\nfiltered:\n{string.Join("\n",filtered)}"); return(filtered); }
public static GradeEvent?GetGrade(RailTrack track, double startSpan, bool direction) { var events = FollowTrack(track, startSpan, direction ? float.NegativeInfinity : float.PositiveInfinity); return(events .OfType <GradeEvent>() .FirstOrDefault(ev => !ev.Direction)); }
public static IEnumerable <TrackEvent> GetTrackEvents(RailTrack track) { if (!indexedTracks.TryGetValue(track, out var data)) { data = indexedTracks[track] = GenerateTrackEvents(track).ToList(); } return(data); }
public static float?GetSpeedLimit(RailTrack track, double startSpan, bool direction) { var events = FollowTrack(track, startSpan, direction ? /*float.NegativeInfinity*/ -1000 : /*float.PositiveInfinity*/ 1000); return(events .OfType <SpeedLimitEvent>() .FirstOrDefault(ev => !ev.Direction) ?.limit); }
public static bool GetDirectionFromPrev(this RailTrack current, RailTrack prev) { if (IsTrackOutBranch(current, prev)) { return(false); } return(true); }
public static bool IsTrackOutBranch(this RailTrack current, RailTrack next) { if (!current.outIsConnected) { return(false); } return(current.GetAllOutBranches().Any(b => b.track == next)); }
public static IEnumerable <TrackEvent> GetTrackEvents(RailTrack track) { if (!indexedTracks.TryGetValue(track, out var data)) { float start = Time.realtimeSinceStartup; data = indexedTracks[track] = GenerateTrackEvents(track).ToList(); float end = Time.realtimeSinceStartup; Main.DebugLog($"Indexed track {track.logicTrack.ID}." + $" Found {data.Count} events ({data.OfType<SpeedLimitEvent>().Count()} speed signs) in {end-start} s."); } return(data); }
public void OnUpdate() { if (potentialTracks.Count == 0 || (uint)(state - 1) > 1U) { return; } if (Physics.Raycast(signalOrigin.position, signalOrigin.forward, out hit, 100f, trackMask)) { var point = hit.point; foreach (var potentialTrack in potentialTracks) { var rangeWithYoffset = RailTrack.GetPointWithinRangeWithYOffset(potentialTrack, point, 3f, -1.75f); if (rangeWithYoffset.HasValue) { destinationTrack = potentialTrack; var startingFromIndex = CarSpawner.FindClosestValidPointForCarStartingFromIndex( potentialTrack.GetPointSet().points, rangeWithYoffset.Value.index, carBounds.extents); var hasValue = startingFromIndex.HasValue; closestPointOnDestinationTrack = !hasValue ? rangeWithYoffset : startingFromIndex; canSpawnAtPoint = hasValue; var position = (Vector3)closestPointOnDestinationTrack.Value.position + WorldMover.currentMove; var forward = closestPointOnDestinationTrack.Value.forward; if (!spawnWithTrackDirection) { forward *= -1f; } destHighlighter.Highlight(position, forward, carBounds, canSpawnAtPoint ? validMaterial : invalidMaterial); display.SetAction(canSpawnAtPoint ? "confirm" : "cancel"); if (canSpawnAtPoint && state == State.PickDestination) { UpdateLCDRerailDirectionArrow(); return; } lcdArrow.TurnOff(); return; } } } canSpawnAtPoint = false; destinationTrack = null; destHighlighter.Highlight(signalOrigin.position + signalOrigin.forward * 20f, signalOrigin.right, carBounds, invalidMaterial); display.SetAction("cancel"); lcdArrow.TurnOff(); }
public static RailTrack GetNextFromPrev(this RailTrack current, RailTrack prev) { if (IsTrackInBranch(current, prev)) { return(GetOutTrack(current)); } if (IsTrackOutBranch(current, prev)) { return(GetInTrack(current)); } return(null); }
private static IEnumerable <TrackEvent> GenerateTrackEvents(RailTrack track) { var pointSet = track.GetPointSet(); EquiPointSet simplified = EquiPointSet.ResampleEquidistant( pointSet, Mathf.Min(SIMPLIFIED_RESOLUTION, (float)pointSet.span / 3)); foreach (var point in simplified.points) { foreach (var trackEvent in FindSigns(point)) { yield return(trackEvent); } } }
private static string DumpNodes(List <RailTrack> neighbors, RailTrack parent) { return("[" + neighbors.Select( t => { string prefix = "NC"; if (parent.outJunction != null) { if (parent.outJunction.inBranch.track == t) { prefix = "OJin"; } if (parent.outJunction.outBranches.Any(b => b.track == t)) { prefix = "OJout"; } } else if (parent.outIsConnected && parent.outBranch.track == t) { prefix = "OB"; } if (parent.inJunction != null) { if (parent.inJunction.inBranch.track == t) { prefix = "IJin"; } if (parent.inJunction.outBranches.Any(b => b.track == t)) { prefix = "IJout"; } } else if (parent.inIsConnected && parent.inBranch.track == t) { prefix = "IB"; } prefix += ":"; return prefix + t.logicTrack.ID.FullID; }) .Aggregate(string.Empty, (a, b) => { return a + "|" + b; }) + "]"); }
public PathFinder(Track start, Track goal) { Terminal.Log($"{start.ID.FullID} -> {goal.ID.FullID}"); RailTrack startTrack = RailTrackRegistry.AllTracks.FirstOrDefault((RailTrack track) => track?.logicTrack.ID.FullID == start.ID.FullID); RailTrack goalTrack = RailTrackRegistry.AllTracks.FirstOrDefault((RailTrack track) => track?.logicTrack.ID.FullID == goal.ID.FullID); if (startTrack == null || goalTrack == null) { Terminal.Log("start track or goal track not found"); return; } this.start = startTrack; this.goal = goalTrack; }
public static bool IsSectorFreeFromJunction(this RailTrack current, double sectorLength, Junction junction) { if (current.inJunction == junction) { return(IsSectorFree(current, sectorLength, false)); } else if (current.outJunction == junction) { return(IsSectorFree(current, sectorLength, true)); } #if DEBUG Terminal.Log($"IsSectorFreeFromJunction: not connected to given junction... track {current.logicTrack.ID.FullID} junction {junction?.GetInstanceID()} inJunction {current.inJunction?.GetInstanceID()} outJunction {current.outJunction?.GetInstanceID()}"); #endif return(true); }
public static bool IsSectorFree(this RailTrack current, double sectorLength, bool fromOutConnection) { if (fromOutConnection) { #if DEBUG2 current.onTrackBogies.ToList().ForEach(b => Terminal.Log($"SpanOut: {b.traveller.Span}")); #endif return(current.onTrackBogies.All(b => b.traveller.Span < (current.logicTrack.length - sectorLength))); } else { #if DEBUG2 current.onTrackBogies.ToList().ForEach(b => Terminal.Log($"SpanIn: {b.traveller.Span}")); #endif return(current.onTrackBogies.All(b => b.traveller.Span > sectorLength)); } }
public static RailTrack GetInTrack(this RailTrack current) { if (current.inJunction != null) { if (current.inJunction.inBranch.track == current) { return(current.inJunction.outBranches[current.inJunction.selectedBranch].track); } else { return(current.inJunction.inBranch.track); } } else { return(current.inBranch.track); } }
public void Initialize(RailTrack track, string name, string yardId) { if (LoadCompletedSound == null) { LoadCompletedSound = PocketWatchManager.Instance.alarmAudio; if (LoadCompletedSound != null) { PassengerJobs.ModEntry.Logger.Log("Grabbed pocket watch bell sound"); } } PlatformTrack = track; TrackId = PlatformTrack.logicTrack.ID.TrackPartOnly; LogicMachine = new WarehouseMachine(track.logicTrack, SUPPORTED_CARGO); PlatformName = name; YardId = yardId; enabled = true; }
// Return a List of Locations representing the found path public async Task <List <RailTrack> > FindPath(bool allowReverse, double consistLength, List <TrackTransition> bannedTransitions) { List <RailTrack> path = new List <RailTrack>(); if (start == null || goal == null) { return(null); } HashSet <string> carsToIgnore = new HashSet <string>(); if (PlayerManager.LastLoco != null) { PlayerManager.LastLoco.trainset.cars.ForEach(c => carsToIgnore.Add(c.logicCar.ID)); } await Astar(allowReverse, carsToIgnore, consistLength, bannedTransitions); RailTrack current = goal; //path.Add(current); while (!current.Equals(start)) { if (!cameFrom.ContainsKey(current)) { Terminal.Log($"cameFrom does not contain current {current.logicTrack.ID.FullID}"); return(null); } path.Add(current); current = cameFrom[current]; } if (path.Count > 0) { path.Add(start); } path.Reverse(); return(path); }
/// <summary> /// If we can go through junctoin without reveresing /// </summary> /// <param name="current"></param> /// <param name="junction"></param> /// <param name="from"></param> /// <param name="to"></param> /// <returns></returns> private static bool CanGoThroughJunctionDirectly(this RailTrack current, Junction junction, RailTrack from, RailTrack to) { bool fromIsOutBranch = junction != null && junction.outBranches.Any(b => b.track == from); if (fromIsOutBranch) { //Terminal.Log($"{from?.logicTrack.ID.FullID} -> {to?.logicTrack.ID.FullID} fromIsOutBranch"); return(false); } bool currentIsOutBranch = junction != null && junction.outBranches.Any(b => b.track == current); if (currentIsOutBranch) { //Terminal.Log($"{from?.logicTrack.ID.FullID} -> {to?.logicTrack.ID.FullID} currentIsOutBranch {junction.inBranch.track.logicTrack.ID.FullID}"); return(junction.inBranch.track == to); } return(true); }
public static bool Prefix(Bogie __instance, RailTrack track, bool first) { var junction = first ? track.inJunction : track.outJunction; if (junction == null) { return(false); } var isBroken = JunctionIsBroken(junction); var branchIndex = junction.outBranches.FindIndex(b => b.track == track); // Main.DebugLog(() => $"branchIndex={branchIndex}, selectedBranch={junction.selectedBranch}"); if (branchIndex < 0) { // facing-point movement if (isBroken && Random.value < Main.settings.damagedJunctionFlipPercent / 100f) { junction.Switch(Junction.SwitchMode.NO_SOUND); } return(false); } if (branchIndex == junction.selectedBranch) { // trailing-point movement on correct branch return(false); } // trailing-point movement on incorrect branch if (!isBroken && Random.value < Main.settings.runningThroughDamagePercent / 100f) { DamageJunction(__instance.Car, junction); } if (Main.settings.forceSwitchOnRunningThrough) { junction.Switch(Junction.SwitchMode.FORCED); } return(false); }
public void UpdatePosition(Bogie bogie) { RailTrack trackCurrent = bogie.track; double spanCurrent = bogie.traveller.Span; if (track == null) { track = trackCurrent; span = spanCurrent; } else if (trackCurrent != track) { trackNext = trackCurrent.GetNextFromPrev(track); trackPrev = track; track = trackCurrent; span = spanCurrent; changedTrack = true; } else { changedTrack = false; moving = dvCar.GetVelocity().sqrMagnitude > 0.02f; if (moving) { double diff = spanCurrent - span; if (diff < -Mathf.Epsilon) { trackNext = trackCurrent.GetInTrack(); } else if (diff > Mathf.Epsilon) { trackNext = trackCurrent.GetOutTrack(); } span = spanCurrent; } } }
public static bool CanGoToDirectly(this RailTrack current, RailTrack from, RailTrack to, out Junction reversingJunction) { reversingJunction = null; bool isInJuction = current.inIsConnected && current.GetAllInBranches().Any(b => b.track == to); bool isOutJuction = current.outIsConnected && current.GetAllOutBranches().Any(b => b.track == to); if (current.inIsConnected) { //Terminal.Log($"IN: {from?.logicTrack.ID.FullID} -> {current.logicTrack.ID.FullID} -> {to?.logicTrack.ID.FullID}"); if (isInJuction && CanGoThroughJunctionDirectly(current, current.inJunction, from, to)) { return(true); } } if (current.outIsConnected) { //Terminal.Log($"OUT: {from?.logicTrack.ID.FullID} -> {current.logicTrack.ID.FullID} -> {to?.logicTrack.ID.FullID}"); if (isOutJuction && CanGoThroughJunctionDirectly(current, current.outJunction, from, to)) { return(true); } } if (isInJuction) { reversingJunction = current.inJunction; } if (isOutJuction) { reversingJunction = current.outJunction; } return(false); }
public static JobChainControllerWithEmptyHaulGeneration GenerateShuntingUnloadJobWithCarSpawning( StationController destinationStation, bool forceLicenseReqs, System.Random rng) { Debug.Log("[PersistentJobs] unload: generating with car spawning"); YardTracksOrganizer yto = YardTracksOrganizer.Instance; List <CargoGroup> availableCargoGroups = destinationStation.proceduralJobsRuleset.inputCargoGroups; int countTrainCars = rng.Next( destinationStation.proceduralJobsRuleset.minCarsPerJob, destinationStation.proceduralJobsRuleset.maxCarsPerJob); if (forceLicenseReqs) { Debug.Log("[PersistentJobs] unload: forcing license requirements"); if (!LicenseManager.IsJobLicenseAcquired(JobLicenses.Shunting)) { Debug.LogError("[PersistentJobs] unload: Trying to generate a ShuntingUnload job with " + "forceLicenseReqs=true should never happen if player doesn't have Shunting license!"); return(null); } availableCargoGroups = (from cg in availableCargoGroups where LicenseManager.IsLicensedForJob(cg.CargoRequiredLicenses) select cg).ToList(); countTrainCars = Math.Min(countTrainCars, LicenseManager.GetMaxNumberOfCarsPerJobWithAcquiredJobLicenses()); } if (availableCargoGroups.Count == 0) { Debug.LogWarning("[PersistentJobs] unload: no available cargo groups"); return(null); } CargoGroup chosenCargoGroup = Utilities.GetRandomFromEnumerable(availableCargoGroups, rng); // choose cargo & trainCar types Debug.Log("[PersistentJobs] unload: choosing cargo & trainCar types"); List <CargoType> availableCargoTypes = chosenCargoGroup.cargoTypes; List <CargoType> orderedCargoTypes = new List <CargoType>(); List <TrainCarType> orderedTrainCarTypes = new List <TrainCarType>(); for (int i = 0; i < countTrainCars; i++) { CargoType chosenCargoType = Utilities.GetRandomFromEnumerable(availableCargoTypes, rng); List <CargoContainerType> availableContainers = CargoTypes.GetCarContainerTypesThatSupportCargoType(chosenCargoType); CargoContainerType chosenContainerType = Utilities.GetRandomFromEnumerable(availableContainers, rng); List <TrainCarType> availableTrainCarTypes = CargoTypes.GetTrainCarTypesThatAreSpecificContainerType(chosenContainerType); TrainCarType chosenTrainCarType = Utilities.GetRandomFromEnumerable(availableTrainCarTypes, rng); orderedCargoTypes.Add(chosenCargoType); orderedTrainCarTypes.Add(chosenTrainCarType); } float approxTrainLength = yto.GetTotalCarTypesLength(orderedTrainCarTypes) + yto.GetSeparationLengthBetweenCars(countTrainCars); // choose starting track Debug.Log("[PersistentJobs] unload: choosing starting track"); Track startingTrack = Utilities.GetTrackThatHasEnoughFreeSpace(yto, destinationStation.logicStation.yard.TransferInTracks, approxTrainLength); if (startingTrack == null) { Debug.LogWarning("[PersistentJobs] unload: Couldn't find startingTrack with enough free space for train!"); return(null); } // choose random starting station // no need to ensure it has has free space; this is just a back story Debug.Log("[PersistentJobs] unload: choosing origin (inconsequential)"); List <StationController> availableOrigins = new List <StationController>(chosenCargoGroup.stations); StationController startingStation = Utilities.GetRandomFromEnumerable(availableOrigins, rng); // spawn trainCars Debug.Log("[PersistentJobs] unload: spawning trainCars"); RailTrack railTrack = SingletonBehaviour <LogicController> .Instance.LogicToRailTrack[startingTrack]; List <TrainCar> orderedTrainCars = CarSpawner.SpawnCarTypesOnTrack( orderedTrainCarTypes, railTrack, true, 0.0, false, true); if (orderedTrainCars == null) { Debug.LogWarning("[PersistentJobs] unload: Failed to spawn trainCars!"); return(null); } JobChainControllerWithEmptyHaulGeneration jcc = GenerateShuntingUnloadJobWithExistingCars( startingStation, startingTrack, destinationStation, orderedTrainCars, orderedCargoTypes, rng, true); if (jcc == null) { Debug.LogWarning("[PersistentJobs] unload: Couldn't generate job chain. Deleting spawned trainCars!"); SingletonBehaviour <CarSpawner> .Instance.DeleteTrainCars(orderedTrainCars, true); return(null); } return(jcc); }
public static IEnumerable <TrackEvent> FollowTrack(RailTrack track, double startSpan, double distance) { const int MAX_ITERATIONS = 100; double distanceFromStart = 0f; for (int i = 0; i < MAX_ITERATIONS; i++) { yield return(new TrackChangeEvent(distanceFromStart, track.logicTrack.ID)); bool travelDirection = distance > 0; var trackEvents = TrackIndexer .GetTrackEvents(track, travelDirection, startSpan) .Offset(distanceFromStart); foreach (var trackEvent in trackEvents) { yield return(trackEvent); } double newSpan = startSpan + distance; Junction nextJunction; Junction.Branch nextBranch; if (newSpan < 0) { nextBranch = track.GetInBranch(); if (nextBranch == null) { yield break; } distance += startSpan; distanceFromStart += startSpan; if (nextBranch.first) { distance *= -1; } nextJunction = track.inJunction; } else { double trackSpan = track.GetPointSet().span; if (newSpan >= trackSpan) { nextBranch = track.GetOutBranch(); if (nextBranch == null) { yield break; } distance -= trackSpan - startSpan; distanceFromStart += trackSpan - startSpan; if (!nextBranch.first) { distance *= -1; } nextJunction = track.outJunction; } else { yield break; } } if (nextBranch == null) { yield break; } if (nextJunction != null && nextJunction.inBranch.track == track) { yield return(new JunctionEvent(distanceFromStart, true, nextJunction)); } track = nextBranch.track; startSpan = nextBranch.first ? 0.0 : nextBranch.track.GetPointSet().span; } }
public static void Register() { Register("hud.dumpTrack", _ => { if (PlayerManager.Car == null) { return; } var bogie = PlayerManager.Car.Bogies[0]; var track = bogie.track; if (track == null) { return; } var direction = bogie.trackDirection; var span = bogie.traveller.Span; var segments = SignPlacer.GetSegmentInfos(track.curve, 0.4f, 200f, false); var output = $"direction = {direction}, span = {span}\n"; output += string.Join("\n", segments.Select(seg => $"{seg.bezierStartT} -> {seg.bezierEndT}, {seg.segmentLength}, {seg.GetSpeed()}")); Terminal.Log(output); Main.DebugLog(output); }); Register("hud.raycast", _ => { var transform = PlayerManager.PlayerTransform; Terminal.Log($"casting from {transform.position} @ {transform.forward}"); var hits = Physics.RaycastAll( new Ray(transform.position, transform.forward), 1000f, 1 << TrackIndexer.SIGN_COLLIDER_LAYER); foreach (var hit in hits) { Terminal.Log($"hit {hit.collider} at {hit.transform.position}: dp = {Vector3.Dot(transform.forward, hit.transform.forward)}, layer = {hit.collider.gameObject.layer}"); } }); Register("hud.trackevents", _ => { var transform = PlayerManager.PlayerTransform; (RailTrack startTrack, EquiPointSet.Point? point) = RailTrack.GetClosest(transform.position); if (startTrack == null) { return; } var events = TrackIndexer.GetTrackEvents(startTrack); Terminal.Log($"All on track {startTrack.logicTrack.ID}"); foreach (var trackEvent in events) { Terminal.Log(trackEvent.ToString()); Main.DebugLog(trackEvent.ToString()); } var pointForward = point?.forward ?? Vector3.zero; var pointSpan = point?.span ?? 0; var trackDirection = Vector3.Dot(transform.forward, pointForward) > 0f; Terminal.Log($"From {pointSpan} {trackDirection}:"); foreach (var trackEvent in TrackIndexer.GetTrackEvents(startTrack, trackDirection, pointSpan)) { Terminal.Log(trackEvent.ToString()); Main.DebugLog(trackEvent.ToString()); } }); Register("hud.followTrack", _ => { var transform = PlayerManager.PlayerTransform; (RailTrack startTrack, EquiPointSet.Point? point) = RailTrack.GetClosest(transform.position); if (startTrack == null) { return; } var pointForward = point?.forward ?? Vector3.zero; var pointSpan = point?.span ?? 0; var trackDirection = Vector3.Dot(transform.forward, pointForward) > 0f ? 1 : -1; var trackEvents = TrackFollower.FollowTrack(startTrack, pointSpan, trackDirection * 1000f); foreach (var trackEvent in trackEvents) { Terminal.Log(trackEvent.ToString()); Main.DebugLog(trackEvent.ToString()); } }); Register("hud.findCarOnJunction", _ => { var transform = PlayerManager.PlayerTransform; (RailTrack startTrack, EquiPointSet.Point? point) = RailTrack.GetClosest(transform.position); if (startTrack == null) { return; } var pointForward = point?.forward ?? Vector3.zero; var pointSpan = point?.span ?? 0; var trackDirection = Vector3.Dot(transform.forward, pointForward) > 0f ? 1 : -1; var trackEvents = TrackFollower.FollowTrack(startTrack, pointSpan, trackDirection * 1000f); var junction = trackEvents.OfType <JunctionEvent>().FirstOrDefault(); if (junction == null) { Terminal.Log("no junction"); return; } Terminal.Log(Overlay.GetCarOnJunction(junction.junction)?.ID ?? "no car on junction"); }); Register("hud.dumpInterior", _ => { if (PlayerManager.Car == null) { return; } if (PlayerManager.Car.loadedInterior != null) { Terminal.Log(DumpHierarchy(PlayerManager.Car.loadedInterior)); } }); Register("hud.getSpeedLimit", _ => { var transform = PlayerManager.PlayerTransform; (RailTrack startTrack, EquiPointSet.Point? point) = RailTrack.GetClosest(transform.position); if (startTrack == null) { return; } var pointForward = point?.forward ?? Vector3.zero; var pointSpan = point?.span ?? 0; var trackDirection = Vector3.Dot(transform.forward, pointForward) > 0f; Terminal.Log($"{TrackFollower.GetSpeedLimit(startTrack, pointSpan, trackDirection)}"); }); }