/// <summary> /// Make sure the junction nodes of have the exact location of the junctions in the track database. /// This is to make sure changes in the track database are taken over in the path /// </summary> void SnapAllJunctionNodes() { TrainpathNode mainNode = CurrentTrainPath.FirstNode; while (mainNode != null) { //siding path. For this routine we do not care if junctions are done twice TrainpathNode sidingNode = mainNode.NextSidingNode; while (sidingNode != null) { TrainpathJunctionNode sidingNodeAsJunction = sidingNode as TrainpathJunctionNode; if ((sidingNodeAsJunction != null) && !sidingNode.IsBroken) { sidingNode.Location = DrawTrackDB.UidLocation(trackDB.TrackNodes[sidingNodeAsJunction.JunctionIndex].UiD); } sidingNode = sidingNode.NextSidingNode; } TrainpathJunctionNode mainNodeAsJunction = mainNode as TrainpathJunctionNode; if ((mainNodeAsJunction != null) && !mainNode.IsBroken) { mainNode.Location = DrawTrackDB.UidLocation(trackDB.TrackNodes[mainNodeAsJunction.JunctionIndex].UiD); } mainNode = mainNode.NextMainNode; } }
/// <summary> /// Constructor. This will actually load the .pat from file and create menus as needed /// </summary> /// <param name="routeData">The route information that contains track data base and track section data</param> /// <param name="drawTrackDB">The drawn tracks to know about where the mouse is</param> /// <param name="path">Path to the .pat file</param> public PathEditor(RouteData routeData, DrawTrackDB drawTrackDB, ORTS.Menu.Path path) : this(routeData, drawTrackDB) { FileName = path.FilePath.Split('\\').Last(); CurrentTrainPath = new Trainpath(trackDB, tsectionDat, path.FilePath); EditingIsActive = false; OnPathChanged(); }
/// <summary> /// Calculate the closest distance to a track, as well as the 'longitude' along it. /// </summary> /// <param name="trackVectorSection">The vectorsection for which we want to know the distance</param> /// <param name="trackSection">The corresponding tracksection</param> /// <returns>Distance Squared to the track as well as length along track.</returns> /// <remarks>Partly the same code as in Traveller.cs, but here no culling, and we just want the distance. /// The math here is not perfect (it is quite difficult to calculate the distances to a curved line /// for all possibilities) but good enough. The math was designed (in Traveller.cs) to work well for close distances. /// Math is modified to prevent NaN and to combine straight and curved tracks.</remarks> DistanceLon CalcRealDistanceSquared(TrVectorSection trackVectorSection, TrackSection trackSection) { //Calculate the vector from start of track to the mouse Vector3 vectorToMouse = new Vector3 { X = storedMouseLocation.Location.X - trackVectorSection.X, Z = storedMouseLocation.Location.Z - trackVectorSection.Z }; vectorToMouse.X += (storedMouseLocation.TileX - trackVectorSection.TileX) * 2048; vectorToMouse.Z += (storedMouseLocation.TileZ - trackVectorSection.TileZ) * 2048; //Now rotate the vector such that a direction along the track is in a direction (x=0, z=1) vectorToMouse = Vector3.Transform(vectorToMouse, Matrix.CreateRotationY(-trackVectorSection.AY)); float lon, lat; if (trackSection.SectionCurve == null) { //Track is straight. In this coordinate system, the distance along track (lon) and orthogonal to track (lat) are easy. lon = vectorToMouse.Z; lat = vectorToMouse.X; } else { // make sure the vector is as if the vector section turns to the left. // The center of the curved track is now a (x=-radius, z=0), track starting at (0,0), pointing in positive Z if (trackSection.SectionCurve.Angle > 0) { vectorToMouse.X *= -1; } //make vector relative to center of curve. Track now starts at (radius,0) vectorToMouse.X += trackSection.SectionCurve.Radius; float radiansAlongCurve = (float)Math.Atan2(vectorToMouse.Z, vectorToMouse.X); //The following calculations make sense when close to the track. Otherwise they are not necessarily sensible, but at least well-defined. // Distance from mouse to circle through track section. lat = (float)Math.Sqrt(vectorToMouse.X * vectorToMouse.X + vectorToMouse.Z * vectorToMouse.Z) - trackSection.SectionCurve.Radius; lon = radiansAlongCurve * trackSection.SectionCurve.Radius; } float trackSectionLength = DrawTrackDB.GetLength(trackSection); if (lon < 0) { // distance from start of track return(new DistanceLon(lat * lat + lon * lon, 0)); } if (lon > trackSectionLength) { // distance from end of track return(new DistanceLon(lat * lat + (lon - trackSectionLength) * (lon - trackSectionLength), trackSectionLength)); //idem } // somewhere along track. Distance is only in lateral direction return(new DistanceLon(lat * lat, lon)); }
/// <summary> /// Constructor. This will create a new empty path. /// </summary> /// <param name="routeData">The route information that contains track data base and track section data</param> /// <param name="drawTrackDB">The drawn tracks to know about where the mouse is</param>/// <param name="pathsDirectory">The directory where paths will be stored</param> public PathEditor(RouteData routeData, DrawTrackDB drawTrackDB, string pathsDirectory) : this(routeData, drawTrackDB) { CurrentTrainPath = new Trainpath(trackDB, tsectionDat); FileName = CurrentTrainPath.PathId + ".pat"; CurrentTrainPath.FilePath = System.IO.Path.Combine(pathsDirectory, FileName); EditingIsActive = true; OnPathChanged(); }
/// <summary> /// Find a road trackItem and center around it and highlight it /// </summary> /// <param name="trackItemIndex">Index of the track item</param> public void CenterAroundTrackItemRoad(int trackItemIndex) { WorldLocation itemLocation = DrawTrackDB.TrackItemHighlightOverrideRoad(trackItemIndex); if (itemLocation == WorldLocation.None) { return; } CenterAround(itemLocation); }
/// <summary> /// Reset zoom area to show complete track /// </summary> /// <param name="drawTrackDB">Track database used to determine what the complete track area is</param> public void ZoomReset(DrawTrackDB drawTrackDB) { if (drawTrackDB == null) { return; } double minX = drawTrackDB.MinTileX * WorldLocation.TileSize - 1024f; double minZ = drawTrackDB.MinTileZ * WorldLocation.TileSize - 1024f; double maxX = drawTrackDB.MaxTileX * WorldLocation.TileSize + 1024f; double maxZ = drawTrackDB.MaxTileZ * WorldLocation.TileSize + 1024f; SetDrawArea(minX, maxX, minZ, maxZ); }
/// <summary> /// Reset zoom area to show complete track /// </summary> /// <param name="drawTrackDB">Track database used to determine what the complete track area is</param> public void ZoomReset(DrawTrackDB drawTrackDB) { if (drawTrackDB == null) { return; } float minX = drawTrackDB.MinTileX * 2048 - 1024f; float minZ = drawTrackDB.MinTileZ * 2048 - 1024f; float maxX = drawTrackDB.MaxTileX * 2048 + 1024f; float maxZ = drawTrackDB.MaxTileZ * 2048 + 1024f; SetDrawArea(minX, maxX, minZ, maxZ); }
/// <summary> /// Find the exact distance of the start of the current tracksection (from the beginning of the vector node) /// </summary> /// <returns></returns> private float GetSectionStartDistance() { float distanceFromStart = 0; TrackVectorNode tn = TrackDB.TrackNodes[TvnIndex] as TrackVectorNode; for (int tvsi = 0; tvsi < TrackVectorSectionIndex; tvsi++) { TrackVectorSection tvs = tn.TrackVectorSections[tvsi]; TrackSection trackSection = TsectionDat.TrackSections.Get(tvs.SectionIndex); if (trackSection != null) // if trackSection is missing somehow, well, do without. { distanceFromStart += DrawTrackDB.GetLength(trackSection); } } return(distanceFromStart); }
/// <summary> /// base constructor that only stores the information of the tracks. /// </summary> /// <param name="routeData">The route information that contains track data base and track section data</param> /// <param name="drawTrackDB">The drawn tracks to know about where the mouse is</param> private PathEditor(RouteData routeData, DrawTrackDB drawTrackDB) { this.drawTrackDB = drawTrackDB; this.trackDB = routeData.TrackDB; this.tsectionDat = routeData.TsectionDat; TrackExtensions.Initialize(trackDB.TrackNodes, tsectionDat); // we might be calling this more than once, but so be it. enableMouseUpdate = true; drawPath = new DrawPath(trackDB, tsectionDat); CreateNonMenuActions(); CreateDirectActions(); CreateContextMenuEntries(); CreateContextMenu(); }
/// <summary> /// Constructor /// </summary> /// <param name="routeData">The route information that contains track data base and track section data</param> /// <param name="drawTrackDB">The drawn tracks to know about where the mouse is</param> public AutoFixAllPaths(RouteData routeData, DrawTrackDB drawTrackDB) { this.routeData = routeData; this.drawTrackDB = drawTrackDB; }
/// <summary> /// Set the location from the tracknode database. /// </summary> public void SetLocationFromTrackNode() { Location = DrawTrackDB.UidLocation(TrackDB.TrackNodes[JunctionIndex].UiD); }
/// <summary> /// Set and load a new route /// </summary> /// <param name="newRoute">The route to load, containing amongst other the directory name of the route</param> public void SetRoute(Route newRoute) { if (newRoute == null) { return; } if (!CanDiscardModifiedPath()) { return; } DrawLoadingMessage(catalog.GetString("Loading route...")); MessageDelegate messageHandler = new MessageDelegate(DrawLoadingMessage); try { RouteData = new RouteData(newRoute.Path, messageHandler); DrawTrackDB = new DrawTrackDB(this.RouteData, messageHandler); CurrentRoute = newRoute; Properties.Settings.Default.defaultRoute = CurrentRoute.Path.Split('\\').Last(); if (Properties.Settings.Default.zoomRoutePath != CurrentRoute.Path) { Properties.Settings.Default.zoomScale = -1; // To disable the use of zoom reset } Properties.Settings.Default.Save(); DrawArea.ZoomReset(DrawTrackDB); drawAreaInset.ZoomReset(DrawTrackDB); SetTitle(); } catch { MessageBox.Show(catalog.GetString("Route cannot be loaded. Sorry")); } if (CurrentRoute == null) { return; } PathEditor = null; DrawMultiplePaths = null; try { findPaths(); } catch { } try { drawWorldTiles.SetRoute(CurrentRoute.Path); drawTerrain = new DrawTerrain(CurrentRoute.Path, messageHandler, drawWorldTiles); drawTerrain.LoadContent(GraphicsDevice); menuControl.MenuSetShowTerrain(false); menuControl.MenuSetShowDMTerrain(false); } catch { } menuControl.PopulatePlatforms(); menuControl.PopulateStations(); menuControl.PopulateSidings(); }
/// <summary> /// This is called when the game should draw itself. /// </summary> /// <param name="gameTime">Provides a snapshot of timing values.</param> protected override void Draw(GameTime gameTime) { // Even if there is nothing new to draw for main window, we might still need to draw for the shadow textures. if (DrawTrackDB != null && Properties.Settings.Default.showInset) { drawAreaInset.DrawShadowTextures(DrawTrackDB.DrawTracks, DrawColors.colorsNormal.ClearWindowInset); } // if there is nothing to draw, be done. if (--skipDrawAmount > 0) { return; } GraphicsDevice.Clear(DrawColors.colorsNormal.ClearWindow); if (DrawTrackDB == null) { return; } spriteBatch.Begin(); if (drawTerrain != null) { drawTerrain.Draw(DrawArea); } drawWorldTiles.Draw(DrawArea); DrawArea.DrawTileGrid(); if (drawTerrain != null) { drawTerrain.DrawPatchLines(DrawArea); } DrawTrackDB.DrawRoads(DrawArea); DrawTrackDB.DrawTracks(DrawArea); DrawTrackDB.DrawTrackHighlights(DrawArea, true); DrawTrackDB.DrawJunctionAndEndNodes(DrawArea); if (Properties.Settings.Default.showInset) { drawAreaInset.DrawBackground(DrawColors.colorsNormal.ClearWindowInset); //drawTrackDB.DrawTracks(drawAreaInset); //replaced by next line drawAreaInset.DrawShadowedTextures(); DrawTrackDB.DrawTrackHighlights(drawAreaInset, false); drawAreaInset.DrawBorder(Color.Red, DrawArea); drawAreaInset.DrawBorder(Color.Black); } if (DrawMultiplePaths != null) { DrawMultiplePaths.Draw(DrawArea); } if (DrawPATfile != null && Properties.Settings.Default.showPATfile) { DrawPATfile.Draw(DrawArea); } if (PathEditor != null && Properties.Settings.Default.showTrainpath) { PathEditor.Draw(DrawArea); } DrawTrackDB.DrawRoadTrackItems(DrawArea); DrawTrackDB.DrawTrackItems(DrawArea); DrawTrackDB.DrawItemHighlights(DrawArea); CalculateFPS(gameTime); statusBarControl.Update(this, DrawArea.MouseLocation); drawScaleRuler.Draw(); drawLongitudeLatitude.Draw(DrawArea.MouseLocation); DebugWindow.DrawAll(); spriteBatch.End(); base.Draw(gameTime); skipDrawAmount = maxSkipDrawAmount; }
/// <summary> /// Allows the game to run logic such as updating the world, /// checking for collisions, gathering input, and playing audio. /// </summary> /// <param name="gameTime">Provides a snapshot of timing values.</param> protected override void Update(GameTime gameTime) { if (!this.IsActive) { lostFocus = true; return; } TVUserInput.Update(); if (lostFocus) { // if the previous call was in inactive mode, we do want TVUserInput to be updated, but we will only // act on it the next round. To make sure moving the mouse to other locations and back is influencing // the location visible in trackviewer. lostFocus = false; return; } fontManager.Update(GraphicsDevice); if (DrawTrackDB != null) { // when update is called, we are not searching via menu DrawTrackDB.ClearHighlightOverrides(); } // First check all the buttons that can be kept down. if (this.drawPathChart.IsActived) { if (TVUserInput.IsDown(TVUserCommands.ShiftLeft)) { drawPathChart.Shift(-1); skipDrawAmount = 0; } if (TVUserInput.IsDown(TVUserCommands.ShiftRight)) { drawPathChart.Shift(1); skipDrawAmount = 0; } if (TVUserInput.IsDown(TVUserCommands.ZoomIn)) { drawPathChart.Zoom(-1); skipDrawAmount = 0; } if (TVUserInput.IsDown(TVUserCommands.ZoomOut)) { drawPathChart.Zoom(1); skipDrawAmount = 0; } } else { if (TVUserInput.IsDown(TVUserCommands.ShiftLeft)) { DrawArea.ShiftLeft(); skipDrawAmount = 0; } if (TVUserInput.IsDown(TVUserCommands.ShiftRight)) { DrawArea.ShiftRight(); skipDrawAmount = 0; } if (TVUserInput.IsDown(TVUserCommands.ShiftUp)) { DrawArea.ShiftUp(); skipDrawAmount = 0; } if (TVUserInput.IsDown(TVUserCommands.ShiftDown)) { DrawArea.ShiftDown(); skipDrawAmount = 0; } if (TVUserInput.IsDown(TVUserCommands.ZoomIn)) { DrawArea.Zoom(-1); skipDrawAmount = 0; } if (TVUserInput.IsDown(TVUserCommands.ZoomOut)) { DrawArea.Zoom(1); skipDrawAmount = 0; } } if (TVUserInput.Changed) { skipDrawAmount = 0; } if (TVUserInput.IsPressed(TVUserCommands.Quit)) { this.Quit(); } if (TVUserInput.IsPressed(TVUserCommands.ReloadRoute)) { this.ReloadRoute(); } if (TVUserInput.IsPressed(TVUserCommands.ShiftToMouseLocation)) { DrawArea.ShiftToLocation(DrawArea.MouseLocation); } if (TVUserInput.IsPressed(TVUserCommands.ZoomInSlow)) { DrawArea.Zoom(-1); } if (TVUserInput.IsPressed(TVUserCommands.ZoomOutSlow)) { DrawArea.Zoom(1); } if (TVUserInput.IsPressed(TVUserCommands.ZoomToTile)) { DrawArea.ZoomToTile(); } if (TVUserInput.IsPressed(TVUserCommands.ZoomReset)) { DrawArea.ZoomReset(DrawTrackDB); drawAreaInset.ZoomReset(DrawTrackDB); // needed in case window was resized } if (DrawPATfile != null && Properties.Settings.Default.showPATfile) { if (TVUserInput.IsPressed(TVUserCommands.ExtendPath)) { DrawPATfile.ExtendPath(); } if (TVUserInput.IsPressed(TVUserCommands.ExtendPathFull)) { DrawPATfile.ExtendPathFull(); } if (TVUserInput.IsPressed(TVUserCommands.ReducePath)) { DrawPATfile.ReducePath(); } if (TVUserInput.IsPressed(TVUserCommands.ReducePathFull)) { DrawPATfile.ReducePathFull(); } if (TVUserInput.IsDown(TVUserCommands.ShiftToPathLocation)) { DrawArea.ShiftToLocation(DrawPATfile.CurrentLocation); } } if (PathEditor != null && Properties.Settings.Default.showTrainpath) { if (TVUserInput.IsPressed(TVUserCommands.ExtendPath)) { PathEditor.ExtendPath(); } if (TVUserInput.IsPressed(TVUserCommands.ExtendPathFull)) { PathEditor.ExtendPathFull(); } if (TVUserInput.IsPressed(TVUserCommands.ReducePath)) { PathEditor.ReducePath(); } if (TVUserInput.IsPressed(TVUserCommands.ReducePathFull)) { PathEditor.ReducePathFull(); } if (TVUserInput.IsDown(TVUserCommands.ShiftToPathLocation)) { DrawArea.ShiftToLocation(PathEditor.CurrentLocation); } if (TVUserInput.IsPressed(TVUserCommands.EditorUndo)) { PathEditor.Undo(); } if (TVUserInput.IsPressed(TVUserCommands.EditorRedo)) { PathEditor.Redo(); } if (TVUserInput.IsMouseXButton1Pressed()) { PathEditor.Undo(); } if (TVUserInput.IsMouseXButton2Pressed()) { PathEditor.Redo(); } } if (PathEditor != null && PathEditor.EditingIsActive) { if (TVUserInput.IsMouseRightButtonPressed()) { PathEditor.OnLeftMouseRelease(); // any action done with left mouse is cancelled now PathEditor.PopupContextMenu(TVUserInput.MouseLocationX, TVUserInput.MouseLocationY); } if (TVUserInput.IsDown(TVUserCommands.EditorTakesMouseClick)) { if (TVUserInput.IsMouseLeftButtonPressed()) { PathEditor.OnLeftMouseClick(TVUserInput.MouseLocationX, TVUserInput.MouseLocationY); } if (TVUserInput.IsMouseLeftButtonDown()) { PathEditor.OnLeftMouseMoved(); // to make sure it is reactive enough, don't even care if mouse is really moved } if (TVUserInput.IsMouseLeftButtonReleased()) { PathEditor.OnLeftMouseRelease(); } } if (TVUserInput.IsReleased(TVUserCommands.EditorTakesMouseClick)) { PathEditor.OnLeftMouseCancel(); } drawPathChart.DrawDynamics(); } if (!TVUserInput.IsDown(TVUserCommands.EditorTakesMouseClick) && !this.MenuHasMouse && !this.drawPathChart.IsActived) { if (TVUserInput.IsMouseMoved() && TVUserInput.IsMouseLeftButtonDown()) { DrawArea.ShiftArea(TVUserInput.MouseMoveX(), TVUserInput.MouseMoveY()); } } if (TVUserInput.IsMouseWheelChanged()) { int mouseWheelChange = TVUserInput.MouseWheelChange(); if (!this.drawPathChart.IsActived) { if (TVUserInput.IsDown(TVUserCommands.MouseZoomSlow)) { DrawArea.Zoom(mouseWheelChange > 0 ? -1 : 1); } else { DrawArea.Zoom(-mouseWheelChange / 40); } } } DrawArea.Update(); drawAreaInset.Update(); drawAreaInset.Follow(DrawArea, 10f); if (TVUserInput.IsPressed(TVUserCommands.ToggleZoomAroundMouse)) { menuControl.MenuToggleZoomingAroundMouse(); } if (TVUserInput.IsPressed(TVUserCommands.ToggleShowTerrain)) { menuControl.MenuToggleShowTerrain(); } if (TVUserInput.IsPressed(TVUserCommands.ToggleShowDMTerrain)) { menuControl.MenuToggleShowDMTerrain(); } if (TVUserInput.IsPressed(TVUserCommands.ToggleShowPatchLines)) { menuControl.MenuToggleShowPatchLines(); } if (TVUserInput.IsPressed(TVUserCommands.ToggleShowSignals)) { menuControl.MenuToggleShowSignals(); } if (TVUserInput.IsPressed(TVUserCommands.ToggleShowSidings)) { menuControl.MenuToggleShowSidings(); } if (TVUserInput.IsPressed(TVUserCommands.ToggleShowSidingNames)) { menuControl.MenuToggleShowSidingNames(); } if (TVUserInput.IsPressed(TVUserCommands.ToggleShowPlatforms)) { menuControl.MenuToggleShowPlatforms(); } if (TVUserInput.IsPressed(TVUserCommands.ToggleShowPlatformNames)) { menuControl.MenuCirculatePlatformStationNames(); } if (TVUserInput.IsPressed(TVUserCommands.ToggleShowSpeedLimits)) { menuControl.MenuToggleShowSpeedLimits(); } if (TVUserInput.IsPressed(TVUserCommands.ToggleShowMilePosts)) { menuControl.MenuToggleShowMilePosts(); } if (TVUserInput.IsPressed(TVUserCommands.ToggleShowTrainpath)) { menuControl.MenuToggleShowTrainpath(); } if (TVUserInput.IsPressed(TVUserCommands.ToggleShowPatFile)) { menuControl.MenuToggleShowPatFile(); } if (TVUserInput.IsPressed(TVUserCommands.ToggleHighlightTracks)) { menuControl.MenuToggleHighlightTracks(); } if (TVUserInput.IsPressed(TVUserCommands.ToggleHighlightItems)) { menuControl.MenuToggleHighlightItems(); } if (TVUserInput.IsPressed(TVUserCommands.Debug)) { runDebug(); } base.Update(gameTime); }
/// <summary> /// Find a Road track node, center around it and highlight it /// </summary> /// <param name="trackNumberIndex">Index of the track node</param> public void CenterAroundTrackNodeRoad(int trackNumberIndex) { CenterAround(DrawTrackDB.TrackNodeHighlightOverrideRoad(trackNumberIndex)); }