/// <summary>Clones this object</summary> /// <returns>The new object</returns> internal AnimatedObject Clone() { AnimatedObject Result = new AnimatedObject { States = new AnimatedObjectState[this.States.Length] }; for (int i = 0; i < this.States.Length; i++) { Result.States[i].Position = this.States[i].Position; if (this.States[i].Object != null) { Result.States[i].Object = (StaticObject)this.States[i].Object.Clone(); } } Result.TrackFollowerFunction = this.TrackFollowerFunction == null ? null : this.TrackFollowerFunction.Clone(); Result.FrontAxlePosition = this.FrontAxlePosition; Result.RearAxlePosition = this.RearAxlePosition; Result.TranslateXScriptFile = this.TranslateXScriptFile; Result.StateFunction = this.StateFunction == null ? null : this.StateFunction.Clone(); Result.CurrentState = this.CurrentState; Result.TranslateZDirection = this.TranslateZDirection; Result.TranslateYDirection = this.TranslateYDirection; Result.TranslateXDirection = this.TranslateXDirection; Result.TranslateXFunction = this.TranslateXFunction == null ? null : this.TranslateXFunction.Clone(); Result.TranslateYFunction = this.TranslateYFunction == null ? null : this.TranslateYFunction.Clone(); Result.TranslateZFunction = this.TranslateZFunction == null ? null : this.TranslateZFunction.Clone(); Result.RotateXDirection = this.RotateXDirection; Result.RotateYDirection = this.RotateYDirection; Result.RotateZDirection = this.RotateZDirection; Result.RotateXFunction = this.RotateXFunction == null ? null : this.RotateXFunction.Clone(); Result.RotateXDamping = this.RotateXDamping == null ? null : this.RotateXDamping.Clone(); Result.RotateYFunction = this.RotateYFunction == null ? null : this.RotateYFunction.Clone(); Result.RotateYDamping = this.RotateYDamping == null ? null : this.RotateYDamping.Clone(); Result.RotateZFunction = this.RotateZFunction == null ? null : this.RotateZFunction.Clone(); Result.RotateZDamping = this.RotateZDamping == null ? null : this.RotateZDamping.Clone(); Result.TextureShiftXDirection = this.TextureShiftXDirection; Result.TextureShiftYDirection = this.TextureShiftYDirection; Result.TextureShiftXFunction = this.TextureShiftXFunction == null ? null : this.TextureShiftXFunction.Clone(); Result.TextureShiftYFunction = this.TextureShiftYFunction == null ? null : this.TextureShiftYFunction.Clone(); Result.LEDClockwiseWinding = this.LEDClockwiseWinding; Result.LEDInitialAngle = this.LEDInitialAngle; Result.LEDLastAngle = this.LEDLastAngle; if (this.LEDVectors != null) { Result.LEDVectors = new Vector3[this.LEDVectors.Length]; for (int i = 0; i < this.LEDVectors.Length; i++) { Result.LEDVectors[i] = this.LEDVectors[i]; } } else { Result.LEDVectors = null; } Result.LEDFunction = this.LEDFunction == null ? null : this.LEDFunction.Clone(); Result.RefreshRate = this.RefreshRate; Result.SecondsSinceLastUpdate = 0.0; Result.ObjectIndex = -1; for (int i = 0; i < Timetable.CustomObjectsUsed; i++) { if (Timetable.CustomObjects[i] == this) { Timetable.AddObjectForCustomTimetable(Result); } } return(Result); }
/// <summary>Is called once a frame to update the station state for the given train</summary> /// <param name="Train">The train</param> /// <param name="TimeElapsed">The frame time elapsed</param> private static void UpdateTrainStation(Train Train, double TimeElapsed) { if (Train.Station >= 0) { int i = Train.Station; int n = Program.CurrentRoute.Stations[Train.Station].GetStopIndex(Train.NumberOfCars); double tf, tb; if (n >= 0) { double p0 = Train.Cars[0].FrontAxle.Follower.TrackPosition - Train.Cars[0].FrontAxle.Position + 0.5 * Train.Cars[0].Length; double p1 = Program.CurrentRoute.Stations[i].Stops[n].TrackPosition; tf = Program.CurrentRoute.Stations[i].Stops[n].ForwardTolerance; tb = Program.CurrentRoute.Stations[i].Stops[n].BackwardTolerance; Train.StationDistanceToStopPoint = p1 - p0; } else { Train.StationDistanceToStopPoint = 0.0; tf = 5.0; tb = 5.0; } if (Train.StationState == TrainStopState.Pending) { Train.StationDepartureSoundPlayed = false; if (Game.StopsAtStation(i, Train)) { Train.StationDepartureSoundPlayed = false; //Check whether all doors are controlled by the driver if (Train.Specs.DoorOpenMode != DoorMode.Manual) { //Check that we are not moving if (Math.Abs(Train.CurrentSpeed) < 0.1 / 3.6 & Math.Abs(Train.Specs.CurrentAverageAcceleration) < 0.1 / 3.6) { //Check the interlock state for the doors switch (Train.SafetySystems.DoorInterlockState) { case DoorInterlockStates.Unlocked: if (Program.CurrentRoute.Stations[i].OpenLeftDoors || Program.CurrentRoute.Stations[i].OpenRightDoors) { AttemptToOpenDoors(Train, i, tb, tf); } break; case DoorInterlockStates.Left: if (Program.CurrentRoute.Stations[i].OpenLeftDoors && !Program.CurrentRoute.Stations[i].OpenRightDoors) { AttemptToOpenDoors(Train, i, tb, tf); } break; case DoorInterlockStates.Right: if (!Program.CurrentRoute.Stations[i].OpenLeftDoors && Program.CurrentRoute.Stations[i].OpenRightDoors) { AttemptToOpenDoors(Train, i, tb, tf); } break; case DoorInterlockStates.Locked: //All doors are currently locked, do nothing break; } } } // detect arrival if (Train.CurrentSpeed > -0.277777777777778 & Train.CurrentSpeed < 0.277777777777778) { bool left, right; if (Program.CurrentRoute.Stations[i].OpenLeftDoors) { left = false; for (int j = 0; j < Train.Cars.Length; j++) { if (Train.Cars[j].Doors[0].AnticipatedOpen) { left = true; break; } } } else { left = true; } if (Program.CurrentRoute.Stations[i].OpenRightDoors) { right = false; for (int j = 0; j < Train.Cars.Length; j++) { if (Train.Cars[j].Doors[1].AnticipatedOpen) { right = true; break; } } } else { right = true; } if (left & right) { // arrival Train.StationState = TrainStopState.Boarding; Train.SafetySystems.StationAdjust.Lit = false; Train.Specs.DoorClosureAttempted = false; Train.SafetySystems.PassAlarm.Halt(); SoundBuffer buffer = (SoundBuffer)Program.CurrentRoute.Stations[i].ArrivalSoundBuffer; if (buffer != null) { OpenBveApi.Math.Vector3 pos = Program.CurrentRoute.Stations[i].SoundOrigin; Program.Sounds.PlaySound(buffer, 1.0, 1.0, pos, false); } Train.StationArrivalTime = Program.CurrentRoute.SecondsSinceMidnight; Train.StationDepartureTime = Program.CurrentRoute.Stations[i].DepartureTime - Train.TimetableDelta; if (Train.StationDepartureTime - Program.CurrentRoute.SecondsSinceMidnight < Program.CurrentRoute.Stations[i].StopTime) { Train.StationDepartureTime = Program.CurrentRoute.SecondsSinceMidnight + Program.CurrentRoute.Stations[i].StopTime; } Train.Passengers.PassengerRatio = Program.CurrentRoute.Stations[i].PassengerRatio; UpdateTrainMassFromPassengerRatio(Train); if (Train.IsPlayerTrain) { double early = 0.0; if (Program.CurrentRoute.Stations[i].ArrivalTime >= 0.0) { early = (Program.CurrentRoute.Stations[i].ArrivalTime - Train.TimetableDelta) - Train.StationArrivalTime; } string s; if (early < -1.0) { s = Translations.GetInterfaceString("message_station_arrival_late"); } else if (early > 1.0) { s = Translations.GetInterfaceString("message_station_arrival_early"); } else { s = Translations.GetInterfaceString("message_station_arrival"); } System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; TimeSpan a = TimeSpan.FromSeconds(Math.Abs(early)); string b = a.Hours.ToString("00", Culture) + ":" + a.Minutes.ToString("00", Culture) + ":" + a.Seconds.ToString("00", Culture); if (Train.StationDistanceToStopPoint < -0.1) { s += Translations.GetInterfaceString("message_delimiter") + Translations.GetInterfaceString("message_station_overrun"); } else if (Train.StationDistanceToStopPoint > 0.1) { s += Translations.GetInterfaceString("message_delimiter") + Translations.GetInterfaceString("message_station_underrun"); } double d = Math.Abs(Train.StationDistanceToStopPoint); string c = d.ToString("0.0", Culture); if (Program.CurrentRoute.Stations[i].Type == StationType.Terminal) { s += Translations.GetInterfaceString("message_delimiter") + Translations.GetInterfaceString("message_station_terminal"); } s = s.Replace("[name]", Program.CurrentRoute.Stations[i].Name); s = s.Replace("[time]", b); s = s.Replace("[difference]", c); Game.AddMessage(s, MessageDependency.StationArrival, GameMode.Normal, MessageColor.White, Program.CurrentRoute.SecondsSinceMidnight + 10.0, null); if (Program.CurrentRoute.Stations[i].Type == StationType.Normal) { s = Translations.GetInterfaceString("message_station_deadline"); Game.AddMessage(s, MessageDependency.StationDeparture, GameMode.Normal, MessageColor.White, double.PositiveInfinity, null); } Timetable.UpdateCustomTimetable(Program.CurrentRoute.Stations[i].TimetableDaytimeTexture, Program.CurrentRoute.Stations[i].TimetableNighttimeTexture); } // schedule door locks (passengers stuck between the doors) for (int j = 0; j < Train.Cars.Length; j++) { for (int k = 0; k < Train.Cars[j].Doors.Length; k++) { Train.Cars[j].Doors[k].DoorLockDuration = 0.0; if (Program.CurrentRoute.Stations[i].OpenLeftDoors & Train.Cars[j].Doors[k].Direction == -1 | Program.CurrentRoute.Stations[i].OpenRightDoors & Train.Cars[j].Doors[k].Direction == 1) { double p = 0.005 * Program.CurrentRoute.Stations[i].PassengerRatio * Program.CurrentRoute.Stations[i].PassengerRatio * Program.CurrentRoute.Stations[i].PassengerRatio * Program.CurrentRoute.Stations[i].PassengerRatio; if (Program.RandomNumberGenerator.NextDouble() < p) { /* * -- door lock at state -- * minimum: 0.2 (nearly closed) * maximum: 0.8 (nearly opened) * */ Train.Cars[j].Doors[k].DoorLockState = 0.2 + 0.6 * Program.RandomNumberGenerator.NextDouble(); /* -- waiting time -- * minimum: 2.9 s * maximum: 40.0 s * average: 7.6 s * */ p = Program.RandomNumberGenerator.NextDouble(); Train.Cars[j].Doors[k].DoorLockDuration = (50.0 - 10.0 * p) / (17.0 - 16.0 * p); } } } } } else { if (Train.SafetySystems.StationAdjust != null) { Train.SafetySystems.StationAdjust.Update(tb, tf); } } } } } else if (Train.StationState == TrainStopState.Boarding) { for (int j = 0; j < Train.Cars.Length; j++) { if (GetDoorsState(Train, j, Program.CurrentRoute.Stations[i].OpenLeftDoors, Program.CurrentRoute.Stations[i].OpenRightDoors) == (TrainDoorState.Opened | TrainDoorState.AllOpened)) { //Check whether all doors are controlled by the driver, and whether this is a non-standard station type //e.g. Change ends if (Train.Specs.DoorCloseMode != DoorMode.Manual & Program.CurrentRoute.Stations[i].Type == StationType.Normal) { //Check the interlock state for the doors switch (Train.SafetySystems.DoorInterlockState) { case DoorInterlockStates.Unlocked: AttemptToCloseDoors(Train); break; case DoorInterlockStates.Left: if (Program.CurrentRoute.Stations[i].OpenLeftDoors) { AttemptToCloseDoors(Train); } break; case DoorInterlockStates.Right: if (Program.CurrentRoute.Stations[i].OpenRightDoors) { AttemptToCloseDoors(Train); } break; case DoorInterlockStates.Locked: //All doors are currently locked, do nothing break; } if (Train.SafetySystems.DoorInterlockState != DoorInterlockStates.Locked & Train.Specs.DoorClosureAttempted) { if (Program.CurrentRoute.Stations[i].OpenLeftDoors && !Train.Cars[j].Doors[0].AnticipatedReopen && Program.RandomNumberGenerator.NextDouble() < Program.CurrentRoute.Stations[i].ReopenDoor) { Train.Cars[j].Doors[0].ReopenLimit = Program.RandomNumberGenerator.Next(1, Program.CurrentRoute.Stations[i].ReopenStationLimit); Train.Cars[j].Doors[0].ReopenCounter = 0; Train.Cars[j].Doors[0].InterferingObjectRate = Program.RandomNumberGenerator.Next(1, Program.CurrentRoute.Stations[i].MaxInterferingObjectRate) * 0.01; if (Train.Cars[j].Doors[0].InterferingObjectRate * Train.Cars[j].Doors[0].Width >= Train.Cars[j].Doors[0].MaxTolerance) { Train.Cars[j].Doors[0].AnticipatedReopen = true; } } if (Program.CurrentRoute.Stations[i].OpenRightDoors && !Train.Cars[j].Doors[1].AnticipatedReopen && Program.RandomNumberGenerator.NextDouble() < Program.CurrentRoute.Stations[i].ReopenDoor) { Train.Cars[j].Doors[1].ReopenLimit = Program.RandomNumberGenerator.Next(1, Program.CurrentRoute.Stations[i].ReopenStationLimit); Train.Cars[j].Doors[1].ReopenCounter = 0; Train.Cars[j].Doors[1].InterferingObjectRate = Program.RandomNumberGenerator.Next(1, Program.CurrentRoute.Stations[i].MaxInterferingObjectRate) * 0.01; if (Train.Cars[j].Doors[1].InterferingObjectRate * Train.Cars[j].Doors[1].Width >= Train.Cars[j].Doors[1].MaxTolerance) { Train.Cars[j].Doors[1].AnticipatedReopen = true; } } } } } } // detect departure bool left, right; if (!Program.CurrentRoute.Stations[i].OpenLeftDoors & !Program.CurrentRoute.Stations[i].OpenRightDoors) { left = true; right = true; } else { if (Program.CurrentRoute.Stations[i].OpenLeftDoors) { left = false; for (int j = 0; j < Train.Cars.Length; j++) { for (int k = 0; k < Train.Cars[j].Doors.Length; k++) { if (Train.Cars[j].Doors[k].State != 0.0) { left = true; break; } } if (left) { break; } } } else { left = false; } if (Program.CurrentRoute.Stations[i].OpenRightDoors) { right = false; for (int j = 0; j < Train.Cars.Length; j++) { for (int k = 0; k < Train.Cars[j].Doors.Length; k++) { if (Train.Cars[j].Doors[k].State != 0.0) { right = true; break; } } if (right) { break; } } } else { right = false; } } // departure sound if (!Train.StationDepartureSoundPlayed) { SoundBuffer buffer = (SoundBuffer)Program.CurrentRoute.Stations[i].DepartureSoundBuffer; if (buffer != null) { double dur = Program.Sounds.GetDuration(buffer); if (Program.CurrentRoute.SecondsSinceMidnight >= Train.StationDepartureTime - dur) { Program.Sounds.PlaySound(buffer, 1.0, 1.0, Program.CurrentRoute.Stations[i].SoundOrigin, false); Train.StationDepartureSoundPlayed = true; } } } for (int j = 0; j < Train.Cars.Length; j++) { if (Train.Cars[j].Doors[0].AnticipatedReopen && Train.Cars[j].Doors[0].State == Train.Cars[j].Doors[0].InterferingObjectRate) { if (Train.Cars[j].Doors[0].NextReopenTime == 0.0) { Train.Cars[j].Doors[0].NextReopenTime = Program.CurrentRoute.SecondsSinceMidnight + Program.CurrentRoute.Stations[i].InterferenceInDoor; } else if (Train.Cars[j].Doors[0].ReopenCounter < Train.Cars[j].Doors[0].ReopenLimit) { if (Program.CurrentRoute.SecondsSinceMidnight >= Train.Cars[j].Doors[0].NextReopenTime) { OpenTrainDoors(Train, j, true, false); } } else { Train.Cars[j].Doors[0].AnticipatedReopen = false; } } if (Train.Cars[j].Doors[1].AnticipatedReopen && Train.Cars[j].Doors[1].State == Train.Cars[j].Doors[1].InterferingObjectRate) { if (Train.Cars[j].Doors[1].NextReopenTime == 0.0) { Train.Cars[j].Doors[1].NextReopenTime = Program.CurrentRoute.SecondsSinceMidnight + Program.CurrentRoute.Stations[i].InterferenceInDoor; } else if (Train.Cars[j].Doors[1].ReopenCounter < Train.Cars[j].Doors[1].ReopenLimit) { if (Program.CurrentRoute.SecondsSinceMidnight >= Train.Cars[j].Doors[1].NextReopenTime) { OpenTrainDoors(Train, j, false, true); } } else { Train.Cars[j].Doors[1].AnticipatedReopen = false; } } } TrainDoorState doorState = GetDoorsState(Train, Program.CurrentRoute.Stations[i].OpenLeftDoors, Program.CurrentRoute.Stations[i].OpenRightDoors); if (left | right) { /* * Assume that passengers only board at a scheduled stop * If the player has opened the doors somewhere else (lineside?) * then passengers should not be boarding */ if (doorState != TrainDoorState.AllClosed && Interface.CurrentOptions.LoadingSway) { // passengers boarding for (int j = 0; j < Train.Cars.Length; j++) { if (!Train.Cars[j].EnableLoadingSway) { continue; } double r = 2.0 * Program.CurrentRoute.Stations[i].PassengerRatio * TimeElapsed; if (r >= Program.RandomNumberGenerator.NextDouble()) { int d = (int)Math.Floor(Program.RandomNumberGenerator.NextDouble() * (double)Train.Cars[j].Doors.Length); if (Train.Cars[j].Doors[d].State == 1.0) { Train.Cars[j].Specs.CurrentRollShakeDirection += (double)Train.Cars[j].Doors[d].Direction; } } } } } if (Train.Specs.DoorCloseMode == DoorMode.Manual || doorState == TrainDoorState.None || doorState == (TrainDoorState.Closed | TrainDoorState.AllClosed) || (Program.CurrentRoute.Stations[Train.Station].Type == StationType.ChangeEnds || Program.CurrentRoute.Stations[Train.Station].Type == StationType.Jump)) { if (left | right) { // departure message if (Program.CurrentRoute.SecondsSinceMidnight > Train.StationDepartureTime && (Program.CurrentRoute.Stations[i].Type != StationType.Terminal || Train != PlayerTrain)) { Train.StationState = TrainStopState.Completed; switch (Program.CurrentRoute.Stations[i].Type) { case StationType.Normal: if (!Train.IsPlayerTrain) { break; // Only trigger messages for the player train } if (!Program.CurrentRoute.Stations[i].OpenLeftDoors & !Program.CurrentRoute.Stations[i].OpenRightDoors | Train.Specs.DoorCloseMode != DoorMode.Manual) { Game.AddMessage(Translations.GetInterfaceString("message_station_depart"), MessageDependency.None, GameMode.Normal, MessageColor.White, Program.CurrentRoute.SecondsSinceMidnight + 5.0, null); } else { Game.AddMessage(Translations.GetInterfaceString("message_station_depart_closedoors"), MessageDependency.None, GameMode.Normal, MessageColor.White, Program.CurrentRoute.SecondsSinceMidnight + 5.0, null); } break; case StationType.ChangeEnds: // Change ends always jumps to the NEXT station JumpTrain(Train, i + 1); break; case StationType.Jump: // Jumps to an arbritrary station as defined in the routefile JumpTrain(Train, Program.CurrentRoute.Stations[i].JumpIndex); break; } } } else { Train.StationState = TrainStopState.Completed; if (Train.IsPlayerTrain & Program.CurrentRoute.Stations[i].Type == StationType.Normal) { Game.AddMessage(Translations.GetInterfaceString("message_station_depart"), MessageDependency.None, GameMode.Normal, MessageColor.White, Program.CurrentRoute.SecondsSinceMidnight + 5.0, null); } } } } } else { if (Train.StationState != TrainStopState.Jumping) { Train.StationState = TrainStopState.Pending; } } // automatically close doors if (Train.Specs.DoorCloseMode != DoorMode.Manual & Train.SafetySystems.DoorInterlockState != DoorInterlockStates.Locked & !Train.Specs.DoorClosureAttempted) { if (Train.Station == -1 | Train.StationState == TrainStopState.Completed) { if ((GetDoorsState(Train, true, true) & TrainDoorState.AllClosed) == 0) { CloseTrainDoors(Train, true, true); Train.Specs.DoorClosureAttempted = true; } } } }
public override void AddObjectForCustomTimeTable(AnimatedObject animatedObject) { Timetable.AddObjectForCustomTimetable(animatedObject); }
public override void UpdateCustomTimetable(Texture Daytime, Texture Nighttime) { Timetable.UpdateCustomTimetable(Daytime, Nighttime); }
/// <summary>Changes to or from fullscreen mode.</summary> internal static void ToggleFullscreen() { Program.Renderer.Screen.Fullscreen = !Program.Renderer.Screen.Fullscreen; // begin HACK // Program.Renderer.OptionFog = false; Program.Renderer.OptionLighting = false; if (Program.Renderer.Screen.Fullscreen) { IList <DisplayResolution> resolutions = DisplayDevice.Default.AvailableResolutions; for (int i = 0; i < resolutions.Count; i++) { //Test each resolution if (resolutions[i].Width == Interface.CurrentOptions.FullscreenWidth && resolutions[i].Height == Interface.CurrentOptions.FullscreenHeight && resolutions[i].BitsPerPixel == Interface.CurrentOptions.FullscreenBits) { DisplayDevice.Default.ChangeResolution(resolutions[i]); Program.currentGameWindow.Width = resolutions[i].Width; Program.currentGameWindow.Height = resolutions[i].Height; Program.Renderer.Screen.Width = Interface.CurrentOptions.FullscreenWidth; Program.Renderer.Screen.Height = Interface.CurrentOptions.FullscreenHeight; Program.currentGameWindow.WindowState = WindowState.Fullscreen; break; } } System.Threading.Thread.Sleep(20); if (Program.currentGameWindow.WindowState != WindowState.Fullscreen) { MessageBox.Show(Translations.GetInterfaceString("errors_fullscreen_switch1") + System.Environment.NewLine + Translations.GetInterfaceString("errors_fullscreen_switch2"), Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand); Program.Renderer.Screen.Fullscreen = false; } } else { DisplayDevice.Default.RestoreResolution(); Program.currentGameWindow.WindowState = WindowState.Normal; Program.currentGameWindow.Width = Interface.CurrentOptions.WindowWidth; Program.currentGameWindow.Height = Interface.CurrentOptions.WindowHeight; Program.Renderer.Screen.Width = Interface.CurrentOptions.WindowWidth; Program.Renderer.Screen.Height = Interface.CurrentOptions.WindowHeight; } Program.Renderer.Lighting.Initialize(); Program.Renderer.UpdateViewport(ViewportChangeMode.NoChange); Program.Renderer.MotionBlur.Initialize(Interface.CurrentOptions.MotionBlur); lock (Illustrations.Locker) { Timetable.CreateTimetable(); } Timetable.UpdateCustomTimetable(null, null); World.InitializeCameraRestriction(); if (Program.Renderer.OptionBackFaceCulling) { GL.Enable(EnableCap.CullFace); } else { GL.Disable(EnableCap.CullFace); } // end HACK // //Reset the camera when switching between fullscreen and windowed mode //Otherwise, if the aspect ratio changes distortion will occur until the view is changed or the camera reset if (Program.Renderer.Camera.CurrentMode == CameraViewMode.Interior | Program.Renderer.Camera.CurrentMode == CameraViewMode.InteriorLookAhead) { Program.Renderer.Camera.Alignment.Position = OpenBveApi.Math.Vector3.Zero; } Program.Renderer.Camera.Alignment.Yaw = 0.0; Program.Renderer.Camera.Alignment.Pitch = 0.0; Program.Renderer.Camera.Alignment.Roll = 0.0; }
/// <summary>This method is called once the route and train data have been preprocessed, in order to physically setup the simulation</summary> private void SetupSimulation() { if (Loading.Cancel) { Close(); } lock (Illustrations.Locker) { Timetable.CreateTimetable(); } //Check if any critical errors have occured during the route or train loading for (int i = 0; i < Interface.MessageCount; i++) { if (Interface.LogMessages[i].Type == MessageType.Critical) { MessageBox.Show("A critical error has occured:\n\n" + Interface.LogMessages[i].Text + "\n\nPlease inspect the error log file for further information.", "Load", MessageBoxButtons.OK, MessageBoxIcon.Hand); Close(); } } Renderer.InitializeLighting(); Game.LogRouteName = System.IO.Path.GetFileName(MainLoop.currentResult.RouteFile); Game.LogTrainName = System.IO.Path.GetFileName(MainLoop.currentResult.TrainFolder); Game.LogDateTime = DateTime.Now; if (Interface.CurrentOptions.LoadInAdvance) { Textures.LoadAllTextures(); } else { Textures.UnloadAllTextures(); } // camera ObjectManager.InitializeVisibility(); World.CurrentDriverBody = new World.DriverBody(); World.CameraTrackFollower.Update(0.0, true, false); World.CameraTrackFollower.Update(-0.1, true, false); World.CameraTrackFollower.Update(0.1, true, false); World.CameraTrackFollower.TriggerType = TrackManager.EventTriggerType.Camera; // starting time and track position Game.SecondsSinceMidnight = 0.0; Game.StartupTime = 0.0; int PlayerFirstStationIndex = -1; double PlayerFirstStationPosition; int os = -1; bool f = false; for (int i = 0; i < Game.Stations.Length; i++) { if (!String.IsNullOrEmpty(Game.InitialStationName)) { if (Game.InitialStationName.ToLowerInvariant() == Game.Stations[i].Name.ToLowerInvariant()) { PlayerFirstStationIndex = i; } } if (Game.Stations[i].StopMode == StationStopMode.AllStop | Game.Stations[i].StopMode == StationStopMode.PlayerStop & Game.Stations[i].Stops.Length != 0) { if (f == false) { os = i; f = true; } } } if (PlayerFirstStationIndex == -1) { PlayerFirstStationIndex = os; } { int s = Game.Stations[PlayerFirstStationIndex].GetStopIndex(TrainManager.PlayerTrain.Cars.Length); if (s >= 0) { PlayerFirstStationPosition = Game.Stations[PlayerFirstStationIndex].Stops[s].TrackPosition; double TrainLength = 0.0; for (int c = 0; c < TrainManager.PlayerTrain.Cars.Length; c++) { TrainLength += TrainManager.PlayerTrain.Cars[c].Length; } for (int j = 0; j < Game.BufferTrackPositions.Length; j++) { if (PlayerFirstStationPosition > Game.BufferTrackPositions[j] && PlayerFirstStationPosition - TrainLength < Game.BufferTrackPositions[j]) { /* * HACK: The initial start position for the player train is stuck on a set of buffers * This means we have to make some one the fly adjustments to the first station stop position */ //Set the start position to be the buffer position plus the train length plus 1m PlayerFirstStationPosition = Game.BufferTrackPositions[j] + TrainLength + 1; //Update the station stop location if (s >= 0) { Game.Stations[PlayerFirstStationIndex].Stops[s].TrackPosition = PlayerFirstStationPosition; } else { Game.Stations[PlayerFirstStationIndex].DefaultTrackPosition = PlayerFirstStationPosition; } break; } } } else { PlayerFirstStationPosition = Game.Stations[PlayerFirstStationIndex].DefaultTrackPosition; } if (Game.InitialStationTime != -1) { Game.SecondsSinceMidnight = Game.InitialStationTime; Game.StartupTime = Game.InitialStationTime; } else { if (Game.Stations[PlayerFirstStationIndex].ArrivalTime < 0.0) { if (Game.Stations[PlayerFirstStationIndex].DepartureTime < 0.0) { Game.SecondsSinceMidnight = 0.0; Game.StartupTime = 0.0; } else { Game.SecondsSinceMidnight = Game.Stations[PlayerFirstStationIndex].DepartureTime - Game.Stations[PlayerFirstStationIndex].StopTime; Game.StartupTime = Game.Stations[PlayerFirstStationIndex].DepartureTime - Game.Stations[PlayerFirstStationIndex].StopTime; } } else { Game.SecondsSinceMidnight = Game.Stations[PlayerFirstStationIndex].ArrivalTime; Game.StartupTime = Game.Stations[PlayerFirstStationIndex].ArrivalTime; } } } int OtherFirstStationIndex = -1; double OtherFirstStationPosition = 0.0; double OtherFirstStationTime = 0.0; for (int i = 0; i < Game.Stations.Length; i++) { if (Game.Stations[i].StopMode == StationStopMode.AllStop | Game.Stations[i].StopMode == StationStopMode.PlayerPass & Game.Stations[i].Stops.Length != 0) { OtherFirstStationIndex = i; int s = Game.Stations[i].GetStopIndex(TrainManager.PlayerTrain.Cars.Length); if (s >= 0) { OtherFirstStationPosition = Game.Stations[i].Stops[s].TrackPosition; } else { OtherFirstStationPosition = Game.Stations[i].DefaultTrackPosition; } if (Game.Stations[i].ArrivalTime < 0.0) { if (Game.Stations[i].DepartureTime < 0.0) { OtherFirstStationTime = 0.0; } else { OtherFirstStationTime = Game.Stations[i].DepartureTime - Game.Stations[i].StopTime; } } else { OtherFirstStationTime = Game.Stations[i].ArrivalTime; } break; } } if (Game.PrecedingTrainTimeDeltas.Length != 0) { OtherFirstStationTime -= Game.PrecedingTrainTimeDeltas[Game.PrecedingTrainTimeDeltas.Length - 1]; if (OtherFirstStationTime < Game.SecondsSinceMidnight) { Game.SecondsSinceMidnight = OtherFirstStationTime; } } // initialize trains for (int i = 0; i < TrainManager.Trains.Length; i++) { TrainManager.Trains[i].Initialize(); int s = TrainManager.Trains[i] == TrainManager.PlayerTrain ? PlayerFirstStationIndex : OtherFirstStationIndex; if (s >= 0) { if (Game.Stations[s].OpenLeftDoors) { for (int j = 0; j < TrainManager.Trains[i].Cars.Length; j++) { TrainManager.Trains[i].Cars[j].Doors[0].AnticipatedOpen = true; } } if (Game.Stations[s].OpenRightDoors) { for (int j = 0; j < TrainManager.Trains[i].Cars.Length; j++) { TrainManager.Trains[i].Cars[j].Doors[1].AnticipatedOpen = true; } } } if (Game.Sections.Length != 0) { Game.Sections[0].Enter(TrainManager.Trains[i]); } for (int j = 0; j < TrainManager.Trains[i].Cars.Length; j++) { double length = TrainManager.Trains[i].Cars[0].Length; TrainManager.Trains[i].Cars[j].Move(-length); TrainManager.Trains[i].Cars[j].Move(length); } } foreach (var Train in TrainManager.TFOs) { Train.Initialize(); foreach (var Car in Train.Cars) { double length = Train.Cars[0].Length; Car.Move(-length); Car.Move(length); } } // score Game.CurrentScore.ArrivalStation = PlayerFirstStationIndex + 1; Game.CurrentScore.DepartureStation = PlayerFirstStationIndex; Game.CurrentScore.Maximum = 0; for (int i = 0; i < Game.Stations.Length; i++) { if (i != PlayerFirstStationIndex & Game.PlayerStopsAtStation(i)) { if (i == 0 || Game.Stations[i - 1].Type != StationType.ChangeEnds) { Game.CurrentScore.Maximum += Game.ScoreValueStationArrival; } } } if (Game.CurrentScore.Maximum <= 0) { Game.CurrentScore.Maximum = Game.ScoreValueStationArrival; } // signals if (Game.Sections.Length > 0) { Game.UpdateSection(Game.Sections.Length - 1); } // move train in position for (int i = 0; i < TrainManager.Trains.Length; i++) { double p; if (TrainManager.Trains[i] == TrainManager.PlayerTrain) { p = PlayerFirstStationPosition; } else if (TrainManager.Trains[i].State == TrainState.Bogus) { p = Game.BogusPretrainInstructions[0].TrackPosition; TrainManager.Trains[i].AI = new Game.BogusPretrainAI(TrainManager.Trains[i]); } else { p = OtherFirstStationPosition; } for (int j = 0; j < TrainManager.Trains[i].Cars.Length; j++) { TrainManager.Trains[i].Cars[j].Move(p); } } // timetable if (Timetable.DefaultTimetableDescription.Length == 0) { Timetable.DefaultTimetableDescription = Game.LogTrainName; } // initialize camera if (World.CameraRestriction == Camera.RestrictionMode.NotAvailable) { World.CameraMode = CameraViewMode.InteriorLookAhead; } //Place the initial camera in the driver car TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].UpdateCamera(); World.CameraTrackFollower.Update(-1.0, true, false); ObjectManager.UpdateVisibility(World.CameraTrackFollower.TrackPosition + World.CameraCurrentAlignment.Position.Z); World.CameraSavedExterior = new World.CameraAlignment(new OpenBveApi.Math.Vector3(-2.5, 1.5, -15.0), 0.3, -0.2, 0.0, PlayerFirstStationPosition, 1.0); World.CameraSavedTrack = new World.CameraAlignment(new OpenBveApi.Math.Vector3(-3.0, 2.5, 0.0), 0.3, 0.0, 0.0, TrainManager.PlayerTrain.Cars[0].FrontAxle.Follower.TrackPosition - 10.0, 1.0); // signalling sections for (int i = 0; i < TrainManager.Trains.Length; i++) { int s = TrainManager.Trains[i].CurrentSectionIndex; Game.Sections[s].Enter(TrainManager.Trains[i]); } if (Game.Sections.Length > 0) { Game.UpdateSection(Game.Sections.Length - 1); } // fast-forward until start time { Game.MinimalisticSimulation = true; const double w = 0.25; double u = Game.StartupTime - Game.SecondsSinceMidnight; if (u > 0) { while (true) { double v = u < w ? u : w; u -= v; Game.SecondsSinceMidnight += v; TrainManager.UpdateTrains(v); if (u <= 0.0) { break; } TotalTimeElapsedForSectionUpdate += v; if (TotalTimeElapsedForSectionUpdate >= 1.0) { if (Game.Sections.Length > 0) { Game.UpdateSection(Game.Sections.Length - 1); } TotalTimeElapsedForSectionUpdate = 0.0; } } } Game.MinimalisticSimulation = false; } // animated objects ObjectManager.UpdateAnimatedWorldObjects(0.0, true); TrainManager.UpdateTrainObjects(0.0, true); //HACK: This function calls a single update on all objects attached to the player's train // but ignores any specified damping so that all needles etc. are in the correct place // for the first frame, rather than spinning wildly to get to the starting point. TrainManager.PlayerTrain.UpdateCabObjects(); // timetable if (TrainManager.PlayerTrain.Station >= 0) { Timetable.UpdateCustomTimetable(Game.Stations[TrainManager.PlayerTrain.Station].TimetableDaytimeTexture, Game.Stations[TrainManager.PlayerTrain.Station].TimetableNighttimeTexture); if (Timetable.CustomObjectsUsed != 0 & Timetable.CustomTimetableAvailable && Interface.CurrentOptions.TimeTableStyle != Interface.TimeTableMode.AutoGenerated && Interface.CurrentOptions.TimeTableStyle != Interface.TimeTableMode.None) { Timetable.CurrentTimetable = Timetable.TimetableState.Custom; } } //Create AI driver for the player train if specified via the commmand line if (Game.InitialAIDriver == true) { TrainManager.PlayerTrain.AI = new Game.SimpleHumanDriverAI(TrainManager.PlayerTrain); if (TrainManager.PlayerTrain.Plugin != null && !TrainManager.PlayerTrain.Plugin.SupportsAI) { Game.AddMessage(Translations.GetInterfaceString("notification_aiunable"), MessageManager.MessageDependency.None, Interface.GameMode.Expert, OpenBveApi.Colors.MessageColor.White, Game.SecondsSinceMidnight + 10.0, null); } } // warnings / errors if (Interface.MessageCount != 0) { int filesNotFound = 0; int errors = 0; int warnings = 0; for (int i = 0; i < Interface.MessageCount; i++) { if (Interface.LogMessages[i].FileNotFound) { filesNotFound++; } else if (Interface.LogMessages[i].Type == MessageType.Error) { errors++; } else if (Interface.LogMessages[i].Type == MessageType.Warning) { warnings++; } } string NotFound = null; string Messages; if (filesNotFound != 0) { NotFound = filesNotFound.ToString() + " file(s) not found"; Game.AddMessage(NotFound, MessageManager.MessageDependency.None, Interface.GameMode.Expert, MessageColor.Magenta, Game.SecondsSinceMidnight + 10.0, null); } if (errors != 0 & warnings != 0) { Messages = errors.ToString() + " error(s), " + warnings.ToString() + " warning(s)"; Game.AddMessage(Messages, MessageManager.MessageDependency.None, Interface.GameMode.Expert, MessageColor.Magenta, Game.SecondsSinceMidnight + 10.0, null); } else if (errors != 0) { Messages = errors.ToString() + " error(s)"; Game.AddMessage(Messages, MessageManager.MessageDependency.None, Interface.GameMode.Expert, MessageColor.Magenta, Game.SecondsSinceMidnight + 10.0, null); } else { Messages = warnings.ToString() + " warning(s)"; Game.AddMessage(Messages, MessageManager.MessageDependency.None, Interface.GameMode.Expert, MessageColor.Magenta, Game.SecondsSinceMidnight + 10.0, null); } Game.RouteInformation.FilesNotFound = NotFound; Game.RouteInformation.ErrorsAndWarnings = Messages; //Print the plugin error encountered (If any) for 10s //This must be done after the simulation has init, as otherwise the timeout doesn't work if (Loading.PluginError != null) { Game.AddMessage(Loading.PluginError, MessageManager.MessageDependency.None, Interface.GameMode.Expert, OpenBveApi.Colors.MessageColor.Red, Game.SecondsSinceMidnight + 5.0, null); Game.AddMessage(Translations.GetInterfaceString("errors_plugin_failure2"), MessageManager.MessageDependency.None, Interface.GameMode.Expert, OpenBveApi.Colors.MessageColor.Red, Game.SecondsSinceMidnight + 5.0, null); } } loadComplete = true; RenderRealTimeElapsed = 0.0; RenderTimeElapsed = 0.0; World.InitializeCameraRestriction(); Loading.SimulationSetup = true; switch (Game.InitialViewpoint) { case 1: //Switch camera to exterior MainLoop.SaveCameraSettings(); World.CameraMode = CameraViewMode.Exterior; MainLoop.RestoreCameraSettings(); for (int j = 0; j < TrainManager.PlayerTrain.Cars.Length; j++) { TrainManager.PlayerTrain.Cars[j].ChangeCarSection(TrainManager.CarSectionType.Exterior); } //Make bogies visible for (int j = 0; j < TrainManager.PlayerTrain.Cars.Length; j++) { TrainManager.PlayerTrain.Cars[j].FrontBogie.ChangeSection(0); TrainManager.PlayerTrain.Cars[j].RearBogie.ChangeSection(0); } World.CameraAlignmentDirection = new World.CameraAlignment(); World.CameraAlignmentSpeed = new World.CameraAlignment(); Renderer.UpdateViewport(Renderer.ViewPortChangeMode.NoChange); World.UpdateAbsoluteCamera(0.0); World.UpdateViewingDistances(); break; case 2: //Switch camera to track MainLoop.SaveCameraSettings(); World.CameraMode = CameraViewMode.Track; MainLoop.RestoreCameraSettings(); for (int j = 0; j < TrainManager.PlayerTrain.Cars.Length; j++) { TrainManager.PlayerTrain.Cars[j].ChangeCarSection(TrainManager.CarSectionType.Exterior); } for (int j = 0; j < TrainManager.PlayerTrain.Cars.Length; j++) { TrainManager.PlayerTrain.Cars[j].FrontBogie.ChangeSection(0); TrainManager.PlayerTrain.Cars[j].RearBogie.ChangeSection(0); } World.CameraAlignmentDirection = new World.CameraAlignment(); World.CameraAlignmentSpeed = new World.CameraAlignment(); Renderer.UpdateViewport(Renderer.ViewPortChangeMode.NoChange); World.UpdateAbsoluteCamera(0.0); World.UpdateViewingDistances(); break; case 3: //Switch camera to flyby MainLoop.SaveCameraSettings(); World.CameraMode = CameraViewMode.FlyBy; MainLoop.RestoreCameraSettings(); for (int j = 0; j < TrainManager.PlayerTrain.Cars.Length; j++) { TrainManager.PlayerTrain.Cars[j].ChangeCarSection(TrainManager.CarSectionType.Exterior); } for (int j = 0; j < TrainManager.PlayerTrain.Cars.Length; j++) { TrainManager.PlayerTrain.Cars[j].FrontBogie.ChangeSection(0); TrainManager.PlayerTrain.Cars[j].RearBogie.ChangeSection(0); } World.CameraAlignmentDirection = new World.CameraAlignment(); World.CameraAlignmentSpeed = new World.CameraAlignment(); Renderer.UpdateViewport(Renderer.ViewPortChangeMode.NoChange); World.UpdateAbsoluteCamera(0.0); World.UpdateViewingDistances(); break; case 4: //Switch camera to flyby MainLoop.SaveCameraSettings(); World.CameraMode = CameraViewMode.FlyByZooming; MainLoop.RestoreCameraSettings(); for (int j = 0; j < TrainManager.PlayerTrain.Cars.Length; j++) { TrainManager.PlayerTrain.Cars[j].ChangeCarSection(TrainManager.CarSectionType.Exterior); } for (int j = 0; j < TrainManager.PlayerTrain.Cars.Length; j++) { TrainManager.PlayerTrain.Cars[j].FrontBogie.ChangeSection(0); TrainManager.PlayerTrain.Cars[j].RearBogie.ChangeSection(0); } World.CameraAlignmentDirection = new World.CameraAlignment(); World.CameraAlignmentSpeed = new World.CameraAlignment(); Renderer.UpdateViewport(Renderer.ViewPortChangeMode.NoChange); World.UpdateAbsoluteCamera(0.0); World.UpdateViewingDistances(); break; } }
// parse animated object config /// <summary>Loads a collection of animated objects from a file.</summary> /// <param name="FileName">The text file to load the animated object from. Must be an absolute file name.</param> /// <param name="Encoding">The encoding the file is saved in. If the file uses a byte order mark, the encoding indicated by the byte order mark is used and the Encoding parameter is ignored.</param> /// <param name="LoadMode">The texture load mode.</param> /// <returns>The collection of animated objects.</returns> internal static ObjectManager.AnimatedObjectCollection ReadObject(string FileName, System.Text.Encoding Encoding, ObjectManager.ObjectLoadMode LoadMode) { System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; ObjectManager.AnimatedObjectCollection Result = new ObjectManager.AnimatedObjectCollection(); Result.Objects = new ObjectManager.AnimatedObject[4]; int ObjectCount = 0; // load file string[] Lines = System.IO.File.ReadAllLines(FileName, Encoding); bool rpnUsed = false; for (int i = 0; i < Lines.Length; i++) { int j = Lines[i].IndexOf(';'); if (j >= 0) { Lines[i] = Lines[i].Substring(0, j).Trim(); } else { Lines[i] = Lines[i].Trim(); } if (Lines[i].IndexOf("functionrpn", StringComparison.OrdinalIgnoreCase) >= 0) { rpnUsed = true; } } if (rpnUsed) { Interface.AddMessage(Interface.MessageType.Error, false, "An animated object file contains RPN functions. These were never meant to be used directly, only for debugging. They won't be supported indefinately. Please get rid of them in file " + FileName); } for (int i = 0; i < Lines.Length; i++) { if (Lines[i].Length != 0) { switch (Lines[i].ToLowerInvariant()) { case "[include]": { i++; World.Vector3D position = new World.Vector3D(0.0, 0.0, 0.0); ObjectManager.UnifiedObject[] obj = new OpenBve.ObjectManager.UnifiedObject[4]; int objCount = 0; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { if (Lines[i].Length != 0) { int j = Lines[i].IndexOf("=", StringComparison.Ordinal); if (j > 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); switch (a.ToLowerInvariant()) { case "position": { string[] s = b.Split(','); if (s.Length == 3) { double x, y, z; if (!double.TryParse(s[0], System.Globalization.NumberStyles.Float, Culture, out x)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[1], System.Globalization.NumberStyles.Float, Culture, out y)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[2], System.Globalization.NumberStyles.Float, Culture, out z)) { Interface.AddMessage(Interface.MessageType.Error, false, "Z is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { position = new World.Vector3D(x, y, z); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 3 arguments are expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; default: Interface.AddMessage(Interface.MessageType.Error, false, "The attribute " + a + " is not supported at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } else { string Folder = System.IO.Path.GetDirectoryName(FileName); if (Interface.ContainsInvalidPathChars(Lines[i])) { Interface.AddMessage(Interface.MessageType.Error, false, Lines[i] + " contains illegal characters at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { string file = OpenBveApi.Path.CombineFile(Folder, Lines[i]); if (System.IO.File.Exists(file)) { if (obj.Length == objCount) { Array.Resize <ObjectManager.UnifiedObject>(ref obj, obj.Length << 1); } obj[objCount] = ObjectManager.LoadObject(file, Encoding, LoadMode, false, false, false); objCount++; } else { Interface.AddMessage(Interface.MessageType.Error, true, "File " + file + " not found at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } } } i++; } i--; for (int j = 0; j < objCount; j++) { if (obj[j] != null) { if (obj[j] is ObjectManager.StaticObject) { ObjectManager.StaticObject s = (ObjectManager.StaticObject)obj[j]; s.Dynamic = true; if (ObjectCount >= Result.Objects.Length) { Array.Resize <ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length << 1); } ObjectManager.AnimatedObject a = new ObjectManager.AnimatedObject(); ObjectManager.AnimatedObjectState aos = new ObjectManager.AnimatedObjectState(); aos.Object = s; aos.Position = position; a.States = new ObjectManager.AnimatedObjectState[] { aos }; Result.Objects[ObjectCount] = a; ObjectCount++; } else if (obj[j] is ObjectManager.AnimatedObjectCollection) { ObjectManager.AnimatedObjectCollection a = (ObjectManager.AnimatedObjectCollection)obj[j]; for (int k = 0; k < a.Objects.Length; k++) { if (ObjectCount >= Result.Objects.Length) { Array.Resize <ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length << 1); } for (int h = 0; h < a.Objects[k].States.Length; h++) { a.Objects[k].States[h].Position.X += position.X; a.Objects[k].States[h].Position.Y += position.Y; a.Objects[k].States[h].Position.Z += position.Z; } Result.Objects[ObjectCount] = a.Objects[k]; ObjectCount++; } } } } } break; case "[object]": { i++; if (Result.Objects.Length == ObjectCount) { Array.Resize <ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length << 1); } Result.Objects[ObjectCount] = new ObjectManager.AnimatedObject(); Result.Objects[ObjectCount].States = new ObjectManager.AnimatedObjectState[] { }; Result.Objects[ObjectCount].CurrentState = -1; Result.Objects[ObjectCount].TranslateXDirection = new World.Vector3D(1.0, 0.0, 0.0); Result.Objects[ObjectCount].TranslateYDirection = new World.Vector3D(0.0, 1.0, 0.0); Result.Objects[ObjectCount].TranslateZDirection = new World.Vector3D(0.0, 0.0, 1.0); Result.Objects[ObjectCount].RotateXDirection = new World.Vector3D(1.0, 0.0, 0.0); Result.Objects[ObjectCount].RotateYDirection = new World.Vector3D(0.0, 1.0, 0.0); Result.Objects[ObjectCount].RotateZDirection = new World.Vector3D(0.0, 0.0, 1.0); Result.Objects[ObjectCount].TextureShiftXDirection = new World.Vector2D(1.0, 0.0); Result.Objects[ObjectCount].TextureShiftYDirection = new World.Vector2D(0.0, 1.0); Result.Objects[ObjectCount].RefreshRate = 0.0; Result.Objects[ObjectCount].ObjectIndex = -1; World.Vector3D Position = new World.Vector3D(0.0, 0.0, 0.0); bool timetableUsed = false; string[] StateFiles = null; string StateFunctionRpn = null; int StateFunctionLine = -1; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { if (Lines[i].Length != 0) { int j = Lines[i].IndexOf("=", StringComparison.Ordinal); if (j > 0) { string a = Lines[i].Substring(0, j).TrimEnd(); string b = Lines[i].Substring(j + 1).TrimStart(); switch (a.ToLowerInvariant()) { case "position": { string[] s = b.Split(','); if (s.Length == 3) { double x, y, z; if (!double.TryParse(s[0], System.Globalization.NumberStyles.Float, Culture, out x)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[1], System.Globalization.NumberStyles.Float, Culture, out y)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[2], System.Globalization.NumberStyles.Float, Culture, out z)) { Interface.AddMessage(Interface.MessageType.Error, false, "Z is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { Position = new World.Vector3D(x, y, z); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 3 arguments are expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "states": { string[] s = b.Split(','); if (s.Length >= 1) { string Folder = System.IO.Path.GetDirectoryName(FileName); StateFiles = new string[s.Length]; for (int k = 0; k < s.Length; k++) { s[k] = s[k].Trim(); if (s[k].Length == 0) { Interface.AddMessage(Interface.MessageType.Error, false, "File" + k.ToString(Culture) + " is an empty string - did you mean something else? - in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); StateFiles[k] = null; } else if (Interface.ContainsInvalidPathChars(s[k])) { Interface.AddMessage(Interface.MessageType.Error, false, "File" + k.ToString(Culture) + " contains illegal characters in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); StateFiles[k] = null; } else { StateFiles[k] = OpenBveApi.Path.CombineFile(Folder, s[k]); if (!System.IO.File.Exists(StateFiles[k])) { Interface.AddMessage(Interface.MessageType.Error, true, "File " + StateFiles[k] + " not found in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); StateFiles[k] = null; } } } } else { Interface.AddMessage(Interface.MessageType.Error, false, "At least one argument is expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); return(null); } } break; case "statefunction": try { StateFunctionLine = i; StateFunctionRpn = FunctionScripts.GetPostfixNotationFromInfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "statefunctionrpn": { StateFunctionLine = i; StateFunctionRpn = b; } break; case "translatexdirection": case "translateydirection": case "translatezdirection": { string[] s = b.Split(','); if (s.Length == 3) { double x, y, z; if (!double.TryParse(s[0], System.Globalization.NumberStyles.Float, Culture, out x)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[1], System.Globalization.NumberStyles.Float, Culture, out y)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[2], System.Globalization.NumberStyles.Float, Culture, out z)) { Interface.AddMessage(Interface.MessageType.Error, false, "Z is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { switch (a.ToLowerInvariant()) { case "translatexdirection": Result.Objects[ObjectCount].TranslateXDirection = new World.Vector3D(x, y, z); break; case "translateydirection": Result.Objects[ObjectCount].TranslateYDirection = new World.Vector3D(x, y, z); break; case "translatezdirection": Result.Objects[ObjectCount].TranslateZDirection = new World.Vector3D(x, y, z); break; } } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 3 arguments are expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "translatexfunction": try { Result.Objects[ObjectCount].TranslateXFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "translateyfunction": try { Result.Objects[ObjectCount].TranslateYFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "translatezfunction": try { Result.Objects[ObjectCount].TranslateZFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "translatexfunctionrpn": try { Result.Objects[ObjectCount].TranslateXFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "translateyfunctionrpn": try { Result.Objects[ObjectCount].TranslateYFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "translatezfunctionrpn": try { Result.Objects[ObjectCount].TranslateZFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "rotatexdirection": case "rotateydirection": case "rotatezdirection": { string[] s = b.Split(','); if (s.Length == 3) { double x, y, z; if (!double.TryParse(s[0], System.Globalization.NumberStyles.Float, Culture, out x)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[1], System.Globalization.NumberStyles.Float, Culture, out y)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[2], System.Globalization.NumberStyles.Float, Culture, out z)) { Interface.AddMessage(Interface.MessageType.Error, false, "Z is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (x == 0.0 & y == 0.0 & z == 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "The direction indicated by X, Y and Z is expected to be non-zero in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { switch (a.ToLowerInvariant()) { case "rotatexdirection": Result.Objects[ObjectCount].RotateXDirection = new World.Vector3D(x, y, z); break; case "rotateydirection": Result.Objects[ObjectCount].RotateYDirection = new World.Vector3D(x, y, z); break; case "rotatezdirection": Result.Objects[ObjectCount].RotateZDirection = new World.Vector3D(x, y, z); break; } } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 3 arguments are expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "rotatexfunction": try { Result.Objects[ObjectCount].RotateXFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "rotateyfunction": try { Result.Objects[ObjectCount].RotateYFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "rotatezfunction": try { Result.Objects[ObjectCount].RotateZFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "rotatexfunctionrpn": try { Result.Objects[ObjectCount].RotateXFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "rotateyfunctionrpn": try { Result.Objects[ObjectCount].RotateYFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "rotatezfunctionrpn": try { Result.Objects[ObjectCount].RotateZFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "rotatexdamping": case "rotateydamping": case "rotatezdamping": { string[] s = b.Split(','); if (s.Length == 2) { double nf, dr; if (!double.TryParse(s[0], System.Globalization.NumberStyles.Float, Culture, out nf)) { Interface.AddMessage(Interface.MessageType.Error, false, "NaturalFrequency is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[1], System.Globalization.NumberStyles.Float, Culture, out dr)) { Interface.AddMessage(Interface.MessageType.Error, false, "DampingRatio is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (nf <= 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "NaturalFrequency is expected to be positive in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (dr <= 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "DampingRatio is expected to be positive in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { switch (a.ToLowerInvariant()) { case "rotatexdamping": Result.Objects[ObjectCount].RotateXDamping = new ObjectManager.Damping(nf, dr); break; case "rotateydamping": Result.Objects[ObjectCount].RotateYDamping = new ObjectManager.Damping(nf, dr); break; case "rotatezdamping": Result.Objects[ObjectCount].RotateZDamping = new ObjectManager.Damping(nf, dr); break; } } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 2 arguments are expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "textureshiftxdirection": case "textureshiftydirection": { string[] s = b.Split(','); if (s.Length == 2) { double x, y; if (!double.TryParse(s[0], System.Globalization.NumberStyles.Float, Culture, out x)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[1], System.Globalization.NumberStyles.Float, Culture, out y)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { switch (a.ToLowerInvariant()) { case "textureshiftxdirection": Result.Objects[ObjectCount].TextureShiftXDirection = new World.Vector2D(x, y); break; case "textureshiftydirection": Result.Objects[ObjectCount].TextureShiftYDirection = new World.Vector2D(x, y); break; } } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 2 arguments are expected in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "textureshiftxfunction": try { Result.Objects[ObjectCount].TextureShiftXFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "textureshiftyfunction": try { Result.Objects[ObjectCount].TextureShiftYFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "textureshiftxfunctionrpn": try { Result.Objects[ObjectCount].TextureShiftXFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "textureshiftyfunctionrpn": try { Result.Objects[ObjectCount].TextureShiftYFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(b); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "textureoverride": switch (b.ToLowerInvariant()) { case "none": break; case "timetable": if (!timetableUsed) { Timetable.AddObjectForCustomTimetable(Result.Objects[ObjectCount]); timetableUsed = true; } break; default: Interface.AddMessage(Interface.MessageType.Error, false, "Unrecognized value in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } break; case "refreshrate": { double r; if (!double.TryParse(b, System.Globalization.NumberStyles.Float, Culture, out r)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (r < 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is expected to be non-negative in " + a + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { Result.Objects[ObjectCount].RefreshRate = r; } } break; default: Interface.AddMessage(Interface.MessageType.Error, false, "The attribute " + a + " is not supported at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); return(null); } } i++; } i--; if (StateFiles != null) { // create the object if (timetableUsed) { if (StateFunctionRpn != null) { StateFunctionRpn = "timetable 0 == " + StateFunctionRpn + " -1 ?"; } else { StateFunctionRpn = "timetable"; } } if (StateFunctionRpn != null) { try { Result.Objects[ObjectCount].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(StateFunctionRpn); } catch (Exception ex) { Interface.AddMessage(Interface.MessageType.Error, false, ex.Message + " in StateFunction at line " + (StateFunctionLine + 1).ToString(Culture) + " in file " + FileName); } } Result.Objects[ObjectCount].States = new ObjectManager.AnimatedObjectState[StateFiles.Length]; bool ForceTextureRepeatX = Result.Objects[ObjectCount].TextureShiftXFunction != null & Result.Objects[ObjectCount].TextureShiftXDirection.X != 0.0 | Result.Objects[ObjectCount].TextureShiftYFunction != null & Result.Objects[ObjectCount].TextureShiftYDirection.Y != 0.0; bool ForceTextureRepeatY = Result.Objects[ObjectCount].TextureShiftXFunction != null & Result.Objects[ObjectCount].TextureShiftXDirection.X != 0.0 | Result.Objects[ObjectCount].TextureShiftYFunction != null & Result.Objects[ObjectCount].TextureShiftYDirection.Y != 0.0; for (int k = 0; k < StateFiles.Length; k++) { Result.Objects[ObjectCount].States[k].Position = new World.Vector3D(0.0, 0.0, 0.0); if (StateFiles[k] != null) { Result.Objects[ObjectCount].States[k].Object = ObjectManager.LoadStaticObject(StateFiles[k], Encoding, LoadMode, false, ForceTextureRepeatX, ForceTextureRepeatY); if (Result.Objects[ObjectCount].States[k].Object != null) { Result.Objects[ObjectCount].States[k].Object.Dynamic = true; } } else { Result.Objects[ObjectCount].States[k].Object = null; } for (int j = 0; j < Result.Objects[ObjectCount].States.Length; j++) { Result.Objects[ObjectCount].States[j].Position = Position; } } } else { Result.Objects[ObjectCount].States = new ObjectManager.AnimatedObjectState[] { }; } ObjectCount++; } break; default: Interface.AddMessage(Interface.MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); return(null); } } } Array.Resize <ObjectManager.AnimatedObject>(ref Result.Objects, ObjectCount); return(Result); }
// parse animated object config /// <summary>Loads a collection of animated objects from a file.</summary> /// <param name="FileName">The text file to load the animated object from. Must be an absolute file name.</param> /// <param name="Encoding">The encoding the file is saved in. If the file uses a byte order mark, the encoding indicated by the byte order mark is used and the Encoding parameter is ignored.</param> /// <param name="LoadMode">The texture load mode.</param> /// <returns>The collection of animated objects.</returns> internal static ObjectManager.AnimatedObjectCollection ReadObject(string FileName, System.Text.Encoding Encoding, ObjectManager.ObjectLoadMode LoadMode) { ObjectManager.AnimatedObjectCollection Result = new ObjectManager.AnimatedObjectCollection(); Result.Objects = new ObjectManager.AnimatedObject[4]; int ObjectCount = 0; // load file string[] Lines = System.IO.File.ReadAllLines(FileName, Encoding); bool rpnUsed = false; for (int i = 0; i < Lines.Length; i++) { int j = Lines[i].IndexOf(';'); // cut comments out Lines[i] = j >= 0 ? Lines[i].Substring(0, j).Trim() : Lines[i].Trim(); rpnUsed = Lines[i].IndexOf("functionrpn", StringComparison.OrdinalIgnoreCase) >= 0; } if (rpnUsed) { Debug.AddMessage(Debug.MessageType.Error, false, "An animated object file contains RPN functions. These were never meant to be used directly, only for debugging. They won't be supported indefinately. Please get rid of them in file " + FileName); } for (int i = 0; i < Lines.Length; i++) { if (Lines[i].Length != 0) { switch (Lines[i].ToLowerInvariant()) { case "[include]": { i++; Vector3D position = new Vector3D(0.0, 0.0, 0.0); ObjectManager.UnifiedObject[] obj = new ObjectManager.UnifiedObject[4]; int objCount = 0; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) && Lines[i].EndsWith("]", StringComparison.Ordinal))) { if (Lines[i].Length != 0) { int equals = Lines[i].IndexOf("=", StringComparison.Ordinal); if (equals > 0) { /* * Process key-value pair, the only supported key is position. */ string before = Lines[i].Substring(0, equals).TrimEnd(); string after = Lines[i].Substring(equals + 1).TrimStart(); switch (before.ToLowerInvariant()) { case "position": ParsePosition(after, ref position, before, i + 1, FileName); break; default: Debug.AddMessage(Debug.MessageType.Error, false, "The attribute " + before + " is not supported at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } else { /* * Process object with file name relative to the location of this ANIMATED file. */ string Folder = System.IO.Path.GetDirectoryName(FileName); if (Path.ContainsInvalidPathChars(Lines[i])) { Debug.AddMessage(Debug.MessageType.Error, false, Lines[i] + " contains illegal characters at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { string file = OpenBveApi.Path.CombineFile(Folder, Lines[i]); if (System.IO.File.Exists(file)) { if (obj.Length == objCount) { Array.Resize <ObjectManager.UnifiedObject>(ref obj, obj.Length << 1); } obj[objCount] = ObjectManager.LoadObject(file, Encoding, LoadMode, false, false, false); objCount++; } else { Debug.AddMessage(Debug.MessageType.Error, true, "File " + file + " not found at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } } } i++; } i--; for (int j = 0; j < objCount; j++) { if (obj[j] != null) { if (obj[j] is ObjectManager.StaticObject) { ObjectManager.StaticObject s = (ObjectManager.StaticObject)obj[j]; s.Dynamic = true; if (ObjectCount >= Result.Objects.Length) { Array.Resize <ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length << 1); } ObjectManager.AnimatedObject a = new ObjectManager.AnimatedObject(); ObjectManager.AnimatedObjectState aos = new ObjectManager.AnimatedObjectState(); aos.Object = s; aos.Position = position; a.States = new[] { aos }; Result.Objects[ObjectCount] = a; ObjectCount++; } else if (obj[j] is ObjectManager.AnimatedObjectCollection) { ObjectManager.AnimatedObjectCollection a = (ObjectManager.AnimatedObjectCollection)obj[j]; for (int k = 0; k < a.Objects.Length; k++) { if (ObjectCount >= Result.Objects.Length) { Array.Resize <ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length << 1); } for (int h = 0; h < a.Objects[k].States.Length; h++) { a.Objects[k].States[h].Position.X += position.X; a.Objects[k].States[h].Position.Y += position.Y; a.Objects[k].States[h].Position.Z += position.Z; } Result.Objects[ObjectCount] = a.Objects[k]; ObjectCount++; } } } } } break; case "[object]": { i++; if (Result.Objects.Length == ObjectCount) { Array.Resize <ObjectManager.AnimatedObject>(ref Result.Objects, Result.Objects.Length << 1); } Result.Objects[ObjectCount] = new ObjectManager.AnimatedObject(); Result.Objects[ObjectCount].States = new ObjectManager.AnimatedObjectState[] { }; Result.Objects[ObjectCount].CurrentState = -1; Result.Objects[ObjectCount].TranslateXDirection = new Vector3D(1.0, 0.0, 0.0); Result.Objects[ObjectCount].TranslateYDirection = new Vector3D(0.0, 1.0, 0.0); Result.Objects[ObjectCount].TranslateZDirection = new Vector3D(0.0, 0.0, 1.0); Result.Objects[ObjectCount].RotateXDirection = new Vector3D(1.0, 0.0, 0.0); Result.Objects[ObjectCount].RotateYDirection = new Vector3D(0.0, 1.0, 0.0); Result.Objects[ObjectCount].RotateZDirection = new Vector3D(0.0, 0.0, 1.0); Result.Objects[ObjectCount].TextureShiftXDirection = new Vector2D(1.0, 0.0); Result.Objects[ObjectCount].TextureShiftYDirection = new Vector2D(0.0, 1.0); Result.Objects[ObjectCount].RefreshRate = 0.0; Result.Objects[ObjectCount].ObjectIndex = -1; Vector3D Position = new Vector3D(0.0, 0.0, 0.0); bool timetableUsed = false; string[] StateFiles = null; string StateFunctionRpn = null; int StateFunctionLine = -1; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) && Lines[i].EndsWith("]", StringComparison.Ordinal))) { if (Lines[i].Length != 0) { int equals = Lines[i].IndexOf("=", StringComparison.Ordinal); if (equals > 0) { string before = Lines[i].Substring(0, equals).TrimEnd(); string after = Lines[i].Substring(equals + 1).TrimStart(); switch (before.ToLowerInvariant()) { case "position": ParsePosition(after, ref Position, before, i + 1, FileName); break; case "states": if (!ParseState(after, ref StateFiles, before, i + 1, FileName)) { return(null); } break; case "statefunction": try { StateFunctionLine = i; StateFunctionRpn = FunctionScripts.GetPostfixNotationFromInfixNotation(after); } catch (Exception ex) { Debug.AddMessage(Debug.MessageType.Error, false, ex.Message + " in " + before + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } break; case "statefunctionrpn": { StateFunctionLine = i; StateFunctionRpn = after; } break; case "translatexdirection": ParseTranslateDirection(after, ref Result.Objects[ObjectCount].TranslateXDirection, before, i + 1, FileName); break; case "translateydirection": ParseTranslateDirection(after, ref Result.Objects[ObjectCount].TranslateYDirection, before, i + 1, FileName); break; case "translatezdirection": ParseTranslateDirection(after, ref Result.Objects[ObjectCount].TranslateZDirection, before, i + 1, FileName); break; case "translatexfunction": ParseInfixFunc(after, ref Result.Objects[ObjectCount].TranslateXFunction, before, i + 1, FileName); break; case "translateyfunction": ParseInfixFunc(after, ref Result.Objects[ObjectCount].TranslateYFunction, before, i + 1, FileName); break; case "translatezfunction": ParseInfixFunc(after, ref Result.Objects[ObjectCount].TranslateZFunction, before, i + 1, FileName); break; case "translatexfunctionrpn": ParsePostfixFunc(after, ref Result.Objects[ObjectCount].TranslateXFunction, before, i + 1, FileName); break; case "translateyfunctionrpn": ParsePostfixFunc(after, ref Result.Objects[ObjectCount].TranslateYFunction, before, i + 1, FileName); break; case "translatezfunctionrpn": ParsePostfixFunc(after, ref Result.Objects[ObjectCount].TranslateZFunction, before, i + 1, FileName); break; case "rotatexdirection": ParseRotateDirection(after, ref Result.Objects[ObjectCount].RotateXDirection, before, i + 1, FileName); break; case "rotateydirection": ParseRotateDirection(after, ref Result.Objects[ObjectCount].RotateYDirection, before, i + 1, FileName); break; case "rotatezdirection": ParseRotateDirection(after, ref Result.Objects[ObjectCount].RotateZDirection, before, i + 1, FileName); break; case "rotatexfunction": ParseInfixFunc(after, ref Result.Objects[ObjectCount].RotateXFunction, before, i + 1, FileName); break; case "rotateyfunction": ParseInfixFunc(after, ref Result.Objects[ObjectCount].RotateYFunction, before, i + 1, FileName); break; case "rotatezfunction": ParseInfixFunc(after, ref Result.Objects[ObjectCount].RotateZFunction, before, i + 1, FileName); break; case "rotatexfunctionrpn": ParsePostfixFunc(after, ref Result.Objects[ObjectCount].RotateXFunction, before, i + 1, FileName); break; case "rotateyfunctionrpn": ParsePostfixFunc(after, ref Result.Objects[ObjectCount].RotateYFunction, before, i + 1, FileName); break; case "rotatezfunctionrpn": ParsePostfixFunc(after, ref Result.Objects[ObjectCount].RotateZFunction, before, i + 1, FileName); break; case "rotatexdamping": ParseRotateDamping(after, ref Result.Objects[ObjectCount].RotateXDamping, before, i + 1, FileName); break; case "rotateydamping": ParseRotateDamping(after, ref Result.Objects[ObjectCount].RotateYDamping, before, i + 1, FileName); break; case "rotatezdamping": ParseRotateDamping(after, ref Result.Objects[ObjectCount].RotateZDamping, before, i + 1, FileName); break; case "textureshiftxdirection": ParseTextureShift(after, ref Result.Objects[ObjectCount].TextureShiftXDirection, before, i + 1, FileName); break; case "textureshiftydirection": ParseTextureShift(after, ref Result.Objects[ObjectCount].TextureShiftYDirection, before, i + 1, FileName); break; case "textureshiftxfunction": ParseInfixFunc(after, ref Result.Objects[ObjectCount].TextureShiftXFunction, before, i + 1, FileName); break; case "textureshiftyfunction": ParseInfixFunc(after, ref Result.Objects[ObjectCount].TextureShiftYFunction, before, i + 1, FileName); break; case "textureshiftxfunctionrpn": ParsePostfixFunc(after, ref Result.Objects[ObjectCount].TextureShiftXFunction, before, i + 1, FileName); break; case "textureshiftyfunctionrpn": ParsePostfixFunc(after, ref Result.Objects[ObjectCount].TextureShiftYFunction, before, i + 1, FileName); break; case "textureoverride": switch (after.ToLowerInvariant()) { case "none": break; case "timetable": if (!timetableUsed) { Timetable.AddObjectForCustomTimetable(Result.Objects[ObjectCount]); timetableUsed = true; } break; default: Debug.AddMessage(Debug.MessageType.Error, false, "Unrecognized value in " + before + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } break; case "refreshrate": { double r; if (!double.TryParse(after, System.Globalization.NumberStyles.Float, Culture, out r)) { Debug.AddMessage(Debug.MessageType.Error, false, "Value is invalid in " + before + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (r < 0.0) { Debug.AddMessage(Debug.MessageType.Error, false, "Value is expected to be non-negative in " + before + " at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { Result.Objects[ObjectCount].RefreshRate = r; } } break; default: Debug.AddMessage(Debug.MessageType.Error, false, "The attribute " + before + " is not supported at line " + (i + 1).ToString(Culture) + " in file " + FileName); break; } } else { Debug.AddMessage(Debug.MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); return(null); } } i++; } i--; if (StateFiles != null) { // create the object if (timetableUsed) { if (StateFunctionRpn != null) { StateFunctionRpn = "timetable 0 == " + StateFunctionRpn + " -1 ?"; } else { StateFunctionRpn = "timetable"; } } if (StateFunctionRpn != null) { try { Result.Objects[ObjectCount].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(StateFunctionRpn); } catch (Exception ex) { Debug.AddMessage(Debug.MessageType.Error, false, ex.Message + " in StateFunction at line " + (StateFunctionLine + 1).ToString(Culture) + " in file " + FileName); } } Result.Objects[ObjectCount].States = new ObjectManager.AnimatedObjectState[StateFiles.Length]; bool ForceTextureRepeatX = Result.Objects[ObjectCount].TextureShiftXFunction != null && Result.Objects[ObjectCount].TextureShiftXDirection.X != 0.0 || Result.Objects[ObjectCount].TextureShiftYFunction != null && Result.Objects[ObjectCount].TextureShiftYDirection.Y != 0.0; bool ForceTextureRepeatY = Result.Objects[ObjectCount].TextureShiftXFunction != null && Result.Objects[ObjectCount].TextureShiftXDirection.X != 0.0 || Result.Objects[ObjectCount].TextureShiftYFunction != null && Result.Objects[ObjectCount].TextureShiftYDirection.Y != 0.0; for (int k = 0; k < StateFiles.Length; k++) { Result.Objects[ObjectCount].States[k].Position = new Vector3D(0.0, 0.0, 0.0); if (StateFiles[k] != null) { Result.Objects[ObjectCount].States[k].Object = ObjectManager.LoadStaticObject(StateFiles[k], Encoding, LoadMode, false, ForceTextureRepeatX, ForceTextureRepeatY); if (Result.Objects[ObjectCount].States[k].Object != null) { Result.Objects[ObjectCount].States[k].Object.Dynamic = true; } } else { Result.Objects[ObjectCount].States[k].Object = null; } for (int j = 0; j < Result.Objects[ObjectCount].States.Length; j++) { Result.Objects[ObjectCount].States[j].Position = Position; } } } else { Result.Objects[ObjectCount].States = new ObjectManager.AnimatedObjectState[] { }; } ObjectCount++; } break; default: Debug.AddMessage(Debug.MessageType.Error, false, "Invalid statement " + Lines[i] + " encountered at line " + (i + 1).ToString(Culture) + " in file " + FileName); return(null); } } } Array.Resize <ObjectManager.AnimatedObject>(ref Result.Objects, ObjectCount); return(Result); }
/// <summary>Changes to or from fullscreen mode.</summary> internal static void ToggleFullscreen() { Fullscreen = !Fullscreen; // begin HACK // Renderer.ClearDisplayLists(); GL.Disable(EnableCap.Fog); GL.Disable(EnableCap.Lighting); Renderer.LightingEnabled = false; if (Fullscreen) { IList <DisplayResolution> resolutions = OpenTK.DisplayDevice.Default.AvailableResolutions; for (int i = 0; i < resolutions.Count; i++) { //Test each resolution if (resolutions[i].Width == Interface.CurrentOptions.FullscreenWidth && resolutions[i].Height == Interface.CurrentOptions.FullscreenHeight && resolutions[i].BitsPerPixel == Interface.CurrentOptions.FullscreenBits) { OpenTK.DisplayDevice.Default.ChangeResolution(resolutions[i]); Program.currentGameWindow.Width = resolutions[i].Width; Program.currentGameWindow.Height = resolutions[i].Height; Screen.Width = Interface.CurrentOptions.FullscreenWidth; Screen.Height = Interface.CurrentOptions.FullscreenHeight; Program.currentGameWindow.WindowState = WindowState.Fullscreen; break; } } System.Threading.Thread.Sleep(20); if (Program.currentGameWindow.WindowState != WindowState.Fullscreen) { MessageBox.Show(Interface.GetInterfaceString("errors_fullscreen_switch1") + System.Environment.NewLine + Interface.GetInterfaceString("errors_fullscreen_switch2"), Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand); Fullscreen = false; } } else { OpenTK.DisplayDevice.Default.RestoreResolution(); Program.currentGameWindow.WindowState = WindowState.Normal; Program.currentGameWindow.Width = Interface.CurrentOptions.WindowWidth; Program.currentGameWindow.Height = Interface.CurrentOptions.WindowHeight; Screen.Width = Interface.CurrentOptions.WindowWidth; Screen.Height = Interface.CurrentOptions.WindowHeight; } Renderer.InitializeLighting(); MainLoop.UpdateViewport(MainLoop.ViewPortChangeMode.NoChange); Renderer.InitializeMotionBlur(); Timetable.CreateTimetable(); Timetable.UpdateCustomTimetable(null, null); World.InitializeCameraRestriction(); if (Renderer.OptionBackfaceCulling) { GL.Enable(EnableCap.CullFace); } else { GL.Disable(EnableCap.CullFace); } Renderer.ReAddObjects(); // end HACK // //Reset the camera when switching between fullscreen and windowed mode //Otherwise, if the aspect ratio changes distortion will occur until the view is changed or the camera reset if (World.CameraMode == World.CameraViewMode.Interior | World.CameraMode == World.CameraViewMode.InteriorLookAhead) { World.CameraCurrentAlignment.Position = new OpenBveApi.Math.Vector3(0.0, 0.0, 0.0); } World.CameraCurrentAlignment.Yaw = 0.0; World.CameraCurrentAlignment.Pitch = 0.0; World.CameraCurrentAlignment.Roll = 0.0; }